refactor: remove screen awareness and capture surfaces (#5186)

This commit is contained in:
Steven Enamakel
2026-07-25 09:18:42 +03:00
committed by GitHub
parent 9f22ae4086
commit ee1fa76081
224 changed files with 1083 additions and 18584 deletions
+1 -1
View File
@@ -1 +1 @@
f09d7e746a44a5187d2abc116e8a4cd7a9f94a67
455b47debb9d1f5d0f1527266e48af1670ad4bc5
+1 -1
View File
@@ -163,7 +163,7 @@ No `UserProvider`/`AIProvider`/`SkillProvider` — auth lives in `CoreStateProvi
## Tauri shell (`app/src-tauri/`)
Thin desktop host. Key modules: `core_process`, `core_rpc`, `cdp`, `cef_preflight`, `cef_profile`, `dictation_hotkeys`, `file_logging`, `mascot_native_window`, `screen_capture`, `window_state`, per-provider scanners (`discord_scanner`, `slack_scanner`, `telegram_scanner`, `whatsapp_scanner`, `wechat_scanner`, `gmessages_scanner`, `imessage_scanner`, `meet_scanner`), `meet_audio`/`meet_call`/`meet_video`, `fake_camera`, `webview_accounts`, `webview_apis`.
Thin desktop host. Key modules: `core_process`, `core_rpc`, `cdp`, `cef_preflight`, `cef_profile`, `dictation_hotkeys`, `file_logging`, `mascot_native_window`, `window_state`, per-provider scanners (`discord_scanner`, `slack_scanner`, `telegram_scanner`, `whatsapp_scanner`, `wechat_scanner`, `gmessages_scanner`, `imessage_scanner`, `meet_scanner`), `meet_audio`/`meet_call`/`meet_video`, `fake_camera`, `webview_accounts`, `webview_apis`.
IPC commands (authoritative list: `generate_handler!` in `app/src-tauri/src/lib.rs`): `core_rpc::relay_http_rpc`, `core_rpc_url`, `core_rpc_token`, `start_core_process`/`restart_core_process`, update commands (`check_app_update`, `apply_core_update`, …), window commands (`activate_main_window`, `mascot_window_*`, `notch_window_*`), `webview_accounts::*`, `workspace_paths::*`, `artifact_commands::*`, hotkeys (dictation/PTT/companion), `meet_call::*`, `native_notifications::*`, `mcp_commands::*`, `loopback_oauth::*`.
-225
View File
@@ -834,70 +834,6 @@
}
]
},
{
"description": "Update screen intelligence runtime settings.",
"function": "update_screen_intelligence_settings",
"inputs": [
{
"comment": "Enable screen intelligence.",
"name": "enabled",
"required": false,
"ty": { "Option": "Bool" }
},
{
"comment": "Capture policy mode.",
"name": "capture_policy",
"required": false,
"ty": { "Option": "String" }
},
{
"comment": "Policy mode override.",
"name": "policy_mode",
"required": false,
"ty": { "Option": "String" }
},
{
"comment": "Baseline capture FPS.",
"name": "baseline_fps",
"required": false,
"ty": { "Option": "F64" }
},
{
"comment": "Enable vision analysis.",
"name": "vision_enabled",
"required": false,
"ty": { "Option": "Bool" }
},
{
"comment": "Enable autocomplete integration.",
"name": "autocomplete_enabled",
"required": false,
"ty": { "Option": "Bool" }
},
{
"comment": "Allowed app list.",
"name": "allowlist",
"required": false,
"ty": { "Option": { "Array": "String" } }
},
{
"comment": "Denied app list.",
"name": "denylist",
"required": false,
"ty": { "Option": { "Array": "String" } }
}
],
"method": "openhuman.config_update_screen_intelligence_settings",
"namespace": "config",
"outputs": [
{
"comment": "Updated config snapshot.",
"name": "snapshot",
"required": true,
"ty": "Json"
}
]
},
{
"description": "Replace tunnel settings with provided config payload.",
"function": "update_tunnel_settings",
@@ -1246,167 +1182,6 @@
}
]
},
{
"description": "Capture screenshot and return image ref.",
"function": "capture_image_ref",
"inputs": [],
"method": "openhuman.screen_intelligence_capture_image_ref",
"namespace": "screen_intelligence",
"outputs": [
{
"comment": "Capture image_ref payload.",
"name": "capture",
"required": true,
"ty": "Json"
}
]
},
{
"description": "Trigger immediate screen capture.",
"function": "capture_now",
"inputs": [],
"method": "openhuman.screen_intelligence_capture_now",
"namespace": "screen_intelligence",
"outputs": [
{ "comment": "Capture result payload.", "name": "capture", "required": true, "ty": "Json" }
]
},
{
"description": "Perform input action through accessibility automation.",
"function": "input_action",
"inputs": [
{
"comment": "Input action payload.",
"name": "action",
"required": true,
"ty": { "Ref": "InputActionParams" }
}
],
"method": "openhuman.screen_intelligence_input_action",
"namespace": "screen_intelligence",
"outputs": [
{
"comment": "Input action result payload.",
"name": "result",
"required": true,
"ty": "Json"
}
]
},
{
"description": "Request one accessibility permission.",
"function": "request_permission",
"inputs": [
{ "comment": "Permission name.", "name": "permission", "required": true, "ty": "String" }
],
"method": "openhuman.screen_intelligence_request_permission",
"namespace": "screen_intelligence",
"outputs": [
{
"comment": "Permission status payload.",
"name": "permissions",
"required": true,
"ty": "Json"
}
]
},
{
"description": "Request required accessibility permissions.",
"function": "request_permissions",
"inputs": [],
"method": "openhuman.screen_intelligence_request_permissions",
"namespace": "screen_intelligence",
"outputs": [
{
"comment": "Permission status payload.",
"name": "permissions",
"required": true,
"ty": "Json"
}
]
},
{
"description": "Start screen intelligence session.",
"function": "start_session",
"inputs": [
{
"comment": "Capture interval in milliseconds.",
"name": "sample_interval_ms",
"required": false,
"ty": { "Option": "U64" }
},
{
"comment": "Capture policy mode.",
"name": "capture_policy",
"required": false,
"ty": { "Option": "String" }
}
],
"method": "openhuman.screen_intelligence_start_session",
"namespace": "screen_intelligence",
"outputs": [
{ "comment": "Session status payload.", "name": "session", "required": true, "ty": "Json" }
]
},
{
"description": "Read screen intelligence accessibility status.",
"function": "status",
"inputs": [],
"method": "openhuman.screen_intelligence_status",
"namespace": "screen_intelligence",
"outputs": [
{
"comment": "Accessibility status payload.",
"name": "status",
"required": true,
"ty": "Json"
}
]
},
{
"description": "Stop screen intelligence session.",
"function": "stop_session",
"inputs": [
{
"comment": "Optional stop reason.",
"name": "reason",
"required": false,
"ty": { "Option": "String" }
}
],
"method": "openhuman.screen_intelligence_stop_session",
"namespace": "screen_intelligence",
"outputs": [
{ "comment": "Session status payload.", "name": "session", "required": true, "ty": "Json" }
]
},
{
"description": "Flush stored vision summaries.",
"function": "vision_flush",
"inputs": [],
"method": "openhuman.screen_intelligence_vision_flush",
"namespace": "screen_intelligence",
"outputs": [
{ "comment": "Vision flush payload.", "name": "result", "required": true, "ty": "Json" }
]
},
{
"description": "Read recent vision summaries.",
"function": "vision_recent",
"inputs": [
{
"comment": "Maximum number of summaries.",
"name": "limit",
"required": false,
"ty": { "Option": "U64" }
}
],
"method": "openhuman.screen_intelligence_vision_recent",
"namespace": "screen_intelligence",
"outputs": [
{ "comment": "Vision recent payload.", "name": "result", "required": true, "ty": "Json" }
]
},
{
"description": "Manage desktop service lifecycle.",
"function": "install",
+1 -2
View File
@@ -403,7 +403,7 @@ if should_run_suite "settings"; then
fi
# ---------------------------------------------------------------------------
# System / AI / voice / screen / Tauri
# System / AI / voice / Tauri
# linux-cef-deb-runtime.spec.ts is Linux-only (tests /usr/bin path resolution
# for .deb package installs) — skipped on macOS/Windows.
# ---------------------------------------------------------------------------
@@ -412,7 +412,6 @@ if should_run_suite "system"; then
echo "## Running suite: system"
run "test/e2e/specs/local-model-runtime.spec.ts" "local-model" "system"
run "test/e2e/specs/voice-mode.spec.ts" "voice-mode" "system"
run "test/e2e/specs/screen-intelligence.spec.ts" "screen-intelligence" "system"
run "test/e2e/specs/audio-toolkit-flow.spec.ts" "audio-toolkit" "system"
run "test/e2e/specs/tauri-commands.spec.ts" "tauri-commands" "system"
# service-connectivity-flow tests the old sidecar service model removed in
+7 -8
View File
@@ -22,14 +22,13 @@
(JSON-RPC server, Socket.IO) under the macOS Hardened Runtime. -->
<key>com.apple.security.network.server</key>
<true/>
<!-- Required for the autocomplete focus query and screen-intelligence
foreground-context query to send Apple Events to "System Events"
under the Hardened Runtime. Without this entitlement, signed DMG
builds have AE calls blocked outright before the macOS consent
dialog renders, producing the broken-looking error popup tracked
in #985. The companion `NSAppleEventsUsageDescription` in
`Info.plist` supplies the user-facing text shown in that
consent dialog. -->
<!-- Required for the autocomplete focus query to send Apple Events to
"System Events" under the Hardened Runtime. Without this entitlement,
signed DMG builds have AE calls blocked outright before the macOS
consent dialog renders, producing the broken-looking error popup
tracked in #985. The companion `NSAppleEventsUsageDescription` in
`Info.plist` supplies the user-facing text shown in that consent
dialog. -->
<key>com.apple.security.automation.apple-events</key>
<true/>
<!-- Required so embedded Chromium can enumerate Bluetooth audio devices
@@ -1,6 +1,6 @@
[[permission]]
identifier = "allow-core-process"
description = "Privileged desktop permissions for core process control, service management, dictation, account webview, window/UI, screen share, notifications, Gmail CDP, log access, Meet, and CEF/profile maintenance"
description = "Privileged desktop permissions for core process control, service management, dictation, account webview, window/UI, notifications, Gmail CDP, log access, Meet, and CEF/profile maintenance"
[permission.commands]
@@ -120,13 +120,6 @@ allow = [
"notch_window_show",
"notch_window_hide",
# =========================
# SCREEN SHARE
# =========================
"screen_share_begin_session",
"screen_share_thumbnail",
"screen_share_finalize_session",
# =========================
# NOTIFICATIONS
# =========================
@@ -1,12 +1,9 @@
[[permission]]
identifier = "allow-webview-recipe"
description = "Allow injected per-provider recipe code (running inside the third-party site's origin) to invoke the recipe ingest command back to Rust. Also includes the session-gated screen-share commands (#713 / #812) so the in-page getDisplayMedia shim can open a short-lived enumeration session after a real user gesture. The session gate prevents drive-by window-title / thumbnail exfiltration by third-party scripts running in the same origin."
description = "Allow injected per-provider recipe code (running inside the third-party site's origin) to invoke the recipe ingest command back to Rust."
[permission.commands]
allow = [
"webview_recipe_event",
"screen_share_begin_session",
"screen_share_thumbnail",
"screen_share_finalize_session",
]
deny = []
-12
View File
@@ -531,13 +531,6 @@ async fn run_session_cycle<R: Runtime>(
// Without these, Meet sits on the consent dialog forever and cam/mic
// never enumerate (verified during #1022 smoke).
//
// displayCapture is intentionally NOT in this set. Pre-granting it
// via `Browser.grantPermissions` bypasses the transient-activation
// requirement Chromium enforces on `getDisplayMedia`, which would
// let the page initiate a desktop capture without any user gesture.
// Without the pre-grant the page's screen-share button triggers
// Chrome's native screen-picker on click — same UX, but the gesture
// gate stays in place.
if origin_host_is(&origin, "meet.google.com") {
perms.extend_from_slice(&["audioCapture", "videoCapture", "clipboardReadWrite"]);
}
@@ -554,11 +547,6 @@ async fn run_session_cycle<R: Runtime>(
// tracking issue — granting these perms now means once the paint
// bug clears, the huddle is functional immediately rather than
// requiring a follow-up perms wire-up.
//
// displayCapture deliberately omitted for the same reason as Meet:
// pre-granting bypasses Chromium's gesture gate on
// `getDisplayMedia`; screen-share inside a huddle still works via
// the native screen-picker on user click.
if origin_host_is(&origin, "app.slack.com") {
perms.extend_from_slice(&["audioCapture", "videoCapture", "clipboardReadWrite"]);
}
+9 -42
View File
@@ -1,14 +1,12 @@
//! Desktop companion — Clicky-style interaction loop (shell-side).
//!
//! Migrated out of the Rust core (`src/openhuman/desktop_companion`) into the
//! Tauri shell. Ties hotkey activation, **native** microphone capture, screen
//! context, LLM reasoning, speech synthesis, and visual pointing into one
//! product experience:
//! Tauri shell. Ties hotkey activation, **native** microphone capture, LLM
//! reasoning, and speech synthesis into one product experience:
//!
//! - `audio` — native `cpal` mic capture (mono i16 PCM ~16 kHz)
//! - `session` — session lifecycle + state machine (idle→listening→…→idle)
//! - `pipeline` — one interaction turn (STT → LLM → TTS → pointing)
//! - `pointing` — `[POINT:x,y:label:screenN]` tag parsing + monitor mapping
//! - `pipeline` — one interaction turn (STT → LLM → TTS)
//! - `events` — `companion://state_changed` Tauri event delivery
//!
//! STT/TTS run in the embedded core over JSON-RPC; the LLM turn runs shell-side
@@ -18,19 +16,17 @@
pub mod audio;
pub mod events;
pub mod pipeline;
pub mod pointing;
pub mod session;
pub mod types;
use log::{debug, info, warn};
use parking_lot::Mutex;
use std::sync::atomic::{AtomicU64, Ordering};
use tauri::{AppHandle, Manager};
use tauri::AppHandle;
use tokio_util::sync::CancellationToken;
use crate::AppRuntime;
use audio::MicCapture;
use pointing::ScreenGeometry;
use types::*;
const LOG_PREFIX: &str = "[companion]";
@@ -167,12 +163,12 @@ pub fn handle_hotkey_pressed(app: AppHandle<AppRuntime>) {
///
/// Only push mode consumes releases. Tap mode intentionally waits for the next
/// press, preserving toggle behavior.
pub fn handle_hotkey_released(app: AppHandle<AppRuntime>) {
pub fn handle_hotkey_released(_app: AppHandle<AppRuntime>) {
if current_config()
.activation_mode
.eq_ignore_ascii_case("push")
{
finish_capture_and_run(app);
finish_capture_and_run();
}
}
@@ -182,7 +178,7 @@ pub fn handle_hotkey_released(app: AppHandle<AppRuntime>) {
/// the interaction turn.
pub fn handle_activation(app: AppHandle<AppRuntime>) {
if ACTIVE_CAPTURE.lock().is_some() {
finish_capture_and_run(app);
finish_capture_and_run();
} else {
start_capture(&app);
}
@@ -227,7 +223,7 @@ fn start_capture(app: &AppHandle<AppRuntime>) {
}
}
fn finish_capture_and_run(app: AppHandle<AppRuntime>) {
fn finish_capture_and_run() {
let _lifecycle = SESSION_LIFECYCLE.lock();
let Some(capture) = ACTIVE_CAPTURE.lock().take() else {
debug!("{LOG_PREFIX} capture stop ignored — not listening");
@@ -235,10 +231,9 @@ fn finish_capture_and_run(app: AppHandle<AppRuntime>) {
};
let (samples, rate) = capture.stop();
info!("{LOG_PREFIX} captured {} samples @ {rate}Hz", samples.len());
let screens = monitors_geometry(&app);
let (turn_id, cancel) = register_active_turn();
tauri::async_runtime::spawn(async move {
let result = pipeline::run_audio_turn(&samples, rate, &screens, cancel.clone()).await;
let result = pipeline::run_audio_turn(&samples, rate, cancel.clone()).await;
if let Err(e) = result {
if cancel.is_cancelled() {
debug!("{LOG_PREFIX} interrupted companion turn stopped: {e}");
@@ -285,34 +280,6 @@ fn clear_active_turn(id: u64) {
}
}
/// Enumerate connected monitors as [`ScreenGeometry`] for POINT-tag coordinate
/// mapping. Best-effort — returns an empty vec if no window/monitor info is
/// available (pointing then falls back to raw coordinates).
fn monitors_geometry(app: &AppHandle<AppRuntime>) -> Vec<ScreenGeometry> {
let Some(window) = app.get_webview_window("main") else {
debug!("{LOG_PREFIX} no main window — cannot enumerate monitors");
return Vec::new();
};
let Ok(monitors) = window.available_monitors() else {
return Vec::new();
};
monitors
.iter()
.enumerate()
.map(|(index, m)| {
let pos = m.position();
let size = m.size();
ScreenGeometry {
index,
x: pos.x as f64,
y: pos.y as f64,
width: size.width as f64,
height: size.height as f64,
}
})
.collect()
}
#[cfg(test)]
mod turn_cancellation_tests {
use super::*;
+19 -99
View File
@@ -1,4 +1,4 @@
//! Companion interaction pipeline: STT → screen context → LLM → TTS → pointing.
//! Companion interaction pipeline: STT → LLM → TTS.
//!
//! Orchestrates a single interaction turn for the desktop companion. Migrated
//! from the former core `desktop_companion::pipeline`, re-plumbed for the shell:
@@ -26,7 +26,6 @@ use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use tokio_util::sync::CancellationToken;
use super::pointing::{self, PointTarget, PointingParseResult, ScreenGeometry};
use super::session;
use super::types::*;
@@ -43,10 +42,8 @@ const CONTEXT_WINDOW: usize = 20;
pub struct TurnResult {
/// The user's transcribed speech (or typed input).
pub transcript: String,
/// The LLM's response text (POINT tags stripped).
/// The LLM's response text.
pub response_text: String,
/// Parsed pointing targets from the LLM response.
pub targets: Vec<PointTarget>,
/// Whether TTS audio was synthesized.
pub tts_synthesized: bool,
/// Whether the turn was cancelled before completion.
@@ -58,12 +55,8 @@ pub struct TurnResult {
/// **Precondition**: the session must already be in `Listening` state. The
/// caller is responsible for the `Idle → Listening` transition before invoking.
///
/// Transitions: Listening → Thinking → Speaking/Pointing → Idle.
pub async fn run_text_turn(
text: &str,
screens: &[ScreenGeometry],
cancel: CancellationToken,
) -> Result<TurnResult, String> {
/// Transitions: Listening → Thinking → Speaking → Idle.
pub async fn run_text_turn(text: &str, cancel: CancellationToken) -> Result<TurnResult, String> {
let trimmed = text.trim();
if trimmed.is_empty() {
return Err("empty input".into());
@@ -82,19 +75,8 @@ pub async fn run_text_turn(
let history = session::conversation_history();
let history_window = tail_history(&history, CONTEXT_WINDOW);
// Foreground app/window context (best-effort and user-configurable).
let screen_context = if super::current_config().include_app_context {
gather_screen_context().await
} else {
None
};
if cancel.is_cancelled() {
return Ok(cancelled_result(trimmed));
}
// LLM call.
let raw_reply = match llm_companion(trimmed, &history_window, screen_context.as_deref()).await {
let response_text = match llm_companion(trimmed, &history_window).await {
Ok(reply) => reply,
Err(err) => {
if cancel.is_cancelled() {
@@ -110,17 +92,7 @@ pub async fn run_text_turn(
return Ok(cancelled_result(trimmed));
}
// Parse POINT tags.
let PointingParseResult {
targets,
clean_text,
} = pointing::parse_and_map(&raw_reply, screens);
debug!(
"{LOG_PREFIX} LLM reply chars={} targets={}",
clean_text.len(),
targets.len()
);
debug!("{LOG_PREFIX} LLM reply chars={}", response_text.len());
// Record conversation turns.
let now_ms = chrono::Utc::now().timestamp_millis();
@@ -129,18 +101,18 @@ pub async fn run_text_turn(
content: trimmed.to_string(),
timestamp_ms: now_ms,
});
if !clean_text.is_empty() {
if !response_text.is_empty() {
let _ = session::push_conversation_turn(ConversationTurn {
role: "assistant".into(),
content: clean_text.clone(),
content: response_text.clone(),
timestamp_ms: now_ms,
});
}
// TTS (skip if response is empty).
let tts_ok = if !clean_text.is_empty() && !cancel.is_cancelled() {
let tts_ok = if !response_text.is_empty() && !cancel.is_cancelled() {
session::transition_state(CompanionState::Speaking, None)?;
match tts(&clean_text).await {
match tts(&response_text).await {
Ok(()) => {
debug!("{LOG_PREFIX} TTS synthesized");
true
@@ -158,18 +130,12 @@ pub async fn run_text_turn(
return Ok(cancelled_result(trimmed));
}
// Pointing phase.
if !targets.is_empty() {
let _ = session::transition_state(CompanionState::Pointing, None);
}
// Back to idle.
session::finish_turn();
let result = TurnResult {
transcript: trimmed.to_string(),
response_text: clean_text,
targets,
response_text,
tts_synthesized: tts_ok,
cancelled: false,
};
@@ -178,15 +144,14 @@ pub async fn run_text_turn(
Ok(result)
}
/// Run a full audio-input companion turn: STT → screen context → LLM → TTS → pointing.
/// Run a full audio-input companion turn: STT → LLM → TTS.
///
/// **Precondition**: the session must already be in `Listening` state.
///
/// Transitions: Listening → Thinking → Speaking/Pointing → Idle.
/// Transitions: Listening → Thinking → Speaking → Idle.
pub async fn run_audio_turn(
audio_samples: &[i16],
sample_rate: u32,
screens: &[ScreenGeometry],
cancel: CancellationToken,
) -> Result<TurnResult, String> {
if audio_samples.is_empty() {
@@ -219,7 +184,6 @@ pub async fn run_audio_turn(
return Ok(TurnResult {
transcript: String::new(),
response_text: String::new(),
targets: Vec::new(),
tts_synthesized: false,
cancelled: false,
});
@@ -238,7 +202,7 @@ pub async fn run_audio_turn(
debug!("{LOG_PREFIX} STT transcript chars={}", transcript.len());
// Hand off to the text pipeline for the rest.
run_text_turn(&transcript, screens, cancel).await
run_text_turn(&transcript, cancel).await
}
// ─── Core-RPC / backend adapters ────────────────────────────────────
@@ -319,41 +283,25 @@ async fn fetch_backend_creds() -> Result<(String, String), String> {
/// System prompt for the desktop companion LLM.
const COMPANION_SYSTEM_PROMPT: &str = "\
You are OpenHuman, a helpful desktop AI companion. The user is talking to you \
via voice or text while using their computer. You can see their screen \
(a screenshot and foreground app info may be provided).\n\
via voice or text while using their computer.\n\
\n\
Guidelines:\n\
- Be concise and conversational. 1-3 sentences unless the question demands more.\n\
- When you want to point the user to a UI element on screen, embed a \
`[POINT:x,y:label:screenN]` tag in your response where x,y are pixel \
coordinates relative to the screen, label describes the element, and N is \
the zero-based screen index.\n\
- Do not use markdown formatting (no asterisks, backticks, or bullet markers) — \
your response will be spoken aloud via TTS.\n\
- If screen context is provided, reference what you see when relevant.\n\
- If you don't know or can't help, say so briefly.\n\
";
/// Build a chat-completions request with screen context and conversation
/// history, then return the assistant's reply. Runs shell-side against the
/// Build a chat-completions request with conversation history, then return the
/// assistant's reply. Runs shell-side against the
/// backend using creds fetched from core.
async fn llm_companion(
prompt: &str,
history: &[&ConversationTurn],
screen_context: Option<&str>,
) -> Result<String, String> {
async fn llm_companion(prompt: &str, history: &[&ConversationTurn]) -> Result<String, String> {
let (token, backend_url) = fetch_backend_creds().await?;
let endpoint = companion_chat_endpoint(&backend_url);
let mut messages: Vec<Value> = Vec::with_capacity(history.len() + 2);
// System prompt with optional screen context.
let system = if let Some(ctx) = screen_context {
format!("{COMPANION_SYSTEM_PROMPT}\nCurrent screen context:\n{ctx}")
} else {
COMPANION_SYSTEM_PROMPT.to_string()
};
messages.push(json!({ "role": "system", "content": system }));
messages.push(json!({ "role": "system", "content": COMPANION_SYSTEM_PROMPT }));
// Rolling conversation history.
for turn in history {
@@ -405,33 +353,6 @@ fn companion_chat_endpoint(backend_url: &str) -> String {
)
}
/// Gather foreground app/window context as a text summary for the LLM.
///
/// The accessibility backbone remains in the embedded core after desktop
/// controls moved out, so the shell can reuse its public, read-only foreground
/// query without restoring any automation commands.
async fn gather_screen_context() -> Option<String> {
#[cfg(target_os = "macos")]
{
openhuman_core::openhuman::accessibility::foreground_context().map(|ctx| {
format_foreground_context(ctx.app_name.as_deref(), ctx.window_title.as_deref())
})
}
#[cfg(not(target_os = "macos"))]
{
None
}
}
#[cfg(any(target_os = "macos", test))]
fn format_foreground_context(app_name: Option<&str>, window_title: Option<&str>) -> String {
format!(
"App: {} | Window: {}",
app_name.unwrap_or("unknown"),
window_title.unwrap_or("unknown"),
)
}
fn extract_chat_completion_text(raw: &Value) -> Option<String> {
raw.get("choices")
.and_then(|c| c.as_array())
@@ -455,7 +376,6 @@ fn cancelled_result(transcript: &str) -> TurnResult {
TurnResult {
transcript: transcript.to_string(),
response_text: String::new(),
targets: Vec::new(),
tts_synthesized: false,
cancelled: true,
}
+9 -42
View File
@@ -1,12 +1,11 @@
//! Tests for the companion interaction pipeline.
//!
//! These tests exercise the pipeline's orchestration logic — state
//! transitions, cancellation, conversation history, and POINT-tag
//! integration. Real STT/LLM/TTS calls are not made; the pipeline's
//! transitions, cancellation, and conversation history. Real STT/LLM/TTS
//! calls are not made; the pipeline's
//! network calls fail fast in a test environment (no embedded core).
use super::*;
use crate::companion::pointing::ScreenGeometry;
use crate::companion::session;
/// Serialize tests that touch the process-global session state. Shared with
@@ -23,16 +22,6 @@ fn lock_and_reset() -> std::sync::MutexGuard<'static, ()> {
guard
}
fn single_screen() -> Vec<ScreenGeometry> {
vec![ScreenGeometry {
index: 0,
x: 0.0,
y: 0.0,
width: 1920.0,
height: 1080.0,
}]
}
// ── Helper tests ─────────────────────────────────────────────────────
#[test]
@@ -73,7 +62,6 @@ fn cancelled_result_has_correct_fields() {
let r = cancelled_result("hello");
assert_eq!(r.transcript, "hello");
assert!(r.response_text.is_empty());
assert!(r.targets.is_empty());
assert!(!r.tts_synthesized);
assert!(r.cancelled);
}
@@ -134,7 +122,7 @@ fn companion_chat_endpoint_joins_only_the_backend_path() {
async fn text_turn_rejects_empty_input() {
let _guard = lock_and_reset();
let cancel = CancellationToken::new();
let result = run_text_turn("", &single_screen(), cancel).await;
let result = run_text_turn("", cancel).await;
assert!(result.is_err());
assert!(result.unwrap_err().contains("empty"));
session::reset_for_test();
@@ -144,7 +132,7 @@ async fn text_turn_rejects_empty_input() {
async fn text_turn_rejects_whitespace_only() {
let _guard = lock_and_reset();
let cancel = CancellationToken::new();
let result = run_text_turn(" \n ", &single_screen(), cancel).await;
let result = run_text_turn(" \n ", cancel).await;
assert!(result.is_err());
session::reset_for_test();
}
@@ -156,7 +144,7 @@ async fn text_turn_cancellation_returns_cancelled() {
cancel.cancel();
// Transition to Listening first so Thinking is a valid transition.
session::transition_state(CompanionState::Listening, None).unwrap();
let result = run_text_turn("hello", &single_screen(), cancel).await;
let result = run_text_turn("hello", cancel).await;
let turn = result.unwrap();
assert!(turn.cancelled);
assert!(turn.response_text.is_empty());
@@ -169,7 +157,7 @@ async fn text_turn_cancellation_returns_cancelled() {
async fn audio_turn_rejects_empty_samples() {
let _guard = lock_and_reset();
let cancel = CancellationToken::new();
let result = run_audio_turn(&[], 16_000, &single_screen(), cancel).await;
let result = run_audio_turn(&[], 16_000, cancel).await;
assert!(result.is_err());
assert!(result.unwrap_err().contains("no audio"));
let status = session::session_status();
@@ -178,33 +166,12 @@ async fn audio_turn_rejects_empty_samples() {
session::reset_for_test();
}
// ── Screen context ───────────────────────────────────────────────────
#[tokio::test]
async fn gather_screen_context_returns_option() {
let ctx = gather_screen_context().await;
// Just verify it doesn't panic — value depends on platform/capability.
let _ = ctx;
}
#[test]
fn foreground_context_summary_preserves_app_and_window() {
assert_eq!(
format_foreground_context(Some("Safari"), Some("OpenHuman")),
"App: Safari | Window: OpenHuman"
);
assert_eq!(
format_foreground_context(None, None),
"App: unknown | Window: unknown"
);
}
// ── System prompt ────────────────────────────────────────────────────
#[test]
fn companion_system_prompt_mentions_point_tags() {
assert!(COMPANION_SYSTEM_PROMPT.contains("[POINT:"));
assert!(COMPANION_SYSTEM_PROMPT.contains("screenN"));
fn companion_system_prompt_does_not_request_screen_context_or_point_tags() {
assert!(!COMPANION_SYSTEM_PROMPT.contains("screen context"));
assert!(!COMPANION_SYSTEM_PROMPT.contains("[POINT:"));
}
#[test]
-128
View File
@@ -1,128 +0,0 @@
//! POINT tag parser and multi-monitor coordinate mapping.
//!
//! The companion LLM embeds `[POINT:x,y:label:screenN]` tags in its
//! response text (Clicky convention). This module extracts those tags,
//! maps screen-relative coordinates to absolute desktop coordinates
//! using monitor geometry, and strips the tags from the display text.
//!
//! Migrated verbatim from the former core `desktop_companion::pointing`
//! (the parsing logic has no core dependencies — it is pure regex + math).
use log::debug;
use serde::{Deserialize, Serialize};
const LOG_PREFIX: &str = "[companion_pointing]";
/// A parsed point target from the LLM response.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct PointTarget {
/// Screen-relative X coordinate (as emitted by the LLM).
pub x: f64,
/// Screen-relative Y coordinate.
pub y: f64,
/// Human-readable label for the target element.
pub label: String,
/// Zero-based screen index.
pub screen_index: usize,
/// Absolute desktop X after multi-monitor mapping.
pub absolute_x: f64,
/// Absolute desktop Y after multi-monitor mapping.
pub absolute_y: f64,
}
/// Monitor geometry used for coordinate mapping.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScreenGeometry {
/// Zero-based index.
pub index: usize,
/// Left edge in absolute desktop coordinates.
pub x: f64,
/// Top edge in absolute desktop coordinates.
pub y: f64,
/// Width in points.
pub width: f64,
/// Height in points.
pub height: f64,
}
/// Result of parsing POINT tags from an LLM response.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PointingParseResult {
/// Extracted point targets with mapped coordinates.
pub targets: Vec<PointTarget>,
/// The response text with POINT tags stripped out.
pub clean_text: String,
}
/// Lazily compiled POINT-tag regex.
fn point_tag_regex() -> &'static regex::Regex {
static RE: std::sync::OnceLock<regex::Regex> = std::sync::OnceLock::new();
RE.get_or_init(|| {
regex::Regex::new(r"\[POINT:(-?\d+(?:\.\d+)?),(-?\d+(?:\.\d+)?):([^:\]]+):screen(\d+)\]")
.expect("companion POINT tag regex is static and valid")
})
}
/// Parse `[POINT:x,y:label:screenN]` tags from LLM response text and map
/// coordinates to absolute desktop positions using the given screen geometry.
pub fn parse_and_map(text: &str, screens: &[ScreenGeometry]) -> PointingParseResult {
let re = point_tag_regex();
let mut targets = Vec::new();
let clean_text = re
.replace_all(text, |caps: &regex::Captures| {
let x: f64 = caps[1].parse().unwrap_or(0.0);
let y: f64 = caps[2].parse().unwrap_or(0.0);
let label = caps[3].trim().to_string();
let screen_index: usize = caps[4].parse().unwrap_or(0);
let (abs_x, abs_y) = map_to_absolute(x, y, screen_index, screens);
debug!(
"{LOG_PREFIX} parsed target: ({x},{y}) label=\"{label}\" screen{screen_index} -> abs({abs_x},{abs_y})"
);
targets.push(PointTarget {
x,
y,
label,
screen_index,
absolute_x: abs_x,
absolute_y: abs_y,
});
// Replace the tag with empty string in the display text.
String::new()
})
.to_string();
PointingParseResult {
targets,
clean_text: clean_text.trim().to_string(),
}
}
/// Map screen-relative coordinates to absolute desktop coordinates.
///
/// If the screen index is out of range, falls back to screen 0 (primary).
/// Coordinates are clamped to screen bounds.
fn map_to_absolute(x: f64, y: f64, screen_index: usize, screens: &[ScreenGeometry]) -> (f64, f64) {
if screens.is_empty() {
return (x, y);
}
let screen = screens
.iter()
.find(|s| s.index == screen_index)
.or_else(|| screens.first())
.unwrap();
let clamped_x = x.clamp(0.0, screen.width);
let clamped_y = y.clamp(0.0, screen.height);
(screen.x + clamped_x, screen.y + clamped_y)
}
#[cfg(test)]
#[path = "pointing_tests.rs"]
mod tests;
@@ -1,170 +0,0 @@
//! Tests for POINT tag parsing and coordinate mapping.
use super::*;
fn single_screen() -> Vec<ScreenGeometry> {
vec![ScreenGeometry {
index: 0,
x: 0.0,
y: 0.0,
width: 1920.0,
height: 1080.0,
}]
}
fn dual_screens() -> Vec<ScreenGeometry> {
vec![
ScreenGeometry {
index: 0,
x: 0.0,
y: 0.0,
width: 1920.0,
height: 1080.0,
},
ScreenGeometry {
index: 1,
x: 1920.0,
y: 0.0,
width: 2560.0,
height: 1440.0,
},
]
}
// ── Basic parsing ─────────────────────────────────────────────────────
#[test]
fn parse_single_point_tag() {
let text = "Click the button [POINT:100,200:Save Button:screen0] to save.";
let result = parse_and_map(text, &single_screen());
assert_eq!(result.targets.len(), 1);
assert_eq!(result.targets[0].x, 100.0);
assert_eq!(result.targets[0].y, 200.0);
assert_eq!(result.targets[0].label, "Save Button");
assert_eq!(result.targets[0].screen_index, 0);
assert_eq!(result.clean_text, "Click the button to save.");
}
#[test]
fn parse_multiple_point_tags() {
let text = "First [POINT:10,20:A:screen0] then [POINT:30,40:B:screen1] done.";
let result = parse_and_map(text, &dual_screens());
assert_eq!(result.targets.len(), 2);
assert_eq!(result.targets[0].label, "A");
assert_eq!(result.targets[1].label, "B");
}
#[test]
fn parse_no_point_tags() {
let text = "No pointing needed here.";
let result = parse_and_map(text, &single_screen());
assert!(result.targets.is_empty());
assert_eq!(result.clean_text, "No pointing needed here.");
}
#[test]
fn parse_decimal_coordinates() {
let text = "[POINT:100.5,200.75:Pin:screen0]";
let result = parse_and_map(text, &single_screen());
assert_eq!(result.targets[0].x, 100.5);
assert_eq!(result.targets[0].y, 200.75);
}
#[test]
fn parse_negative_coordinates_clamped() {
let text = "[POINT:-50,-100:Off-screen:screen0]";
let result = parse_and_map(text, &single_screen());
assert_eq!(result.targets[0].absolute_x, 0.0);
assert_eq!(result.targets[0].absolute_y, 0.0);
}
// ── Multi-monitor mapping ─────────────────────────────────────────────
#[test]
fn map_to_primary_screen() {
let text = "[POINT:500,300:Target:screen0]";
let result = parse_and_map(text, &dual_screens());
assert_eq!(result.targets[0].absolute_x, 500.0);
assert_eq!(result.targets[0].absolute_y, 300.0);
}
#[test]
fn map_to_secondary_screen() {
let text = "[POINT:500,300:Target:screen1]";
let result = parse_and_map(text, &dual_screens());
// screen1 starts at x=1920
assert_eq!(result.targets[0].absolute_x, 2420.0);
assert_eq!(result.targets[0].absolute_y, 300.0);
}
#[test]
fn screen_index_out_of_range_falls_back_to_primary() {
let text = "[POINT:100,200:Target:screen5]";
let result = parse_and_map(text, &single_screen());
// Falls back to screen 0
assert_eq!(result.targets[0].absolute_x, 100.0);
assert_eq!(result.targets[0].absolute_y, 200.0);
}
#[test]
fn coordinates_clamped_to_screen_bounds() {
let text = "[POINT:5000,3000:Far:screen0]";
let result = parse_and_map(text, &single_screen());
assert_eq!(result.targets[0].absolute_x, 1920.0);
assert_eq!(result.targets[0].absolute_y, 1080.0);
}
#[test]
fn empty_screens_returns_raw_coordinates() {
let text = "[POINT:100,200:Target:screen0]";
let result = parse_and_map(text, &[]);
assert_eq!(result.targets[0].absolute_x, 100.0);
assert_eq!(result.targets[0].absolute_y, 200.0);
}
// ── Malformed tags ────────────────────────────────────────────────────
#[test]
fn malformed_tag_not_parsed() {
let text = "[POINT:abc,def:Bad:screen0] and [POINT:100:Missing:screen0]";
let result = parse_and_map(text, &single_screen());
// Neither matches the regex
assert!(result.targets.is_empty());
}
#[test]
fn partial_tag_not_parsed() {
let text = "[POINT:100,200:No Screen] and POINT:100,200:bare:screen0]";
let result = parse_and_map(text, &single_screen());
assert!(result.targets.is_empty());
}
// ── Clean text ────────────────────────────────────────────────────────
#[test]
fn clean_text_strips_all_tags() {
let text = "Start [POINT:0,0:A:screen0] middle [POINT:0,0:B:screen0] end";
let result = parse_and_map(text, &single_screen());
assert_eq!(result.clean_text, "Start middle end");
}
#[test]
fn clean_text_trims_whitespace() {
let text = " [POINT:0,0:A:screen0] ";
let result = parse_and_map(text, &single_screen());
assert_eq!(result.clean_text, "");
}
+2 -10
View File
@@ -2,7 +2,7 @@
//!
//! A companion session represents a single period of desktop companion
//! activity. It owns the state machine (idle → listening → thinking →
//! speaking → pointing → idle), TTL enforcement, and conversation history.
//! speaking → idle), TTL enforcement, and conversation history.
//!
//! Only one session may be active at a time. Sessions are created with
//! explicit user consent and can be stopped manually or via TTL expiry.
@@ -345,13 +345,9 @@ pub fn conversation_history() -> Vec<ConversationTurn> {
/// - Listening → Thinking (transcript received)
/// - Listening → Idle (cancelled / released)
/// - Thinking → Speaking (response ready)
/// - Thinking → Pointing (response has point targets, no TTS)
/// - Thinking → Idle (cancelled)
/// - Speaking → Pointing (TTS done, point targets present)
/// - Speaking → Idle (TTS done, no pointing)
/// - Speaking → Idle (TTS done)
/// - Speaking → Listening (interrupted — new turn)
/// - Pointing → Idle (animation done)
/// - Pointing → Listening (interrupted — new turn)
/// - Error → Idle (reset)
/// - Any → Error (error from any state)
fn is_valid_transition(from: CompanionState, to: CompanionState) -> bool {
@@ -366,13 +362,9 @@ fn is_valid_transition(from: CompanionState, to: CompanionState) -> bool {
| (CompanionState::Listening, CompanionState::Thinking)
| (CompanionState::Listening, CompanionState::Idle)
| (CompanionState::Thinking, CompanionState::Speaking)
| (CompanionState::Thinking, CompanionState::Pointing)
| (CompanionState::Thinking, CompanionState::Idle)
| (CompanionState::Speaking, CompanionState::Pointing)
| (CompanionState::Speaking, CompanionState::Idle)
| (CompanionState::Speaking, CompanionState::Listening)
| (CompanionState::Pointing, CompanionState::Idle)
| (CompanionState::Pointing, CompanionState::Listening)
| (CompanionState::Error, CompanionState::Idle)
)
}
@@ -182,30 +182,6 @@ fn transition_thinking_to_speaking() {
});
}
#[test]
fn transition_speaking_to_pointing() {
with_clean_session(|| {
let _s = start_default_session();
transition_state(CompanionState::Listening, None).unwrap();
transition_state(CompanionState::Thinking, None).unwrap();
transition_state(CompanionState::Speaking, None).unwrap();
let prev = transition_state(CompanionState::Pointing, None).unwrap();
assert_eq!(prev, CompanionState::Speaking);
});
}
#[test]
fn transition_pointing_to_idle() {
with_clean_session(|| {
let _s = start_default_session();
transition_state(CompanionState::Listening, None).unwrap();
transition_state(CompanionState::Thinking, None).unwrap();
transition_state(CompanionState::Pointing, None).unwrap();
let prev = transition_state(CompanionState::Idle, None).unwrap();
assert_eq!(prev, CompanionState::Pointing);
});
}
#[test]
fn transition_full_happy_path() {
with_clean_session(|| {
@@ -213,7 +189,6 @@ fn transition_full_happy_path() {
transition_state(CompanionState::Listening, None).unwrap();
transition_state(CompanionState::Thinking, None).unwrap();
transition_state(CompanionState::Speaking, None).unwrap();
transition_state(CompanionState::Pointing, None).unwrap();
transition_state(CompanionState::Idle, None).unwrap();
assert_eq!(session_status().state, CompanionState::Idle);
});
+2 -16
View File
@@ -17,12 +17,10 @@ pub enum CompanionState {
Idle,
/// Microphone is live — capturing user speech.
Listening,
/// Transcript + screen context sent to LLM; awaiting response.
/// Transcript sent to LLM; awaiting response.
Thinking,
/// TTS is playing the response audio.
Speaking,
/// Visual pointer is animating toward a UI target.
Pointing,
/// An unrecoverable error occurred in the current turn.
Error,
}
@@ -34,7 +32,6 @@ impl std::fmt::Display for CompanionState {
Self::Listening => write!(f, "listening"),
Self::Thinking => write!(f, "thinking"),
Self::Speaking => write!(f, "speaking"),
Self::Pointing => write!(f, "pointing"),
Self::Error => write!(f, "error"),
}
}
@@ -63,12 +60,6 @@ pub struct CompanionConfig {
/// Session TTL in seconds. `0` means no automatic expiry.
#[serde(default = "default_ttl_secs")]
pub ttl_secs: u64,
/// Whether to capture a screenshot on each activation.
#[serde(default = "default_true")]
pub capture_screen: bool,
/// Whether to include the foreground app context.
#[serde(default = "default_true")]
pub include_app_context: bool,
}
impl Default for CompanionConfig {
@@ -77,8 +68,6 @@ impl Default for CompanionConfig {
hotkey: default_hotkey(),
activation_mode: default_activation_mode(),
ttl_secs: default_ttl_secs(),
capture_screen: true,
include_app_context: true,
}
}
}
@@ -92,14 +81,11 @@ fn default_activation_mode() -> String {
fn default_ttl_secs() -> u64 {
3600
}
fn default_true() -> bool {
true
}
/// Parameters for starting a companion session.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StartCompanionSessionParams {
/// Explicit user consent to screen monitoring and audio capture.
/// Explicit user consent to audio capture.
pub consent: bool,
/// Optional TTL override in seconds.
#[serde(default, skip_serializing_if = "Option::is_none")]
-5
View File
@@ -94,7 +94,6 @@ mod ptt_hotkeys;
mod ptt_overlay;
#[cfg(target_os = "windows")]
mod reset_reboot_schedule;
mod screen_capture;
mod slack_scanner;
mod stderr_panic_hook;
mod telegram_scanner;
@@ -3129,7 +3128,6 @@ pub fn run() {
let builder = builder.manage(discord_scanner::ScannerRegistry::new());
let builder = builder.manage(telegram_scanner::ScannerRegistry::new());
let builder = builder.manage(wechat_scanner::ScannerRegistry::new());
let builder = builder.manage(screen_capture::ScreenShareState::new());
let builder = builder.manage(meet_call::MeetCallState::new());
let builder = builder.manage(meet_audio::MeetAudioState::new());
let builder = builder.manage(meet_video::frame_bus::MeetVideoFrameBusState::new());
@@ -3874,9 +3872,6 @@ pub fn run() {
webview_accounts::webview_set_focused_account,
notification_settings::notification_settings_get,
notification_settings::notification_settings_set,
screen_capture::screen_share_begin_session,
screen_capture::screen_share_thumbnail,
screen_capture::screen_share_finalize_session,
native_notifications::notification_permission_state,
native_notifications::notification_permission_request,
activate_main_window,
@@ -17,9 +17,7 @@
// `__openhumanCaptionsBridgeInfo()` for the Tauri shell to drive
// over CDP `Runtime.evaluate`.
//
// Why scraping (and not getDisplayMedia, or Web Speech, or Meet's
// undocumented APIs)?
// - getDisplayMedia would prompt the user for screen-share permission.
// Why scraping (and not Web Speech or Meet's undocumented APIs)?
// - Web Speech doesn't reach the remote participants' audio — only
// local mic.
// - Meet has no public caption API.
File diff suppressed because it is too large Load Diff
+45 -661
View File
@@ -96,676 +96,60 @@
window.__openhumanRecipe = api;
send('log', { level: 'info', msg: '[recipe-runtime] ready provider=' + ctx.provider + ' accountId=' + ctx.accountId });
// --- #713 getDisplayMedia shim ---
//
// Background: embedded webviews run under CEF Alloy, which does not link
// Chromium's DesktopMediaPicker. Without an interceptor, `getDisplayMedia`
// gets auto-granted by our permission handler and Chromium silently picks
// the primary display (issue #713 AC2: "OS screen/window picker appears").
//
// The picker UI is injected DIRECTLY into the child webview's own DOM
// rather than rendered as a React modal in the main OpenHuman window.
// Two reasons:
// (a) Works uniformly for every embedded provider — Meet, Slack
// Huddles, Discord, Zoom — without per-provider host-side glue.
// (b) Dodges the CEF native-view stacking problem: a React modal in
// the main window is always occluded by the child webview's
// NSView, forcing a hide/bounds dance that flickers the embedded
// site. An overlay inside the page is stacked in the page's own
// compositing context, so it sits above Meet/Slack UI naturally.
//
// Flow:
// 1. Shim calls Tauri `screen_share_list_sources` to enumerate real
// screens (`screen:<CGDirectDisplayID>:0`) and windows
// (`window:<CGWindowID>:0`) natively.
// 2. Shim builds a fixed-position picker overlay inside the page's
// document and awaits the user's choice.
// 3. On Share, shim calls `getUserMedia` with a hand-crafted
// `chromeMediaSource: 'desktop' + chromeMediaSourceId` constraint.
// Stage 0 PoC proved Chromium honours the ID directly because our
// CEF permission callback grants `DESKTOP_VIDEO_CAPTURE` bits.
// 4. On Cancel, shim throws `NotAllowedError` — same shape the real
// Chromium picker emits so page error handling is unchanged.
(function installGetDisplayMediaShim() {
if (!navigator.mediaDevices || typeof navigator.mediaDevices.getDisplayMedia !== 'function') {
// Never had getDisplayMedia to begin with (non-WebRTC webview); skip.
return;
}
if (navigator.mediaDevices.__ohGdmShimInstalled) return;
// `navigator.mediaDevices.getDisplayMedia` is a WebIDL-defined prototype
// method on `MediaDevices.prototype`. Chromium marks it
// `writable: true, configurable: true` but *only* on the prototype —
// plain `navigator.mediaDevices.getDisplayMedia = ...` on the instance
// creates an own-property shadow that Chromium's IDL bindings bypass
// when the page actually invokes the method. We override on the
// prototype with `defineProperty` so the shim is what runs for every
// MediaDevices instance in this page (including any iframes that
// inherit from the same prototype).
const proto = Object.getPrototypeOf(navigator.mediaDevices);
const descriptor = Object.getOwnPropertyDescriptor(proto, 'getDisplayMedia');
const origGetDisplayMedia = (descriptor && descriptor.value
? descriptor.value
: navigator.mediaDevices.getDisplayMedia
).bind(navigator.mediaDevices);
// Fire-and-forget session cleanup. Swallows errors because finalize
// is a no-op on the host side for unknown/expired tokens and we don't
// want a late IPC failure to leak into the getDisplayMedia rejection.
function finalizeSessionQuiet(token, pickedId) {
if (!token) return Promise.resolve();
return rawInvoke('screen_share_finalize_session', {
args: { token: token, pickedId: pickedId || null },
}).catch(function () {});
}
// In-flight guard (graycyrus refactor #6). The host-side state already
// evicts a stale session when begin_session fires twice, but without a
// shim-side guard a second call would still append a second picker DOM
// while the first is open — the user would see two stacked overlays.
// Reject a concurrent call the same way the MediaStreams spec does
// when an existing capture request is in progress.
let pickerInFlight = false;
const shim = async function (constraints) {
constraints = constraints || {};
if (pickerInFlight) {
send('log', { level: 'warn', msg: '[gdm-shim] picker already open, rejecting concurrent call' });
throw new DOMException(
'A screen-share picker is already open',
'InvalidStateError'
);
}
pickerInFlight = true;
try {
return await runShim(constraints);
} finally {
pickerInFlight = false;
}
};
const runShim = async function (constraints) {
constraints = constraints || {};
// User-activation gate (#812). `navigator.userActivation.isActive`
// is transient — true only during the direct call stack of a real
// gesture handler (click, key, touch). Third-party JS calling
// getDisplayMedia from a timer or async continuation gets filtered
// here, so our downstream commands (begin_session etc.) never open
// a session without a gesture. Fall through to the original
// implementation rather than throw so pages with legitimate
// non-gesture flows (rare but possible) aren't hard-blocked.
const hasActivation = !!(
typeof navigator !== 'undefined' &&
navigator.userActivation &&
navigator.userActivation.isActive
);
send('log', {
level: 'info',
msg:
'[gdm-shim] getDisplayMedia intercepted audio=' +
!!constraints.audio +
' activation=' +
hasActivation,
});
if (!hasActivation) {
send('log', {
level: 'warn',
msg: '[gdm-shim] no user activation, falling through to native getDisplayMedia',
});
return origGetDisplayMedia(constraints);
}
let session;
try {
session = await rawInvoke('screen_share_begin_session', {
args: {
accountId: ctx.accountId,
origin: (typeof location !== 'undefined' && location.origin) || 'unknown',
hasUserActivation: hasActivation,
},
});
} catch (e) {
send('log', {
level: 'error',
msg: '[gdm-shim] begin_session IPC failed: ' + (e && e.message ? e.message : String(e)),
});
return origGetDisplayMedia(constraints);
}
if (!session || typeof session.token !== 'string' || !Array.isArray(session.sources)) {
send('log', {
level: 'warn',
msg: '[gdm-shim] begin_session returned malformed payload, falling back',
});
return origGetDisplayMedia(constraints);
}
const sessionToken = session.token;
const sources = session.sources;
if (sources.length === 0) {
send('log', { level: 'warn', msg: '[gdm-shim] no sources enumerated, falling back' });
await finalizeSessionQuiet(sessionToken, null);
return origGetDisplayMedia(constraints);
}
const pick = await showInPagePicker(sources, sessionToken);
if (!pick) {
send('log', { level: 'info', msg: '[gdm-shim] user cancelled picker' });
await finalizeSessionQuiet(sessionToken, null);
// Meet (and other video-conf sites) treat `NotAllowedError` on
// getDisplayMedia as "the browser blocked us" and pop a
// "needs permission" modal. Real Chrome ALSO throws
// NotAllowedError on picker cancel, but Meet silently swallows
// it there — presumably via a separate Permissions API check
// that reports 'granted'. Since we can't easily signal that
// state in CEF, throw `AbortError` instead: it's the MDN-blessed
// "user interrupted a UI operation" error and most sites (Meet
// included) dismiss it silently.
throw new DOMException('User cancelled screen share picker', 'AbortError');
}
// Finalize the session BEFORE getUserMedia: the Chromium capture
// path doesn't need the token, and leaving the session open past
// this point would just hold the `active` slot for the account
// until the 30s TTL fires.
await finalizeSessionQuiet(sessionToken, pick.id);
send('log', {
level: 'info',
msg: '[gdm-shim] picked id=' + pick.id + ' kind=' + pick.kind,
});
const videoMandatory = {
chromeMediaSource: 'desktop',
chromeMediaSourceId: pick.id,
maxFrameRate: 30,
// CEF Alloy's Permissions API does not reflect the media-access callback.
// Preserve the mic/camera response for providers that check it before
// calling getUserMedia; display-capture is deliberately not overridden.
try {
if (
navigator.permissions &&
typeof navigator.permissions.query === 'function' &&
!navigator.permissions.__ohPermissionsShimInstalled
) {
const permProto = Object.getPrototypeOf(navigator.permissions);
const permDescriptor = Object.getOwnPropertyDescriptor(permProto, 'query');
const origQuery = (permDescriptor && permDescriptor.value
? permDescriptor.value
: navigator.permissions.query
).bind(navigator.permissions);
const spoofed = {
camera: 'granted',
microphone: 'granted',
};
// System-audio capture via `chromeMediaSource: 'desktop'` needs a
// loopback driver on macOS (no stock API). If the page requested
// audio we try with audio first and fall back to video-only on
// rejection so Meet/Slack/etc don't see a generic "Can't share"
// error on every attempt. Chromium cleanly handles a missing audio
// track in the SDP.
const videoOnly = { video: { mandatory: videoMandatory }, audio: false };
let stream;
if (constraints.audio) {
const audioMandatory = {
chromeMediaSource: 'desktop',
chromeMediaSourceId: pick.id,
};
try {
stream = await navigator.mediaDevices.getUserMedia({
video: { mandatory: videoMandatory },
audio: { mandatory: audioMandatory },
});
} catch (e) {
send('log', {
level: 'warn',
msg:
'[gdm-shim] audio+video getUserMedia rejected (' +
(e && e.name ? e.name : '?') +
'), retrying video-only',
});
stream = await navigator.mediaDevices.getUserMedia(videoOnly);
const spoofedQuery = async function (descriptor) {
const n = descriptor && descriptor.name;
if (n && spoofed[n]) {
return {
state: spoofed[n],
status: spoofed[n],
name: n,
onchange: null,
addEventListener: function () {},
removeEventListener: function () {},
dispatchEvent: function () { return true; },
};
}
} else {
stream = await navigator.mediaDevices.getUserMedia(videoOnly);
}
// Stream returned by the legacy `chromeMediaSource: 'desktop'`
// getUserMedia path is a real capture stream but its tracks lack
// the display-media metadata the page expects from real
// getDisplayMedia. Google Meet (and others) inspect
// `track.getSettings().displaySurface` before they will route the
// track over WebRTC — if the field is missing they throw "Can't
// share your screen — Something went wrong".
//
// Patch each video track to expose the right displaySurface and
// a `contentHint` of `detail` (standard WebRTC screen-capture
// content hint). The underlying capture pipeline is unchanged;
// we're only fixing the introspectable metadata the page relies
// on to identify a display-media track.
const displaySurface = pick.kind === 'screen' ? 'monitor' : 'window';
stream.getVideoTracks().forEach(function (track) {
try { track.contentHint = 'detail'; } catch (_) { /* ignore */ }
try {
const origGetSettings = track.getSettings.bind(track);
Object.defineProperty(track, 'getSettings', {
configurable: true,
writable: true,
value: function () {
const base = origGetSettings() || {};
return Object.assign({}, base, {
displaySurface: displaySurface,
logicalSurface: true,
cursor: 'motion',
});
},
});
} catch (e) {
send('log', {
level: 'warn',
msg: '[gdm-shim] patch getSettings failed: ' + (e && e.message ? e.message : e),
});
}
});
return stream;
};
// In-page picker. Renders straight into the host page's <body> so the
// overlay stacks above the site's own compositor (Meet/Slack/Discord
// UI) without any native-view gymnastics. All nodes are namespaced
// under `__ohsp_*` class/ID prefixes and attached to a closed shadow
// root where possible to avoid colliding with the host page's CSS.
function showInPagePicker(sources, sessionToken) {
return new Promise(function (resolveOuter, rejectOuter) {
function host() { return (document.body || document.documentElement); }
if (!host()) {
// DOM hasn't parsed yet — wait for it and retry. Previously we
// resolved null here, which the shim turned into an AbortError
// even though no picker was ever shown (coderabbit #809).
document.addEventListener(
'DOMContentLoaded',
function () {
showInPagePicker(sources, sessionToken).then(resolveOuter, rejectOuter);
},
{ once: true }
);
return;
}
const root = document.createElement('div');
root.setAttribute('data-openhuman-screen-share-picker', '');
root.style.cssText = [
'all: initial',
'position: fixed',
'inset: 0',
'z-index: 2147483647',
'display: flex',
'align-items: center',
'justify-content: center',
'background: rgba(0,0,0,0.55)',
'backdrop-filter: blur(6px)',
'-webkit-backdrop-filter: blur(6px)',
'font-family: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", sans-serif',
].join(';');
const shadow = root.attachShadow ? root.attachShadow({ mode: 'closed' }) : root;
const styleTag = document.createElement('style');
styleTag.textContent = [
'* { box-sizing: border-box; margin: 0; padding: 0; font-family: inherit; }',
'.card { background: #fff; color: #1C1917; border-radius: 16px; width: min(640px, 92vw);',
' max-height: 86vh; box-shadow: 0 24px 64px rgba(0,0,0,0.35); overflow: hidden;',
' display: flex; flex-direction: column; }',
'.head { padding: 20px 24px; border-bottom: 1px solid #E7E5E4; display: flex;',
' align-items: flex-start; justify-content: space-between; gap: 16px; }',
'.title { font-size: 17px; font-weight: 600; color: #1C1917; }',
'.origin { margin-top: 4px; font-size: 13px; color: #78716C; }',
'.closebtn { width: 32px; height: 32px; border: none; background: transparent;',
' color: #78716C; cursor: pointer; border-radius: 8px; font-size: 18px;',
' display: flex; align-items: center; justify-content: center; }',
'.closebtn:hover { background: #F5F5F4; color: #1C1917; }',
'.tabs { display: flex; gap: 4px; padding: 0 24px; border-bottom: 1px solid #E7E5E4; }',
'.tab { appearance: none; -webkit-appearance: none; background: transparent; border: 0;',
' padding: 12px 16px; font-size: 14px; font-weight: 500; color: #78716C;',
' cursor: pointer; border-bottom: 2px solid transparent; }',
'.tab.active { color: #4A83DD; border-bottom-color: #4A83DD; }',
'.body { padding: 20px 24px; overflow-y: auto; }',
'.grid { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 12px; }',
'.srcbtn { background: #FAFAF9; border: 2px solid #E7E5E4; border-radius: 10px;',
' padding: 0; cursor: pointer; text-align: left; overflow: hidden;',
' transition: border-color .15s, box-shadow .15s; }',
'.srcbtn:hover { border-color: #D4D4D1; }',
'.srcbtn.selected { border-color: #4A83DD;',
' box-shadow: 0 0 0 3px rgba(74,131,221,0.18); }',
'.srcthumb { aspect-ratio: 16/10; background: #F5F5F4; display: flex;',
' align-items: center; justify-content: center; color: #A8A29E;',
' font-size: 32px; }',
'.srcname { padding: 8px 10px; font-size: 13px; color: #1C1917; font-weight: 500;',
' white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }',
'.srcapp { padding: 0 10px 8px; font-size: 11px; color: #78716C;',
' white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }',
'.empty { padding: 32px 0; text-align: center; color: #78716C; font-size: 13px; }',
'.foot { padding: 12px 16px; border-top: 1px solid #E7E5E4; display: flex;',
' justify-content: flex-end; gap: 8px; }',
'.btn { appearance: none; -webkit-appearance: none; border: 0; border-radius: 10px;',
' padding: 9px 16px; font-size: 14px; font-weight: 500; cursor: pointer; }',
'.btn-secondary { background: transparent; color: #1C1917; }',
'.btn-secondary:hover { background: #F5F5F4; }',
'.btn-primary { background: #4A83DD; color: #fff; }',
'.btn-primary:hover { background: #3D6DC4; }',
'.btn-primary:disabled { background: #D4D4D1; cursor: not-allowed; }',
].join('\n');
shadow.appendChild(styleTag);
function hostnameOf(url) {
try { return new URL(url).hostname || url; } catch (e) { return url; }
}
const origin = (typeof location !== 'undefined' && location.origin) || 'this site';
let activeTab = sources.some(function (s) { return s.kind === 'screen'; })
? 'screen'
: 'window';
let selectedId = null;
// DOM is constructed imperatively (no innerHTML) because hosts
// like Google Meet ship strict Trusted Types CSP that rejects
// string-based HTML assignment with a TypeError. `createElement`
// and `appendChild` are policy-free and work everywhere.
const card = document.createElement('div');
card.className = 'card';
function el(tag, attrs, text) {
const node = document.createElement(tag);
if (attrs) {
Object.keys(attrs).forEach(function (k) {
if (k === 'className') node.className = attrs[k];
else node.setAttribute(k, attrs[k]);
});
}
if (text != null) node.textContent = text;
return node;
}
const head = el('div', { className: 'head' });
const headLeft = el('div');
headLeft.appendChild(el('div', { className: 'title' }, 'Choose what to share'));
const originEl = el(
'div',
{ className: 'origin' },
hostnameOf(origin) + ' wants to share your screen.'
);
headLeft.appendChild(originEl);
head.appendChild(headLeft);
const closeBtn = el(
'button',
{ className: 'closebtn', 'data-action': 'cancel', 'aria-label': 'Cancel' },
'✕'
);
head.appendChild(closeBtn);
card.appendChild(head);
const tabs = el('div', { className: 'tabs' });
const screenTab = el('button', { className: 'tab', 'data-tab': 'screen' }, 'Entire Screen');
const windowTab = el('button', { className: 'tab', 'data-tab': 'window' }, 'Window');
tabs.appendChild(screenTab);
tabs.appendChild(windowTab);
card.appendChild(tabs);
const bodyEl = el('div', { className: 'body' });
const gridEl = el('div', { className: 'grid' });
bodyEl.appendChild(gridEl);
card.appendChild(bodyEl);
const foot = el('div', { className: 'foot' });
const cancelBtn = el(
'button',
{ className: 'btn btn-secondary', 'data-action': 'cancel' },
'Cancel'
);
const shareBtn = el('button', { className: 'btn btn-primary' }, 'Share');
shareBtn.disabled = true;
foot.appendChild(cancelBtn);
foot.appendChild(shareBtn);
card.appendChild(foot);
shadow.appendChild(card);
const tabButtons = [screenTab, windowTab];
function setTab(next) {
activeTab = next;
tabButtons.forEach(function (btn) {
btn.classList.toggle('active', btn.getAttribute('data-tab') === activeTab);
});
render();
}
function render() {
while (gridEl.firstChild) gridEl.removeChild(gridEl.firstChild);
const filtered = sources.filter(function (s) { return s.kind === activeTab; });
if (filtered.length === 0) {
const empty = document.createElement('div');
empty.className = 'empty';
empty.textContent =
'No ' + (activeTab === 'screen' ? 'screens' : 'windows') + ' available.';
gridEl.appendChild(empty);
shareBtn.disabled = true;
return;
}
filtered.forEach(function (src) {
const btn = document.createElement('button');
btn.className = 'srcbtn' + (selectedId === src.id ? ' selected' : '');
btn.setAttribute('data-source-id', src.id);
const thumb = document.createElement('div');
thumb.className = 'srcthumb';
if (src.thumbnailPngBase64) {
const img = document.createElement('img');
img.src = 'data:image/png;base64,' + src.thumbnailPngBase64;
img.alt = '';
img.style.cssText =
'width: 100%; height: 100%; object-fit: contain; display: block;';
thumb.appendChild(img);
} else {
// Placeholder glyph until the lazy-loaded thumbnail arrives.
thumb.textContent = activeTab === 'screen' ? '□' : '▣';
// Dedup in-flight thumbnail IPCs: render() re-runs on every
// selection change and tab switch, and without this cache
// each pass would re-issue screen_share_thumbnail for every
// source that hadn't yet returned (coderabbit #809).
function paintThumb(b64) {
if (!b64 || typeof b64 !== 'string') return;
const liveBtn = gridEl.querySelector(
'[data-source-id="' + src.id.replace(/"/g, '\\"') + '"]'
);
if (!liveBtn) return;
const liveThumb = liveBtn.querySelector('.srcthumb');
if (!liveThumb) return;
while (liveThumb.firstChild) liveThumb.removeChild(liveThumb.firstChild);
const img = document.createElement('img');
img.src = 'data:image/png;base64,' + b64;
img.alt = '';
img.style.cssText =
'width: 100%; height: 100%; object-fit: contain; display: block;';
liveThumb.appendChild(img);
}
if (src.__thumbnailPromise) {
src.__thumbnailPromise.then(paintThumb, function () {});
} else {
src.__thumbnailPromise = rawInvoke('screen_share_thumbnail', {
args: { token: sessionToken, id: src.id },
}).then(
function (b64) {
if (b64 && typeof b64 === 'string') {
// Stash on the source so future re-renders keep
// the thumbnail without re-requesting it.
src.thumbnailPngBase64 = b64;
}
paintThumb(b64);
return b64;
},
function () {
/* thumbnail failures degrade gracefully to the glyph */
}
);
}
}
const name = document.createElement('div');
name.className = 'srcname';
name.textContent = src.name;
btn.appendChild(thumb);
btn.appendChild(name);
if (src.appName) {
const app = document.createElement('div');
app.className = 'srcapp';
app.textContent = src.appName;
btn.appendChild(app);
}
btn.addEventListener('click', function () {
selectedId = src.id;
render();
});
btn.addEventListener('dblclick', function () {
selectedId = src.id;
finish(sources.find(function (s) { return s.id === selectedId; }) || null);
});
gridEl.appendChild(btn);
});
if (!selectedId || !filtered.some(function (s) { return s.id === selectedId; })) {
selectedId = filtered[0].id;
gridEl.firstChild && gridEl.firstChild.classList.add('selected');
}
shareBtn.disabled = !selectedId;
}
tabButtons.forEach(function (btn) {
btn.addEventListener('click', function () { setTab(btn.getAttribute('data-tab')); });
});
let settled = false;
function finish(pick) {
if (settled) return;
settled = true;
window.removeEventListener('keydown', onKey, true);
try { root.remove(); } catch (e) { /* ignore */ }
resolveOuter(pick);
}
card.querySelectorAll('[data-action="cancel"]').forEach(function (btn) {
btn.addEventListener('click', function () { finish(null); });
});
shareBtn.addEventListener('click', function () {
const pick = sources.find(function (s) { return s.id === selectedId; }) || null;
finish(pick);
});
// Clicks on the backdrop (outside the card) cancel. Clicks inside
// the card bubble up to root too, but we stop them there.
root.addEventListener('click', function (e) {
if (e.target === root || e.composedPath()[0] === root) finish(null);
});
card.addEventListener('click', function (e) { e.stopPropagation(); });
function onKey(e) {
if (e.key === 'Escape') {
e.preventDefault();
e.stopPropagation();
finish(null);
}
}
window.addEventListener('keydown', onKey, true);
setTab(activeTab);
host().appendChild(root);
});
}
let installed = false;
try {
Object.defineProperty(proto, 'getDisplayMedia', {
configurable: true,
writable: true,
value: shim,
});
installed = true;
} catch (e) {
send('log', {
level: 'error',
msg: '[gdm-shim] defineProperty(proto) failed: ' + (e && e.message ? e.message : String(e)),
});
}
if (!installed) {
return origQuery(descriptor);
};
try {
Object.defineProperty(navigator.mediaDevices, 'getDisplayMedia', {
Object.defineProperty(permProto, 'query', {
configurable: true,
writable: true,
value: shim,
value: spoofedQuery,
});
installed = true;
} catch (e2) {
send('log', {
level: 'error',
msg: '[gdm-shim] defineProperty(instance) failed: ' + (e2 && e2.message ? e2.message : String(e2)),
} catch (e) {
Object.defineProperty(navigator.permissions, 'query', {
configurable: true,
writable: true,
value: spoofedQuery,
});
}
navigator.permissions.__ohPermissionsShimInstalled = true;
send('log', { level: 'info', msg: '[recipe-runtime] media permissions.query shim installed' });
}
navigator.mediaDevices.__ohGdmShimInstalled = installed;
// Some pages (Meet) also consult `navigator.permissions.query` and
// branch on the reported state for `display-capture` /
// `camera` / `microphone`. CEF Alloy's Permissions API does not
// reflect what our OnRequestMediaAccessPermission callback will
// grant dynamically, so it defaults to 'prompt' or even 'denied'
// for `display-capture`. A page that sees 'denied' will assume
// sharing is structurally blocked and refuse to call
// getDisplayMedia — or show the "needs permission" modal on cancel.
// We shadow the query for these names so the page sees 'granted'
// and relies on our shim for the actual user decision.
try {
if (
navigator.permissions &&
typeof navigator.permissions.query === 'function' &&
!navigator.permissions.__ohPermissionsShimInstalled
) {
const permProto = Object.getPrototypeOf(navigator.permissions);
const permDescriptor = Object.getOwnPropertyDescriptor(permProto, 'query');
const origQuery = (permDescriptor && permDescriptor.value
? permDescriptor.value
: navigator.permissions.query
).bind(navigator.permissions);
// CEF Alloy's Permissions API doesn't reflect what our
// OnRequestMediaAccessPermission callback will grant dynamically,
// so it defaults to 'prompt' or 'denied' for the media permissions
// we do handle. Pages that consult the Permissions API up front
// (Meet for display-capture; some flows for camera/microphone)
// refuse to try the actual getUserMedia call if they see 'denied'
// here. Spoof all three to 'granted'; the real grant still goes
// through our CEF permission handler where it's scoped per-call.
const spoofed = {
'display-capture': 'granted',
camera: 'granted',
microphone: 'granted',
};
const spoofedQuery = async function (descriptor) {
const n = descriptor && descriptor.name;
if (n && spoofed[n]) {
return {
state: spoofed[n],
status: spoofed[n],
name: n,
onchange: null,
addEventListener: function () {},
removeEventListener: function () {},
dispatchEvent: function () { return true; },
};
}
return origQuery(descriptor);
};
try {
Object.defineProperty(permProto, 'query', {
configurable: true,
writable: true,
value: spoofedQuery,
});
} catch (e) {
Object.defineProperty(navigator.permissions, 'query', {
configurable: true,
writable: true,
value: spoofedQuery,
});
}
navigator.permissions.__ohPermissionsShimInstalled = true;
send('log', { level: 'info', msg: '[gdm-shim] permissions.query shim installed' });
}
} catch (e) {
send('log', {
level: 'warn',
msg: '[gdm-shim] permissions.query shim failed: ' + (e && e.message ? e.message : e),
});
}
} catch (e) {
send('log', {
level: 'info',
msg:
'[gdm-shim] install=' + installed +
' on ' + ((typeof location !== 'undefined' && location.origin) || '?'),
level: 'warn',
msg: '[recipe-runtime] media permissions.query shim failed: ' + (e && e.message ? e.message : e),
});
})();
}
})();
@@ -1,266 +0,0 @@
import { useCallback } from 'react';
import {
type ScreenIntelligenceState,
useScreenIntelligenceState,
} from '../../features/screen-intelligence/useScreenIntelligenceState';
import { useT } from '../../lib/i18n/I18nContext';
const formatBytes = (bytes: number | null | undefined): string => {
if (bytes == null) return '-';
if (bytes < 1024) return `${bytes} B`;
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
};
interface ScreenIntelligenceDebugPanelProps {
state?: Pick<
ScreenIntelligenceState,
| 'status'
| 'captureTestResult'
| 'isCaptureTestRunning'
| 'recentVisionSummaries'
| 'lastError'
| 'refreshStatus'
| 'refreshVision'
| 'runCaptureTest'
>;
}
const ScreenIntelligenceDebugPanelContent = ({
state: providedState,
}: Required<Pick<ScreenIntelligenceDebugPanelProps, 'state'>>) => {
const { t } = useT();
const {
status,
captureTestResult,
isCaptureTestRunning,
recentVisionSummaries,
lastError,
refreshStatus,
refreshVision,
runCaptureTest,
} = providedState;
const handleCaptureTest = useCallback(() => {
void runCaptureTest();
}, [runCaptureTest]);
const handleRefreshStatus = useCallback(() => {
void refreshStatus();
void refreshVision(5);
}, [refreshStatus, refreshVision]);
const permissions = status?.permissions;
const session = status?.session;
return (
<div className="space-y-4">
<div className="flex items-center justify-between">
<h3 className="text-sm font-semibold text-stone-100">
{t('intelligence.screenDebug.title')}
</h3>
<button
onClick={handleRefreshStatus}
className="rounded-lg border border-stone-700 bg-stone-800/60 px-3 py-1 text-xs text-content-faint dark:text-neutral-600 transition-colors hover:bg-stone-700/60">
{t('common.refresh')}
</button>
</div>
{/* Permissions */}
<div className="rounded-xl border border-stone-700 bg-stone-900/50 p-3">
<h4 className="mb-2 text-xs font-medium uppercase tracking-wide text-content-faint">
{t('intelligence.screenDebug.permissions')}
</h4>
{status?.platform_supported === false ? (
<p className="text-xs text-content-muted">
{t('intelligence.screenDebug.platformNotSupported')}
</p>
) : (
<div className="grid grid-cols-3 gap-2 text-xs">
<PermissionDot
label={t('intelligence.screenDebug.permScreen')}
value={permissions?.screen_recording}
/>
<PermissionDot
label={t('intelligence.screenDebug.permAccessibility')}
value={permissions?.accessibility}
/>
<PermissionDot
label={t('intelligence.screenDebug.permInput')}
value={permissions?.input_monitoring}
/>
</div>
)}
</div>
{/* Session Status */}
<div className="rounded-xl border border-stone-700 bg-stone-900/50 p-3">
<h4 className="mb-2 text-xs font-medium uppercase tracking-wide text-content-faint">
{t('intelligence.screenDebug.session')}
</h4>
<div className="space-y-1 text-xs text-content-faint dark:text-neutral-600">
<div className="flex justify-between">
<span>{t('intelligence.screenDebug.active')}</span>
<span className={session?.active ? 'text-green-400' : 'text-content-muted'}>
{session?.active ? t('common.yes') : t('common.no')}
</span>
</div>
<div className="flex justify-between">
<span>{t('intelligence.screenDebug.frames')}</span>
<span>{session?.frames_in_memory ?? 0}</span>
</div>
<div className="flex justify-between">
<span>{t('intelligence.screenDebug.visionState')}</span>
<span>{session?.vision_state ?? t('intelligence.screenDebug.idle')}</span>
</div>
<div className="flex justify-between">
<span>{t('intelligence.screenDebug.visionQueue')}</span>
<span>{session?.vision_queue_depth ?? 0}</span>
</div>
{session?.last_context && (
<div className="flex justify-between">
<span>{t('intelligence.screenDebug.lastApp')}</span>
<span className="max-w-[180px] truncate">{session.last_context}</span>
</div>
)}
</div>
</div>
{/* Capture Test */}
<div className="rounded-xl border border-stone-700 bg-stone-900/50 p-3">
<h4 className="mb-2 text-xs font-medium uppercase tracking-wide text-content-faint">
{t('intelligence.screenDebug.captureTest')}
</h4>
<button
onClick={handleCaptureTest}
disabled={isCaptureTestRunning}
className="mb-3 w-full rounded-lg border border-primary-600/40 bg-primary-600/20 px-3 py-2 text-sm font-medium text-primary-300 transition-colors hover:bg-primary-600/30 disabled:opacity-50">
{isCaptureTestRunning
? t('intelligence.screenDebug.capturing')
: t('intelligence.screenDebug.testCapture')}
</button>
{captureTestResult && (
<div className="space-y-2">
<div className="space-y-1 text-xs text-content-faint dark:text-neutral-600">
<div className="flex justify-between">
<span>{t('intelligence.screenDebug.status')}</span>
<span className={captureTestResult.ok ? 'text-green-400' : 'text-red-400'}>
{captureTestResult.ok
? t('intelligence.screenDebug.captureSuccess')
: t('intelligence.screenDebug.captureFailed')}
</span>
</div>
<div className="flex justify-between">
<span>{t('intelligence.screenDebug.mode')}</span>
<span>{captureTestResult.capture_mode}</span>
</div>
<div className="flex justify-between">
<span>{t('intelligence.screenDebug.time')}</span>
<span>{captureTestResult.timing_ms}ms</span>
</div>
{captureTestResult.bytes_estimate != null && (
<div className="flex justify-between">
<span>{t('intelligence.screenDebug.size')}</span>
<span>{formatBytes(captureTestResult.bytes_estimate)}</span>
</div>
)}
{captureTestResult.context && (
<div className="flex justify-between">
<span>{t('intelligence.screenDebug.app')}</span>
<span className="max-w-[180px] truncate">
{captureTestResult.context.app_name ?? t('intelligence.screenDebug.unknown')}
</span>
</div>
)}
{captureTestResult.context?.bounds_width != null && (
<div className="flex justify-between">
<span>{t('intelligence.screenDebug.bounds')}</span>
<span>
{captureTestResult.context.bounds_width}x
{captureTestResult.context.bounds_height} at (
{captureTestResult.context.bounds_x},{captureTestResult.context.bounds_y})
</span>
</div>
)}
</div>
{captureTestResult.error && (
<div className="rounded-lg border border-red-800/50 bg-red-900/20 p-2 text-xs text-red-300">
{captureTestResult.error}
</div>
)}
{captureTestResult.image_ref && (
<div className="overflow-hidden rounded-lg border border-stone-700">
<img
src={captureTestResult.image_ref}
alt={t('intelligence.screenDebug.captureAlt')}
className="w-full"
/>
</div>
)}
</div>
)}
</div>
{/* Recent Vision Summaries */}
{recentVisionSummaries.length > 0 && (
<div className="rounded-xl border border-stone-700 bg-stone-900/50 p-3">
<h4 className="mb-2 text-xs font-medium uppercase tracking-wide text-content-faint">
{t('intelligence.screenDebug.recentVisionSummaries')}
</h4>
<div className="space-y-2">
{recentVisionSummaries.map(summary => (
<div
key={summary.id}
className="rounded-lg border border-stone-700/50 bg-stone-800/30 p-2 text-xs">
<div className="flex justify-between text-content-faint">
<span>{summary.app_name ?? t('intelligence.screenDebug.unknown')}</span>
<span>
{new Date(summary.captured_at_ms).toLocaleTimeString()} &middot;{' '}
{(summary.confidence * 100).toFixed(0)}%
</span>
</div>
<div className="mt-1 text-stone-200">{summary.actionable_notes}</div>
</div>
))}
</div>
</div>
)}
{/* Error Display */}
{lastError && (
<div className="rounded-lg border border-red-800/50 bg-red-900/20 p-2 text-xs text-red-300">
{lastError}
</div>
)}
</div>
);
};
const OwnedScreenIntelligenceDebugPanel = () => {
const state = useScreenIntelligenceState({ loadVision: true, visionLimit: 5, pollMs: 2000 });
return <ScreenIntelligenceDebugPanelContent state={state} />;
};
const ScreenIntelligenceDebugPanel = ({ state }: ScreenIntelligenceDebugPanelProps) => {
if (state) {
return <ScreenIntelligenceDebugPanelContent state={state} />;
}
return <OwnedScreenIntelligenceDebugPanel />;
};
const PermissionDot = ({ label, value }: { label: string; value?: string }) => {
const color =
value === 'granted' ? 'bg-green-500' : value === 'denied' ? 'bg-red-500' : 'bg-stone-600';
return (
<div className="flex items-center gap-1.5">
<div className={`h-2 w-2 rounded-full ${color}`} />
<span className="text-content-faint dark:text-neutral-600">{label}</span>
</div>
);
};
export default ScreenIntelligenceDebugPanel;
@@ -26,7 +26,6 @@ import type { ToastNotification } from '../../types/intelligence';
import SettingsPanel from '../settings/layout/SettingsPanel';
import CreateSkillModal from '../skills/CreateSkillModal';
import UnifiedSkillCard from '../skills/SkillCard';
import { BUILT_IN_SKILL_ICONS } from '../skills/skillIcons';
import UninstallSkillConfirmDialog from '../skills/UninstallSkillConfirmDialog';
import Button from '../ui/Button';
import { ToastContainer } from './Toast';
@@ -181,7 +180,7 @@ export default function WorkflowsTab({ asSettingsPanel = false }: WorkflowsTabPr
return (
<UnifiedSkillCard
key={wf.id}
icon={BUILT_IN_SKILL_ICONS.screenIntelligence}
icon={null}
title={wf.name}
description={wf.description}
statusLabel={scopeLabel}
@@ -1,161 +0,0 @@
import { fireEvent, render, screen } from '@testing-library/react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import {
type ScreenIntelligenceState,
useScreenIntelligenceState,
} from '../../../features/screen-intelligence/useScreenIntelligenceState';
import ScreenIntelligenceDebugPanel from '../ScreenIntelligenceDebugPanel';
vi.mock('../../../features/screen-intelligence/useScreenIntelligenceState', () => ({
useScreenIntelligenceState: vi.fn(),
}));
const baseState: ScreenIntelligenceState = {
status: {
platform_supported: true,
permissions: {
screen_recording: 'granted',
accessibility: 'granted',
input_monitoring: 'granted',
},
features: { screen_monitoring: true },
session: {
active: false,
started_at_ms: null,
expires_at_ms: null,
remaining_ms: null,
ttl_secs: 300,
panic_hotkey: 'Cmd+Shift+.',
stop_reason: null,
frames_in_memory: 0,
last_capture_at_ms: null,
last_context: null,
vision_enabled: true,
vision_state: 'idle',
vision_queue_depth: 0,
last_vision_at_ms: null,
last_vision_summary: null,
},
config: {
enabled: true,
capture_policy: 'hybrid',
policy_mode: 'all_except_blacklist',
baseline_fps: 1,
vision_enabled: true,
session_ttl_secs: 300,
panic_stop_hotkey: 'Cmd+Shift+.',
autocomplete_enabled: true,
use_vision_model: true,
keep_screenshots: false,
allowlist: [],
denylist: [],
},
denylist: [],
is_context_blocked: false,
},
lastRestartSummary: null,
recentVisionSummaries: [],
captureTestResult: null,
isCaptureTestRunning: false,
isLoading: false,
isRequestingPermissions: false,
isRestartingCore: false,
isStartingSession: false,
isStoppingSession: false,
isLoadingVision: false,
isFlushingVision: false,
lastError: null,
refreshStatus: vi.fn().mockResolvedValue(null),
requestPermission: vi.fn().mockResolvedValue(null),
refreshPermissionsWithRestart: vi.fn().mockResolvedValue(null),
startSession: vi.fn().mockResolvedValue(null),
stopSession: vi.fn().mockResolvedValue(null),
refreshVision: vi.fn().mockResolvedValue([]),
flushVision: vi.fn().mockResolvedValue(undefined),
runCaptureTest: vi.fn().mockResolvedValue(undefined),
clearError: vi.fn(),
};
describe('ScreenIntelligenceDebugPanel', () => {
beforeEach(() => {
vi.clearAllMocks();
vi.mocked(useScreenIntelligenceState).mockReturnValue(baseState);
});
it('renders successful capture diagnostics and preview image', async () => {
const state: ScreenIntelligenceState = {
...baseState,
captureTestResult: {
ok: true,
capture_mode: 'windowed',
context: {
app_name: 'Safari',
window_title: 'GitHub',
bounds_x: 10,
bounds_y: 20,
bounds_width: 1440,
bounds_height: 900,
},
image_ref: 'data:image/png;base64,ZmFrZQ==',
bytes_estimate: 2048,
error: null,
timing_ms: 155,
},
};
render(<ScreenIntelligenceDebugPanel state={state} />);
expect(screen.getByText('Success')).toBeInTheDocument();
expect(screen.getByText('windowed')).toBeInTheDocument();
expect(screen.getByText('155ms')).toBeInTheDocument();
expect(screen.getByText('2.0 KB')).toBeInTheDocument();
expect(screen.getByText('Safari')).toBeInTheDocument();
expect(screen.getByAltText('Capture test result')).toHaveAttribute(
'src',
'data:image/png;base64,ZmFrZQ=='
);
});
it('shows "Platform not supported" and hides permission dots when platform_supported is false', () => {
const state: ScreenIntelligenceState = {
...baseState,
status: { ...baseState.status!, platform_supported: false },
};
render(<ScreenIntelligenceDebugPanel state={state} />);
expect(screen.getByText('Platform not supported')).toBeInTheDocument();
expect(screen.queryByText('Screen')).not.toBeInTheDocument();
expect(screen.queryByText('Accessibility')).not.toBeInTheDocument();
expect(screen.queryByText('Input')).not.toBeInTheDocument();
});
it('renders capture failures without breaking the diagnostics panel', async () => {
const state: ScreenIntelligenceState = {
...baseState,
captureTestResult: {
ok: false,
capture_mode: 'fullscreen',
context: null,
image_ref: null,
bytes_estimate: null,
error: 'screen recording permission is not granted',
timing_ms: 42,
},
};
render(<ScreenIntelligenceDebugPanel state={state} />);
fireEvent.click(screen.getByRole('button', { name: 'Refresh' }));
expect(screen.getByText('Failed')).toBeInTheDocument();
expect(screen.getByText('fullscreen')).toBeInTheDocument();
expect(screen.getByText('42ms')).toBeInTheDocument();
expect(screen.getByText('screen recording permission is not granted')).toBeInTheDocument();
expect(screen.queryByAltText('Capture test result')).not.toBeInTheDocument();
expect(screen.getByText('Permissions')).toBeInTheDocument();
expect(baseState.refreshStatus).toHaveBeenCalledTimes(1);
expect(baseState.refreshVision).toHaveBeenCalledWith(5);
});
});
@@ -58,7 +58,7 @@ describe('oauthAuthReadiness', () => {
activeMode: 'os_keyring',
backendName: 'os',
},
runtime: { screenIntelligence: null, localAi: null, service: null },
runtime: { localAi: null, service: null },
},
teams: [],
teamMembersById: {},
@@ -125,7 +125,7 @@ describe('oauthAuthReadiness', () => {
activeMode: 'os_keyring',
backendName: 'os',
},
runtime: { screenIntelligence: null, localAi: null, service: null },
runtime: { localAi: null, service: null },
},
teams: [],
teamMembersById: {},
@@ -20,7 +20,6 @@ type SettingsRoute =
| 'agent-access'
| 'account'
| 'cron-jobs'
| 'screen-intelligence'
| 'privacy'
| 'billing'
| 'team'
@@ -32,7 +31,6 @@ type SettingsRoute =
| 'tools'
| 'recovery-phrase'
| 'wallet-balances'
| 'screen-awareness-debug'
| 'local-model-debug'
| 'notifications'
| 'notification-routing'
@@ -116,7 +116,6 @@ export const SETTINGS_NAV_ICONS: Record<string, ReactNode> = {
'M21 16V8a2 2 0 00-1-1.73l-7-4a2 2 0 00-2 0l-7 4A2 2 0 003 8v8a2 2 0 001 1.73l7 4a2 2 0 002 0l7-4A2 2 0 0021 16z'
)
),
'screen-awareness-debug': icon(stroke('M3 5h18v12H3zM8 21h8m-4-4v4')),
// Automation & Integrations
tasks: icon(
stroke(
@@ -101,7 +101,7 @@ const CompanionPanel = () => {
const sessionActive = status?.active ?? false;
return (
<SettingsPanel description={t('pages.settings.features.desktopCompanionDesc')}>
<SettingsPanel description={t('settings.assistant.desktopCompanionDesc')}>
{/* Session status + controls */}
<SettingsSection>
<SettingsRow
@@ -182,22 +182,6 @@ const CompanionPanel = () => {
label={t('settings.companion.sessionTtl')}
control={<span className="text-xs text-content-muted">{config.ttl_secs}s</span>}
/>
<SettingsRow
label={t('settings.companion.screenCapture')}
control={
<span className="text-xs text-content-muted">
{config.capture_screen ? t('common.enabled') : t('common.disabled')}
</span>
}
/>
<SettingsRow
label={t('settings.companion.appContext')}
control={
<span className="text-xs text-content-muted">
{config.include_app_context ? t('common.enabled') : t('common.disabled')}
</span>
}
/>
</SettingsSection>
)}
@@ -1,6 +1,6 @@
// [settings] Developer & Diagnostics panel — debug-only entries only.
// User-facing routes (agents, autonomy, agent-access, sandbox-settings,
// activity-level, tools, companion, screen-intelligence, voice, embeddings,
// activity-level, tools, companion, voice, embeddings,
// heartbeat, ledger-usage, cost-dashboard, task-sources, composio-routing,
// webhooks-triggers, migration, security) have been moved to their canonical
// section pages. Only genuine diagnostics remain here.
@@ -46,7 +46,7 @@ interface DevGroup {
// Removed from all groups (moved to canonical section pages):
// agents, autonomy, agent-access, sandbox-settings, activity-level
// → Settings → Agents
// tools, companion, screen-intelligence
// tools, companion
// → Settings → Features
// voice, embeddings, heartbeat, ledger-usage, cost-dashboard
// → Settings → AI & Models
@@ -121,22 +121,6 @@ const modelsDebugGroup: DevGroup = {
</svg>
),
},
{
id: 'screen-awareness-debug',
titleKey: 'settings.developerMenu.screenAwareness.title',
descriptionKey: 'settings.developerMenu.screenAwareness.desc',
route: 'screen-awareness-debug',
icon: (
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M3 5h18v12H3zM8 21h8m-4-4v4"
/>
</svg>
),
},
],
};
@@ -1,292 +0,0 @@
import { type ComponentProps, useRef, useState } from 'react';
import ScreenIntelligenceDebugPanel from '../../../components/intelligence/ScreenIntelligenceDebugPanel';
import { useScreenIntelligenceState } from '../../../features/screen-intelligence/useScreenIntelligenceState';
import { useT } from '../../../lib/i18n/I18nContext';
import { isTauri, openhumanUpdateScreenIntelligenceSettings } from '../../../utils/tauriCommands';
import Button from '../../ui/Button';
import {
SettingsCheckbox,
SettingsEmptyState,
SettingsNumberField,
SettingsRow,
SettingsSection,
SettingsStatusLine,
SettingsTextArea,
} from '../controls';
import SettingsPanel from '../layout/SettingsPanel';
const DebugSection = ({
state,
t,
}: {
state: ComponentProps<typeof ScreenIntelligenceDebugPanel>['state'];
t: (key: string, fallback?: string) => string;
}) => {
const [isOpen, setIsOpen] = useState(false);
return (
<SettingsSection>
<div className="px-4 py-3 space-y-3">
<button
type="button"
onClick={() => setIsOpen(prev => !prev)}
className="flex w-full items-center justify-between text-sm font-semibold text-content">
<span>{t('screenAwareness.debug.debugAndDiagnostics')}</span>
<span className="text-xs text-content-muted">
{isOpen ? t('screenAwareness.debug.collapse') : t('screenAwareness.debug.expand')}
</span>
</button>
{isOpen && <ScreenIntelligenceDebugPanel state={state} />}
</div>
</SettingsSection>
);
};
const ScreenAwarenessDebugPanel = () => {
const { t } = useT();
const {
status,
lastError,
isLoadingVision,
recentVisionSummaries,
refreshStatus,
refreshVision,
runCaptureTest,
captureTestResult,
isCaptureTestRunning,
} = useScreenIntelligenceState({ loadVision: true, visionLimit: 10, pollMs: 2000 });
const [baselineFps, setBaselineFps] = useState<string>('1');
const [useVisionModel, setUseVisionModel] = useState<boolean>(true);
const [keepScreenshots, setKeepScreenshots] = useState<boolean>(false);
const [allowlistText, setAllowlistText] = useState('');
const [denylistText, setDenylistText] = useState('');
const [isSavingConfig, setIsSavingConfig] = useState(false);
const [configError, setConfigError] = useState<string | null>(null);
// Initialize form state from server config once on first render where config
// is available. After initialization, form state is user-controlled until save.
// This runs during render (not in useEffect) so it is synchronous and avoids
// the set-state-in-effect lint rule.
const initializedRef = useRef(false);
if (!initializedRef.current && status?.config) {
initializedRef.current = true;
// One-time assignment — React batches these with the current render.
setBaselineFps(String(status.config.baseline_fps ?? 1));
setUseVisionModel(status.config.use_vision_model ?? true);
setKeepScreenshots(status.config.keep_screenshots ?? false);
setAllowlistText((status.config.allowlist ?? []).join('\n'));
setDenylistText((status.config.denylist ?? []).join('\n'));
}
const saveConfig = async () => {
if (!isTauri()) return;
setConfigError(null);
setIsSavingConfig(true);
try {
const fps = Number(baselineFps);
await openhumanUpdateScreenIntelligenceSettings({
enabled: status?.config.enabled ?? false,
policy_mode:
status?.config.policy_mode === 'whitelist_only'
? 'whitelist_only'
: 'all_except_blacklist',
baseline_fps: Number.isFinite(fps) && fps > 0 ? fps : 1,
use_vision_model: useVisionModel,
keep_screenshots: keepScreenshots,
allowlist: allowlistText
.split('\n')
.map(v => v.trim())
.filter(Boolean),
denylist: denylistText
.split('\n')
.map(v => v.trim())
.filter(Boolean),
});
await refreshStatus();
} catch (error) {
setConfigError(
error instanceof Error ? error.message : t('screenAwareness.debug.failedToSave')
);
} finally {
setIsSavingConfig(false);
}
};
return (
<SettingsPanel description={t('settings.developerMenu.screenAwareness.desc')}>
<>
{/* Advanced policy settings */}
<SettingsSection title={t('screenAwareness.debug.policyTitle')}>
<div className="px-4 py-3 space-y-3">
<SettingsRow
htmlFor="screen-baseline-fps"
label={t('screenAwareness.debug.baselineFps')}
control={
<SettingsNumberField
id="screen-baseline-fps"
min={0.2}
max={30}
step={0.1}
value={baselineFps}
onChange={setBaselineFps}
onCommit={() => {}}
aria-label={t('screenAwareness.debug.baselineFps')}
/>
}
/>
<SettingsRow
htmlFor="screen-use-vision-model"
label={t('screenAwareness.debug.useVisionModel')}
description={t('screenAwareness.debug.useVisionModelDesc')}
control={
<SettingsCheckbox
id="screen-use-vision-model"
checked={useVisionModel}
onCheckedChange={setUseVisionModel}
/>
}
/>
<SettingsRow
htmlFor="screen-keep-screenshots"
label={t('screenAwareness.debug.keepScreenshots')}
description={t('screenAwareness.debug.keepScreenshotsDesc')}
control={
<SettingsCheckbox
id="screen-keep-screenshots"
checked={keepScreenshots}
onCheckedChange={setKeepScreenshots}
/>
}
/>
<div className="space-y-1">
<div className="text-xs text-content-muted">
{t('screenAwareness.debug.allowlist')}
</div>
<SettingsTextArea
value={allowlistText}
onChange={event => setAllowlistText(event.target.value)}
rows={3}
aria-label={t('screenAwareness.debug.allowlist')}
/>
</div>
<div className="space-y-1">
<div className="text-xs text-content-muted">
{t('screenAwareness.debug.denylist')}
</div>
<SettingsTextArea
value={denylistText}
onChange={event => setDenylistText(event.target.value)}
rows={3}
aria-label={t('screenAwareness.debug.denylist')}
/>
</div>
<Button
type="button"
variant="primary"
size="sm"
onClick={() => void saveConfig()}
disabled={isSavingConfig}>
{isSavingConfig ? t('common.loading') : t('screenAwareness.debug.saveSettings')}
</Button>
<SettingsStatusLine saving={false} error={configError} savingLabel="" />
</div>
</SettingsSection>
{/* Session stats */}
<SettingsSection title={t('screenAwareness.debug.sessionStats')}>
<div className="px-4 py-3 text-sm text-content-muted space-y-1">
<div>
{t('screenAwareness.debug.framesEphemeral')}: {status?.session.frames_in_memory ?? 0}
</div>
<div>
{t('screenAwareness.debug.panicStop')}:{' '}
{status?.session.panic_hotkey ?? t('screenAwareness.debug.defaultPanicHotkey')}
</div>
<div>
{t('screenAwareness.debug.vision')}:{' '}
{status?.session.vision_state ?? t('screenAwareness.debug.idle')}
</div>
<div>
{t('screenAwareness.debug.visionQueue')}: {status?.session.vision_queue_depth ?? 0}
</div>
<div>
{t('screenAwareness.debug.lastVision')}:{' '}
{status?.session.last_vision_at_ms
? new Date(status.session.last_vision_at_ms).toLocaleTimeString()
: t('screenAwareness.debug.notAvailable')}
</div>
</div>
</SettingsSection>
{/* Vision summaries */}
<SettingsSection title={t('screenAwareness.debug.visionSummaries')}>
<div className="px-4 py-3 space-y-3">
<div className="flex justify-end">
<Button
type="button"
variant="secondary"
size="xs"
onClick={() => void refreshVision(10)}
disabled={isLoadingVision}>
{isLoadingVision ? t('screenAwareness.debug.refreshing') : t('common.refresh')}
</Button>
</div>
{recentVisionSummaries.length === 0 ? (
<SettingsEmptyState label={t('screenAwareness.debug.noSummaries')} />
) : (
<div className="space-y-2">
{recentVisionSummaries.map(summary => (
<div
key={summary.id}
className="rounded-xl border border-line bg-surface p-3 text-xs">
<div className="text-content-muted">
{new Date(summary.captured_at_ms).toLocaleTimeString()} ·{' '}
{summary.app_name ?? t('screenAwareness.debug.unknownApp')}
{summary.window_title ? ` · ${summary.window_title}` : ''}
</div>
<div className="mt-1 text-content">{summary.actionable_notes}</div>
</div>
))}
</div>
)}
</div>
</SettingsSection>
{/* Debug & Diagnostics (collapsible) */}
<DebugSection
t={t}
state={{
status,
recentVisionSummaries,
lastError,
captureTestResult,
isCaptureTestRunning,
refreshStatus,
refreshVision,
runCaptureTest,
}}
/>
{/* Platform unsupported notice */}
{status !== null && !status.platform_supported && (
<div className="rounded-xl border border-amber-300 dark:border-amber-500/40 bg-amber-50 dark:bg-amber-500/10 p-3 text-sm text-amber-700 dark:text-amber-300">
{t('screenAwareness.debug.macosOnly')}
</div>
)}
{/* Error notice */}
{lastError && <SettingsStatusLine saving={false} error={lastError} savingLabel="" />}
</>
</SettingsPanel>
);
};
export default ScreenAwarenessDebugPanel;
@@ -1,281 +0,0 @@
import { useEffect, useMemo, useRef, useState } from 'react';
import { useScreenIntelligenceState } from '../../../features/screen-intelligence/useScreenIntelligenceState';
import { useT } from '../../../lib/i18n/I18nContext';
import { isTauri, openhumanUpdateScreenIntelligenceSettings } from '../../../utils/tauriCommands';
import Button from '../../ui/Button';
import {
SettingsRow,
SettingsSection,
SettingsSelect,
SettingsStatusLine,
SettingsSwitch,
} from '../controls';
import SettingsPanel from '../layout/SettingsPanel';
import PermissionsSection from './screen-intelligence/PermissionsSection';
const formatRemaining = (remainingMs: number | null): string => {
if (remainingMs === null || remainingMs <= 0) {
return '00:00';
}
const totalSeconds = Math.floor(remainingMs / 1000);
const mins = Math.floor(totalSeconds / 60)
.toString()
.padStart(2, '0');
const secs = (totalSeconds % 60).toString().padStart(2, '0');
return `${mins}:${secs}`;
};
const ScreenIntelligencePanel = () => {
const { t } = useT();
const {
status,
lastRestartSummary,
isLoading,
isRequestingPermissions,
isRestartingCore,
isStartingSession,
isStoppingSession,
isFlushingVision,
lastError,
refreshStatus,
startSession,
stopSession,
flushVision,
requestPermission,
refreshPermissionsWithRestart,
} = useScreenIntelligenceState({ loadVision: false, pollMs: 2000 });
const [featureOverrides, setFeatureOverrides] = useState<{ screen_monitoring?: boolean }>({});
const [enabled, setEnabled] = useState<boolean>(false);
const [policyMode, setPolicyMode] = useState<'all_except_blacklist' | 'whitelist_only'>(
'all_except_blacklist'
);
const [isSavingConfig, setIsSavingConfig] = useState(false);
const [configError, setConfigError] = useState<string | null>(null);
const lastSyncedConfigSigRef = useRef<string | null>(null);
useEffect(() => {
if (!status?.config) {
return;
}
const sig = JSON.stringify(status.config);
if (lastSyncedConfigSigRef.current === sig) {
return;
}
lastSyncedConfigSigRef.current = sig;
setEnabled(status.config.enabled ?? false);
setPolicyMode(
status.config.policy_mode === 'whitelist_only' ? 'whitelist_only' : 'all_except_blacklist'
);
}, [status?.config]);
const screenMonitoring =
featureOverrides.screen_monitoring ?? status?.features.screen_monitoring ?? true;
const remaining = useMemo(
() => formatRemaining(status?.session.remaining_ms ?? null),
[status?.session.remaining_ms]
);
const anyPermissionDenied =
status?.permissions.screen_recording === 'denied' ||
status?.permissions.accessibility === 'denied' ||
status?.permissions.input_monitoring === 'denied';
const startDisabled =
isStartingSession ||
isLoading ||
!status ||
!status.platform_supported ||
status.session.active ||
status.permissions.accessibility !== 'granted';
const stopDisabled = isStoppingSession || !status?.session.active;
const saveConfig = async () => {
if (!isTauri()) return;
setConfigError(null);
setIsSavingConfig(true);
try {
await openhumanUpdateScreenIntelligenceSettings({
enabled,
policy_mode: policyMode,
baseline_fps: status?.config.baseline_fps ?? 1,
use_vision_model: status?.config.use_vision_model ?? true,
keep_screenshots: status?.config.keep_screenshots ?? false,
allowlist: status?.config.allowlist ?? [],
denylist: status?.config.denylist ?? [],
});
await refreshStatus();
} catch (error) {
setConfigError(error instanceof Error ? error.message : 'Failed to save screen intelligence');
} finally {
setIsSavingConfig(false);
}
};
return (
<SettingsPanel description={t('pages.settings.features.screenAwarenessDesc')}>
<>
{(status?.platform_supported ?? true) && (
<PermissionsSection
screenRecording={status?.permissions.screen_recording ?? 'unknown'}
accessibility={status?.permissions.accessibility ?? 'unknown'}
inputMonitoring={status?.permissions.input_monitoring ?? 'unknown'}
anyPermissionDenied={anyPermissionDenied ?? false}
lastRestartSummary={lastRestartSummary}
permissionCheckProcessPath={status?.permission_check_process_path}
isRequestingPermissions={isRequestingPermissions}
isRestartingCore={isRestartingCore}
isLoading={isLoading}
requestPermission={requestPermission}
refreshPermissionsWithRestart={refreshPermissionsWithRestart}
refreshStatus={refreshStatus}
/>
)}
{/* Screen awareness config */}
<SettingsSection title={t('settings.features.screenAwareness')}>
{/* Enabled toggle */}
<SettingsRow
htmlFor="screen-intelligence-enabled"
label={t('common.enabled')}
control={
<SettingsSwitch
id="screen-intelligence-enabled"
checked={enabled}
onCheckedChange={setEnabled}
aria-label={t('common.enabled')}
/>
}
/>
{/* Policy mode */}
<SettingsRow
htmlFor="select-policy-mode"
label={t('settings.screenAwareness.mode')}
control={
<SettingsSelect
id="select-policy-mode"
value={policyMode}
onChange={event =>
setPolicyMode(
event.target.value === 'whitelist_only'
? 'whitelist_only'
: 'all_except_blacklist'
)
}
inputSize="sm">
<option value="all_except_blacklist">
{t('settings.screenAwareness.allExceptBlacklist')}
</option>
<option value="whitelist_only">
{t('settings.screenAwareness.whitelistOnly')}
</option>
</SettingsSelect>
}
/>
{/* Screen monitoring toggle */}
<SettingsRow
htmlFor="screen-intelligence-monitoring"
label={t('settings.screenAwareness.screenMonitoring')}
control={
<SettingsSwitch
id="screen-intelligence-monitoring"
checked={screenMonitoring}
onCheckedChange={next =>
setFeatureOverrides(current => ({ ...current, screen_monitoring: next }))
}
aria-label={t('settings.screenAwareness.screenMonitoring')}
/>
}
/>
{/* Save */}
<div className="px-4 py-3 space-y-2">
<Button
type="button"
variant="primary"
size="sm"
onClick={() => void saveConfig()}
disabled={isSavingConfig}>
{isSavingConfig ? 'Saving…' : t('settings.screenAwareness.saveSettings')}
</Button>
<SettingsStatusLine
saving={false}
savedNote={null}
error={configError}
savingLabel={t('settings.agentAccess.saving')}
/>
</div>
</SettingsSection>
{/* Session controls */}
<SettingsSection title={t('settings.screenAwareness.session')}>
<div className="px-4 py-3 space-y-3">
<div className="text-sm text-content-secondary space-y-1">
<div>
{t('settings.screenAwareness.status')}:{' '}
{status?.session.active
? t('settings.screenAwareness.active')
: t('settings.screenAwareness.stopped')}
</div>
<div>
{t('settings.screenAwareness.remaining')}: {remaining}
</div>
</div>
<div className="flex gap-2">
<Button
type="button"
variant="secondary"
size="sm"
onClick={() =>
void startSession({
consent: true,
ttl_secs: status?.config.session_ttl_secs ?? 300,
screen_monitoring: screenMonitoring,
})
}
disabled={startDisabled}>
{isStartingSession ? 'Starting…' : t('settings.screenAwareness.startSession')}
</Button>
<Button
type="button"
variant="secondary"
tone="danger"
size="sm"
onClick={() => void stopSession('manual_stop')}
disabled={stopDisabled}>
{isStoppingSession ? 'Stopping…' : t('settings.screenAwareness.stopSession')}
</Button>
<Button
type="button"
variant="secondary"
size="sm"
onClick={() => void flushVision()}
disabled={isFlushingVision || !status?.session.active}>
{isFlushingVision ? 'Analyzing…' : t('settings.screenAwareness.analyzeNow')}
</Button>
</div>
</div>
</SettingsSection>
{status !== null && !status.platform_supported && (
<div className="rounded-xl border border-amber-300 dark:border-amber-500/40 bg-amber-50 dark:bg-amber-500/10 p-3 text-sm text-amber-700 dark:text-amber-300">
{t('settings.screenAwareness.macosOnly')}
</div>
)}
{lastError && (
<div className="rounded-xl border border-red-300 dark:border-red-500/40 bg-red-50 dark:bg-red-500/10 p-3 text-sm text-red-600 dark:text-red-300">
{lastError}
</div>
)}
</>
</SettingsPanel>
);
};
export default ScreenIntelligencePanel;
@@ -31,13 +31,7 @@ const mockStatus = {
last_error: null,
};
const mockConfig = {
hotkey: 'ctrl+space',
activation_mode: 'push',
ttl_secs: 3600,
capture_screen: true,
include_app_context: true,
};
const mockConfig = { hotkey: 'ctrl+space', activation_mode: 'push', ttl_secs: 3600 };
beforeEach(() => {
vi.clearAllMocks();
@@ -244,17 +238,12 @@ describe('CompanionPanel', () => {
});
});
it('renders "Disabled" when capture_screen and include_app_context are false', async () => {
invokeMock.mockImplementation(async (cmd: string) => {
if (cmd === 'companion_status') return mockStatus;
if (cmd === 'companion_config_get') {
return { ...mockConfig, capture_screen: false, include_app_context: false };
}
throw new Error(`unmocked command: ${cmd}`);
});
it('does not render screen capture or app context configuration', async () => {
renderWithProviders(<CompanionPanel />);
await waitFor(() => {
expect(screen.getAllByText('Disabled').length).toBeGreaterThanOrEqual(2);
expect(screen.getByText('ctrl+space')).toBeInTheDocument();
});
expect(screen.queryByText('Screen Capture')).not.toBeInTheDocument();
expect(screen.queryByText('App Context')).not.toBeInTheDocument();
});
});
@@ -127,9 +127,7 @@ describe('DeveloperOptionsPanel — CoreModeBadge', () => {
// Knowledge & Memory group was retired. Assert a destination that IS
// present: 事件日志 (Event Log).
expect(screen.getByText('事件日志')).toBeInTheDocument();
// Two screen-awareness rows now exist (the moved settings row + the debug
// panel), which collapse to the same zh-CN label — assert at least one.
expect(screen.getAllByText('屏幕感知').length).toBeGreaterThan(0);
expect(screen.queryByText('屏幕感知')).not.toBeInTheDocument();
// Composio triggers moved to the Connections Composio page — assert a
// destination that IS still present: MCP 服务器 (MCP Server).
expect(screen.getByText('MCP 服务器')).toBeInTheDocument();
@@ -97,7 +97,7 @@ const setCoreSession = ({
activeMode: 'os_keyring',
backendName: 'os',
},
runtime: { screenIntelligence: null, localAi: null, service: null },
runtime: { localAi: null, service: null },
},
teams: [],
teamMembersById: {},
@@ -1,310 +0,0 @@
/**
* ScreenAwarenessDebugPanel coverage tests.
*
* Target uncovered lines (from diff-cover report):
* 34,38,41,143,181,193,203,205,245,247,251,255-256,261-262,297
*
* These cover:
* - DebugSection toggle (line 34,38,41): expand/collapse
* - Config form inputs: baselineFps, use-vision-model checkbox, keep-screenshots (143,181,193)
* - saveConfig handler (line 203,205)
* - Vision summaries: list rendering, refresh, empty state (245,247,251,255-256,261-262)
* - lastError display (297)
*/
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
// ------------------------------------------------------------------
// Module mocks
// ------------------------------------------------------------------
vi.mock('../../../../lib/i18n/I18nContext', () => ({ useT: () => ({ t: (key: string) => key }) }));
vi.mock('../../hooks/useSettingsNavigation', () => ({
useSettingsNavigation: () => ({ navigateBack: vi.fn(), breadcrumbs: [] }),
}));
vi.mock('../../../../components/intelligence/ScreenIntelligenceDebugPanel', () => ({
default: () => <div data-testid="screen-debug-inner">debug-content</div>,
}));
const { mockIsTauri, mockUpdateScreenIntelligenceSettings, mockUseScreenIntelligenceState } =
vi.hoisted(() => ({
mockIsTauri: vi.fn(() => true),
mockUpdateScreenIntelligenceSettings: vi.fn(),
mockUseScreenIntelligenceState: vi.fn(),
}));
vi.mock('../../../../utils/tauriCommands', () => ({
isTauri: mockIsTauri,
openhumanUpdateScreenIntelligenceSettings: mockUpdateScreenIntelligenceSettings,
}));
vi.mock('../../../../features/screen-intelligence/useScreenIntelligenceState', () => ({
useScreenIntelligenceState: mockUseScreenIntelligenceState,
}));
// ------------------------------------------------------------------
// Fixture builders
// ------------------------------------------------------------------
const makeStatus = (overrides: Record<string, unknown> = {}) => ({
platform_supported: true,
config: {
enabled: true,
baseline_fps: 1,
use_vision_model: true,
keep_screenshots: false,
allowlist: ['TextEdit', 'Xcode'],
denylist: ['Safari'],
policy_mode: 'all_except_blacklist',
...((overrides.config as Record<string, unknown>) ?? {}),
},
session: {
frames_in_memory: 5,
panic_hotkey: 'Ctrl+Shift+P',
vision_state: 'running',
vision_queue_depth: 2,
last_vision_at_ms: Date.now() - 3000,
},
...overrides,
});
const makeVisionSummary = (overrides: Record<string, unknown> = {}) => ({
id: 'vs-001',
captured_at_ms: Date.now() - 10000,
app_name: 'TextEdit',
window_title: 'Untitled',
actionable_notes: 'User is editing a document.',
...overrides,
});
const makeDefaultState = (overrides: Record<string, unknown> = {}) => ({
status: makeStatus(),
lastError: null,
isLoadingVision: false,
recentVisionSummaries: [],
refreshStatus: vi.fn().mockResolvedValue(null),
refreshVision: vi.fn().mockResolvedValue([]),
runCaptureTest: vi.fn().mockResolvedValue(undefined),
captureTestResult: null,
isCaptureTestRunning: false,
...overrides,
});
async function renderPanel(stateOverrides: Record<string, unknown> = {}) {
mockUseScreenIntelligenceState.mockReturnValue(makeDefaultState(stateOverrides));
const { default: ScreenAwarenessDebugPanel } = await import('../ScreenAwarenessDebugPanel');
return render(<ScreenAwarenessDebugPanel />);
}
// ------------------------------------------------------------------
// Tests
// ------------------------------------------------------------------
describe('ScreenAwarenessDebugPanel', () => {
beforeEach(() => {
vi.clearAllMocks();
mockIsTauri.mockReturnValue(true);
mockUpdateScreenIntelligenceSettings.mockResolvedValue({});
});
// ── DebugSection expand/collapse (lines 34, 38, 41) ───────────────────────
it('collapses debug section by default and shows expand label (line 34, 38)', async () => {
await renderPanel();
expect(screen.getByText('screenAwareness.debug.expand')).toBeInTheDocument();
expect(screen.queryByTestId('screen-debug-inner')).not.toBeInTheDocument();
});
it('expands debug section when toggle is clicked (line 41)', async () => {
await renderPanel();
fireEvent.click(screen.getByText('screenAwareness.debug.debugAndDiagnostics'));
expect(screen.getByText('screenAwareness.debug.collapse')).toBeInTheDocument();
expect(screen.getByTestId('screen-debug-inner')).toBeInTheDocument();
});
it('collapses debug section after second click', async () => {
await renderPanel();
const toggleBtn = screen.getByText('screenAwareness.debug.debugAndDiagnostics');
fireEvent.click(toggleBtn);
fireEvent.click(toggleBtn);
expect(screen.getByText('screenAwareness.debug.expand')).toBeInTheDocument();
expect(screen.queryByTestId('screen-debug-inner')).not.toBeInTheDocument();
});
// ── Policy settings form: FPS input (line 143) ───────────────────────────
it('renders baseline FPS input with config value (line 143)', async () => {
await renderPanel({
status: makeStatus({
config: {
baseline_fps: 2,
use_vision_model: true,
keep_screenshots: false,
allowlist: [],
denylist: [],
policy_mode: 'all_except_blacklist',
enabled: true,
},
}),
});
const fpsInput = screen.getByRole('spinbutton') as HTMLInputElement;
expect(fpsInput.value).toBe('2');
});
it('updates baseline FPS on change', async () => {
await renderPanel();
const fpsInput = screen.getByRole('spinbutton');
fireEvent.change(fpsInput, { target: { value: '5' } });
expect((fpsInput as HTMLInputElement).value).toBe('5');
});
// ── Use vision model checkbox (line 181) ──────────────────────────────────
it('renders use-vision-model checkbox checked (line 181)', async () => {
await renderPanel();
// SettingsCheckbox renders a native <input type="checkbox" id="screen-use-vision-model">.
// The panel also has a keep-screenshots checkbox, so query by id.
const checkbox = document.getElementById('screen-use-vision-model') as HTMLInputElement;
expect(checkbox).toBeInTheDocument();
// config.use_vision_model=true → checked=true
expect(checkbox.checked).toBe(true);
expect(screen.getByText('screenAwareness.debug.useVisionModel')).toBeInTheDocument();
});
// ── saveConfig handler (lines 203, 205) ───────────────────────────────────
it('calls updateScreenIntelligenceSettings when save is clicked (line 203)', async () => {
const refreshStatus = vi.fn().mockResolvedValue(null);
mockUseScreenIntelligenceState.mockReturnValue(makeDefaultState({ refreshStatus }));
const { default: ScreenAwarenessDebugPanel } = await import('../ScreenAwarenessDebugPanel');
render(<ScreenAwarenessDebugPanel />);
fireEvent.click(screen.getByText('screenAwareness.debug.saveSettings'));
await waitFor(() => expect(mockUpdateScreenIntelligenceSettings).toHaveBeenCalled());
expect(mockUpdateScreenIntelligenceSettings).toHaveBeenCalledWith(
expect.objectContaining({ enabled: true, use_vision_model: true, keep_screenshots: false })
);
});
it('shows error when saveConfig throws (line 205)', async () => {
mockUpdateScreenIntelligenceSettings.mockRejectedValue(new Error('permission denied'));
await renderPanel();
fireEvent.click(screen.getByText('screenAwareness.debug.saveSettings'));
await waitFor(() => expect(screen.getByText('permission denied')).toBeInTheDocument());
});
it('skips saveConfig when not in tauri env (line 203)', async () => {
mockIsTauri.mockReturnValue(false);
await renderPanel();
fireEvent.click(screen.getByText('screenAwareness.debug.saveSettings'));
// Should not call update
expect(mockUpdateScreenIntelligenceSettings).not.toHaveBeenCalled();
});
// ── Vision summaries: empty state (line 251) ──────────────────────────────
it('shows empty state when no vision summaries (line 251)', async () => {
await renderPanel({ recentVisionSummaries: [] });
expect(screen.getByText('screenAwareness.debug.noSummaries')).toBeInTheDocument();
});
// ── Vision summaries: list rendering (lines 245, 247, 255-256, 261-262) ───
it('renders vision summary rows with app name and notes (lines 255-256, 261-262)', async () => {
const summary = makeVisionSummary();
await renderPanel({ recentVisionSummaries: [summary] });
// app_name and window_title are text nodes inside a div that also contains the
// timestamp and bullet separators — use body.textContent to avoid split-node issues.
expect(document.body.textContent).toContain('TextEdit');
expect(document.body.textContent).toContain('User is editing a document.');
expect(document.body.textContent).toContain('Untitled');
});
it('renders unknownApp when app_name is null (line 261)', async () => {
const summary = makeVisionSummary({ app_name: null, window_title: null });
await renderPanel({ recentVisionSummaries: [summary] });
expect(document.body.textContent).toContain('screenAwareness.debug.unknownApp');
});
it('calls refreshVision when Refresh button is clicked (line 247)', async () => {
const refreshVision = vi.fn().mockResolvedValue([]);
await renderPanel({ refreshVision });
fireEvent.click(screen.getByText('common.refresh'));
await waitFor(() => expect(refreshVision).toHaveBeenCalledWith(10));
});
it('shows refreshing label while vision is loading (line 245)', async () => {
await renderPanel({ isLoadingVision: true });
expect(screen.getByText('screenAwareness.debug.refreshing')).toBeInTheDocument();
});
// ── Platform unsupported notice and lastError (line 297) ─────────────────
it('shows macosOnly notice when platform not supported (line 290-292)', async () => {
const status = makeStatus();
status.platform_supported = false;
await renderPanel({ status });
expect(screen.getByText('screenAwareness.debug.macosOnly')).toBeInTheDocument();
});
it('shows lastError status line when lastError is non-null (line 297)', async () => {
await renderPanel({ lastError: 'screen recording permission denied' });
expect(screen.getByText('screen recording permission denied')).toBeInTheDocument();
});
// ── Session stats section (lines 215-235) ────────────────────────────────
it('renders session stats with live values', async () => {
await renderPanel();
// These keys appear as text nodes inside divs that also hold ': <value>' —
// use body.textContent to avoid split-text-node matching failures.
expect(document.body.textContent).toContain('screenAwareness.debug.framesEphemeral');
expect(document.body.textContent).toContain('screenAwareness.debug.panicStop');
expect(document.body.textContent).toContain('screenAwareness.debug.vision');
});
it('shows defaultPanicHotkey when panic_hotkey is null', async () => {
const status = makeStatus();
// At runtime the JSON from core may return null even though the TypeScript type says string.
// Force-cast to test the nullish-coalescing fallback branch.
(status.session as unknown as Record<string, unknown>).panic_hotkey = null;
await renderPanel({ status });
expect(document.body.textContent).toContain('screenAwareness.debug.defaultPanicHotkey');
});
it('shows notAvailable for last_vision_at_ms when null', async () => {
const status = makeStatus();
// last_vision_at_ms is number | null in the real type, but inferred as number in the fixture.
// Force-cast to assign null and test the notAvailable branch.
(status.session as unknown as Record<string, unknown>).last_vision_at_ms = null;
await renderPanel({ status });
expect(document.body.textContent).toContain('screenAwareness.debug.notAvailable');
});
});
@@ -1,339 +0,0 @@
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import {
type ScreenIntelligenceState,
useScreenIntelligenceState,
} from '../../../../features/screen-intelligence/useScreenIntelligenceState';
import {
type ConfigSnapshot,
isTauri,
openhumanUpdateScreenIntelligenceSettings,
} from '../../../../utils/tauriCommands';
import ScreenIntelligencePanel from '../ScreenIntelligencePanel';
vi.mock('../../../../features/screen-intelligence/useScreenIntelligenceState', () => ({
useScreenIntelligenceState: vi.fn(),
}));
vi.mock('../../../../utils/tauriCommands', async importOriginal => {
const actual = await importOriginal<typeof import('../../../../utils/tauriCommands')>();
return {
...actual,
isTauri: vi.fn(() => true),
openhumanUpdateScreenIntelligenceSettings: vi.fn(),
};
});
const baseState: ScreenIntelligenceState = {
status: {
platform_supported: true,
core_process: { pid: 4242, started_at_ms: 1712700000000 },
permissions: {
screen_recording: 'granted',
accessibility: 'granted',
input_monitoring: 'unknown',
},
features: { screen_monitoring: true },
session: {
active: false,
started_at_ms: null,
expires_at_ms: null,
remaining_ms: null,
ttl_secs: 300,
panic_hotkey: 'Cmd+Shift+.',
stop_reason: null,
frames_in_memory: 0,
last_capture_at_ms: null,
last_context: null,
vision_enabled: true,
vision_state: 'idle',
vision_queue_depth: 0,
last_vision_at_ms: null,
last_vision_summary: null,
},
config: {
enabled: false,
capture_policy: 'hybrid',
policy_mode: 'all_except_blacklist',
baseline_fps: 1,
vision_enabled: true,
session_ttl_secs: 300,
panic_stop_hotkey: 'Cmd+Shift+.',
autocomplete_enabled: true,
use_vision_model: true,
keep_screenshots: false,
allowlist: ['Code'],
denylist: ['1Password'],
},
denylist: ['1Password'],
is_context_blocked: false,
permission_check_process_path: '/tmp/openhuman-core',
},
lastRestartSummary: null,
recentVisionSummaries: [],
captureTestResult: null,
isCaptureTestRunning: false,
isLoading: false,
isRequestingPermissions: false,
isRestartingCore: false,
isStartingSession: false,
isStoppingSession: false,
isLoadingVision: false,
isFlushingVision: false,
lastError: null,
refreshStatus: vi.fn().mockResolvedValue(null),
requestPermission: vi.fn().mockResolvedValue(null),
refreshPermissionsWithRestart: vi.fn().mockResolvedValue(null),
startSession: vi.fn().mockResolvedValue(null),
stopSession: vi.fn().mockResolvedValue(null),
refreshVision: vi.fn().mockResolvedValue([]),
flushVision: vi.fn().mockResolvedValue(undefined),
runCaptureTest: vi.fn().mockResolvedValue(undefined),
clearError: vi.fn(),
};
function renderPanel(state: ScreenIntelligenceState = baseState) {
vi.mocked(useScreenIntelligenceState).mockReturnValue(state);
render(
<MemoryRouter initialEntries={['/settings/screen-intelligence']}>
<ScreenIntelligencePanel />
</MemoryRouter>
);
}
function createDeferred<T>() {
let resolve!: (value: T) => void;
const promise = new Promise<T>(res => {
resolve = res;
});
return { promise, resolve };
}
describe('ScreenIntelligencePanel', () => {
beforeEach(() => {
vi.clearAllMocks();
vi.mocked(isTauri).mockReturnValue(true);
});
it('saves screen awareness settings and refreshes core-backed status', async () => {
const deferred = createDeferred<{ result: ConfigSnapshot; logs: [] }>();
vi.mocked(openhumanUpdateScreenIntelligenceSettings).mockReturnValueOnce(deferred.promise);
renderPanel();
// The SettingsHeader renders "Screen Awareness" as the panel heading; wait for it.
await waitFor(() => {
expect(screen.getAllByText('Screen Awareness').length).toBeGreaterThan(0);
});
const enabledSwitch = screen.getByRole('switch', { name: 'Enabled' });
expect(enabledSwitch).toHaveAttribute('aria-checked', 'false');
fireEvent.click(enabledSwitch);
fireEvent.click(screen.getByRole('button', { name: 'Save Settings' }));
expect(await screen.findByRole('button', { name: 'Saving…' })).toBeInTheDocument();
expect(openhumanUpdateScreenIntelligenceSettings).toHaveBeenCalledWith(
expect.objectContaining({ enabled: true, policy_mode: 'all_except_blacklist' })
);
deferred.resolve({
result: { config: {}, workspace_dir: '/tmp/workspace', config_path: '/tmp/config.toml' },
logs: [],
});
await waitFor(() => {
expect(screen.getByRole('button', { name: 'Save Settings' })).toBeInTheDocument();
});
expect(baseState.refreshStatus).toHaveBeenCalledTimes(1);
});
it('hides permissions section and shows unsupported-platform messaging', async () => {
renderPanel({
...baseState,
status: {
...baseState.status!,
platform_supported: false,
permissions: {
screen_recording: 'denied',
accessibility: 'denied',
input_monitoring: 'unknown',
},
},
});
await waitFor(() => {
expect(
screen.getByText(
'Screen Awareness desktop capture and permission controls are currently supported on macOS only.'
)
).toBeInTheDocument();
});
expect(screen.queryByText('Permissions')).not.toBeInTheDocument();
expect(screen.queryByText(/After granting in System Settings, click/i)).not.toBeInTheDocument();
expect(
screen.queryByRole('button', { name: 'Restart & Refresh Permissions' })
).not.toBeInTheDocument();
});
it('shows the last successful restart summary', async () => {
renderPanel({
...baseState,
lastRestartSummary: 'Core restarted: PID 4000 at 9:00:00 AM -> PID 4242 at 9:01:00 AM.',
});
expect(await screen.findByText(/Core restarted: PID 4000/i)).toBeInTheDocument();
});
// ─── Session controls ─────────────────────────────────────────────────────
it('Start Session button calls startSession with consent=true', async () => {
const startSession = vi.fn().mockResolvedValue(null);
renderPanel({
...baseState,
status: {
...baseState.status!,
// accessibility granted so Start is not disabled
permissions: { ...baseState.status!.permissions, accessibility: 'granted' },
session: { ...baseState.status!.session, active: false },
},
startSession,
});
await waitFor(() => expect(screen.getAllByText(/start session/i).length).toBeGreaterThan(0));
fireEvent.click(screen.getByRole('button', { name: /start session/i }));
await waitFor(() =>
expect(startSession).toHaveBeenCalledWith(expect.objectContaining({ consent: true }))
);
});
it('Stop Session button calls stopSession', async () => {
const stopSession = vi.fn().mockResolvedValue(null);
renderPanel({
...baseState,
status: { ...baseState.status!, session: { ...baseState.status!.session, active: true } },
stopSession,
});
await waitFor(() =>
expect(screen.getByRole('button', { name: /stop session/i })).not.toBeDisabled()
);
fireEvent.click(screen.getByRole('button', { name: /stop session/i }));
await waitFor(() => expect(stopSession).toHaveBeenCalledWith('manual_stop'));
});
it('Analyze Now button calls flushVision when session is active', async () => {
const flushVision = vi.fn().mockResolvedValue(undefined);
renderPanel({
...baseState,
status: { ...baseState.status!, session: { ...baseState.status!.session, active: true } },
flushVision,
});
await waitFor(() =>
expect(screen.getByRole('button', { name: /analyze now/i })).not.toBeDisabled()
);
fireEvent.click(screen.getByRole('button', { name: /analyze now/i }));
await waitFor(() => expect(flushVision).toHaveBeenCalled());
});
it('Analyze Now is disabled when session is not active', async () => {
renderPanel({
...baseState,
status: { ...baseState.status!, session: { ...baseState.status!.session, active: false } },
});
await waitFor(() =>
expect(screen.getByRole('button', { name: /analyze now/i })).toBeDisabled()
);
});
// ─── Policy mode select ───────────────────────────────────────────────────
it('changing policy mode select updates the local state', async () => {
renderPanel();
await waitFor(() => expect(screen.getAllByText(/screen awareness/i).length).toBeGreaterThan(0));
const policySelect = screen.getByRole('combobox', { name: /mode/i }) as HTMLSelectElement;
fireEvent.change(policySelect, { target: { value: 'whitelist_only' } });
expect(policySelect.value).toBe('whitelist_only');
});
it('saves with whitelist_only policy when mode is changed', async () => {
const deferred = createDeferred<{ result: ConfigSnapshot; logs: [] }>();
vi.mocked(openhumanUpdateScreenIntelligenceSettings).mockReturnValueOnce(deferred.promise);
renderPanel();
await waitFor(() => expect(screen.getAllByText(/screen awareness/i).length).toBeGreaterThan(0));
const policySelect = screen.getByRole('combobox', { name: /mode/i });
fireEvent.change(policySelect, { target: { value: 'whitelist_only' } });
fireEvent.click(screen.getByRole('button', { name: 'Save Settings' }));
expect(vi.mocked(openhumanUpdateScreenIntelligenceSettings)).toHaveBeenCalledWith(
expect.objectContaining({ policy_mode: 'whitelist_only' })
);
deferred.resolve({
result: { config: {}, workspace_dir: '/tmp/workspace', config_path: '/tmp/config.toml' },
logs: [],
});
});
// ─── Error display ────────────────────────────────────────────────────────
it('shows lastError when state has an error', async () => {
renderPanel({ ...baseState, lastError: 'Permission denied by OS' });
expect(await screen.findByText('Permission denied by OS')).toBeInTheDocument();
});
// ─── Screen monitoring toggle ─────────────────────────────────────────────
it('toggling screen monitoring checkbox updates the override', async () => {
renderPanel();
await waitFor(() => expect(screen.getAllByText(/screen awareness/i).length).toBeGreaterThan(0));
const monitoringSwitch = screen.getByRole('switch', { name: 'Screen Monitoring' });
// Initially reflects status value (true in baseState.features)
expect(monitoringSwitch).toHaveAttribute('aria-checked', 'true');
fireEvent.click(monitoringSwitch);
expect(monitoringSwitch).toHaveAttribute('aria-checked', 'false');
});
// ─── Session status display ───────────────────────────────────────────────
it('shows Active session status when session is active', async () => {
renderPanel({
...baseState,
status: { ...baseState.status!, session: { ...baseState.status!.session, active: true } },
});
await waitFor(() => expect(screen.getByText(/active/i)).toBeInTheDocument());
});
it('shows Stopped session status when session is inactive', async () => {
renderPanel({
...baseState,
status: { ...baseState.status!, session: { ...baseState.status!.session, active: false } },
});
await waitFor(() => expect(screen.getByText(/stopped/i)).toBeInTheDocument());
});
});
@@ -1,144 +0,0 @@
import { useT } from '../../../../lib/i18n/I18nContext';
import type { AccessibilityPermissionKind } from '../../../../utils/tauriCommands';
import Button from '../../../ui/Button';
import {
SettingsBadge,
type SettingsBadgeVariant,
SettingsRow,
SettingsSection,
} from '../../controls';
const badgeVariant = (value: string): SettingsBadgeVariant =>
value === 'granted' ? 'success' : value === 'denied' ? 'danger' : 'neutral';
interface PermissionsSectionProps {
screenRecording: string;
accessibility: string;
inputMonitoring: string;
anyPermissionDenied: boolean;
lastRestartSummary: string | null;
permissionCheckProcessPath: string | null | undefined;
isRequestingPermissions: boolean;
isRestartingCore: boolean;
isLoading: boolean;
requestPermission: (permission: AccessibilityPermissionKind) => Promise<unknown>;
refreshPermissionsWithRestart: () => Promise<unknown>;
refreshStatus: () => Promise<unknown>;
}
const PermissionsSection = ({
screenRecording,
accessibility,
inputMonitoring,
anyPermissionDenied,
lastRestartSummary,
permissionCheckProcessPath,
isRequestingPermissions,
isRestartingCore,
isLoading,
requestPermission,
refreshPermissionsWithRestart,
refreshStatus,
}: PermissionsSectionProps) => {
const { t } = useT();
return (
<SettingsSection title={t('settings.screenIntel.permissions.title')}>
<SettingsRow
label={t('settings.screenIntel.permissions.screenRecording')}
control={
<SettingsBadge variant={badgeVariant(screenRecording)}>{screenRecording}</SettingsBadge>
}
/>
<SettingsRow
label={t('settings.screenIntel.permissions.accessibility')}
control={
<SettingsBadge variant={badgeVariant(accessibility)}>{accessibility}</SettingsBadge>
}
/>
<SettingsRow
label={t('settings.screenIntel.permissions.inputMonitoring')}
control={
<SettingsBadge variant={badgeVariant(inputMonitoring)}>{inputMonitoring}</SettingsBadge>
}
/>
{anyPermissionDenied && (
<div className="px-4 py-3">
<div className="space-y-1 rounded-xl border border-amber-300 dark:border-amber-500/40 bg-amber-50 dark:bg-amber-500/10 p-3 text-sm text-amber-700 dark:text-amber-300">
<p>{t('settings.screenIntel.permissions.grantHint')}</p>
{permissionCheckProcessPath ? (
<p className="text-xs opacity-75">
{t('settings.screenIntel.permissions.macosAppliesPrivacy')}{' '}
<span className="break-all font-mono text-content-secondary">
{permissionCheckProcessPath}
</span>
</p>
) : null}
</div>
</div>
)}
{lastRestartSummary ? (
<div className="px-4 py-3">
<div className="rounded-xl border border-green-300 dark:border-green-500/40 bg-green-50 dark:bg-green-500/10 p-3 text-sm text-green-700 dark:text-green-300">
{lastRestartSummary}
</div>
</div>
) : null}
<div className="flex flex-wrap gap-2 px-4 py-3">
<Button
variant="secondary"
size="sm"
onClick={() => void requestPermission('screen_recording')}
disabled={isRequestingPermissions || isRestartingCore}>
{isRequestingPermissions
? t('settings.screenIntel.permissions.requesting')
: t('settings.screenIntel.permissions.requestScreenRecording')}
</Button>
<Button
variant="secondary"
size="sm"
onClick={() => void requestPermission('accessibility')}
disabled={isRequestingPermissions || isRestartingCore}>
{isRequestingPermissions
? t('settings.screenIntel.permissions.requesting')
: t('settings.screenIntel.permissions.requestAccessibility')}
</Button>
<Button
variant="secondary"
size="sm"
onClick={() => void requestPermission('input_monitoring')}
disabled={isRequestingPermissions || isRestartingCore}>
{isRequestingPermissions
? t('settings.screenIntel.permissions.requesting')
: t('settings.screenIntel.permissions.openInputMonitoring')}
</Button>
{anyPermissionDenied ? (
<Button
variant="secondary"
tone="danger"
size="sm"
onClick={() => void refreshPermissionsWithRestart()}
disabled={isRestartingCore || isLoading}>
{isRestartingCore
? t('settings.screenIntel.permissions.restartingCore')
: t('settings.screenIntel.permissions.restartRefresh')}
</Button>
) : (
<Button
variant="secondary"
size="sm"
onClick={() => void refreshStatus()}
disabled={isLoading || isRestartingCore}>
{isLoading
? t('settings.screenIntel.permissions.refreshing')
: t('settings.screenIntel.permissions.refreshStatus')}
</Button>
)}
</div>
</SettingsSection>
);
};
export default PermissionsSection;
@@ -1,130 +0,0 @@
/**
* Tests for the Screen Awareness → Permissions section.
*
* PermissionsSection is a pure, props-driven presentational component (no
* hooks/RPC of its own beyond i18n). These tests exercise both the
* "permission denied" branch (denied alert + macOS process-path hint +
* Restart & Refresh button) and the "all granted" branch (plain Refresh
* Status button), plus the per-permission request buttons and the
* granted/denied/unknown badge variants.
*/
import { fireEvent, render, screen } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
import PermissionsSection from '../PermissionsSection';
type Props = React.ComponentProps<typeof PermissionsSection>;
const baseProps = (): Props => ({
screenRecording: 'granted',
accessibility: 'denied',
inputMonitoring: 'unknown',
anyPermissionDenied: true,
lastRestartSummary: null,
permissionCheckProcessPath: null,
isRequestingPermissions: false,
isRestartingCore: false,
isLoading: false,
requestPermission: vi.fn().mockResolvedValue(null),
refreshPermissionsWithRestart: vi.fn().mockResolvedValue(null),
refreshStatus: vi.fn().mockResolvedValue(null),
});
describe('PermissionsSection', () => {
it('renders denied alert with process path, restart summary, and request buttons', () => {
const props = {
...baseProps(),
anyPermissionDenied: true,
permissionCheckProcessPath: '/tmp/openhuman-core',
lastRestartSummary: 'Core restarted: PID 4000 -> PID 4242.',
};
render(<PermissionsSection {...props} />);
// Mixed badge variants render (granted/denied/unknown).
expect(screen.getByText('granted')).toBeInTheDocument();
expect(screen.getByText('denied')).toBeInTheDocument();
expect(screen.getByText('unknown')).toBeInTheDocument();
// Denied-alert block surfaces the macOS process path (line ~69).
expect(screen.getByText('/tmp/openhuman-core')).toBeInTheDocument();
// Last restart summary is shown.
expect(screen.getByText('Core restarted: PID 4000 -> PID 4242.')).toBeInTheDocument();
// The three per-permission request buttons render.
expect(screen.getByRole('button', { name: 'Request Screen Recording' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Request Accessibility' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Open Input Monitoring' })).toBeInTheDocument();
});
it('invokes the request handlers and the restart-with-refresh handler when denied', () => {
const props = { ...baseProps(), anyPermissionDenied: true };
render(<PermissionsSection {...props} />);
fireEvent.click(screen.getByRole('button', { name: 'Request Screen Recording' }));
expect(props.requestPermission).toHaveBeenCalledWith('screen_recording');
fireEvent.click(screen.getByRole('button', { name: 'Request Accessibility' }));
expect(props.requestPermission).toHaveBeenCalledWith('accessibility');
fireEvent.click(screen.getByRole('button', { name: 'Open Input Monitoring' }));
expect(props.requestPermission).toHaveBeenCalledWith('input_monitoring');
// When permissions are denied, the danger Restart & Refresh button shows.
const restartBtn = screen.getByRole('button', { name: 'Restarting core…' });
fireEvent.click(restartBtn);
expect(props.refreshPermissionsWithRestart).toHaveBeenCalledTimes(1);
expect(props.refreshStatus).not.toHaveBeenCalled();
});
it('shows the plain Refresh Status button when nothing is denied', () => {
const props = {
...baseProps(),
screenRecording: 'granted',
accessibility: 'granted',
inputMonitoring: 'granted',
anyPermissionDenied: false,
permissionCheckProcessPath: null,
};
render(<PermissionsSection {...props} />);
// The denied-alert process path is absent when nothing is denied.
expect(screen.queryByText('/tmp/openhuman-core')).not.toBeInTheDocument();
// The restart-with-refresh button is replaced by the plain refresh button.
expect(screen.queryByRole('button', { name: 'Restarting core…' })).not.toBeInTheDocument();
const refreshBtn = screen.getByRole('button', { name: 'Refreshing…' });
fireEvent.click(refreshBtn);
expect(props.refreshStatus).toHaveBeenCalledTimes(1);
expect(props.refreshPermissionsWithRestart).not.toHaveBeenCalled();
});
it('reflects in-flight labels while requesting permissions', () => {
const props = { ...baseProps(), isRequestingPermissions: true };
render(<PermissionsSection {...props} />);
// All three request buttons collapse to the "Requesting…" label.
expect(screen.getAllByRole('button', { name: 'Requesting…' })).toHaveLength(3);
});
it('shows the restarting-core label and disables the restart button while restarting', () => {
// Exercises the `isRestartingCore ? …` branch of the denied restart button.
const props = { ...baseProps(), anyPermissionDenied: true, isRestartingCore: true };
render(<PermissionsSection {...props} />);
const restartBtn = screen.getByRole('button', { name: 'Restarting core…' });
expect(restartBtn).toBeDisabled();
// While restarting, the request buttons are also disabled.
expect(screen.getByRole('button', { name: 'Request Screen Recording' })).toBeDisabled();
});
it('shows the refreshing label and disables the refresh button while loading', () => {
// Exercises the `isLoading ? …` branch of the not-denied refresh button.
const props = { ...baseProps(), anyPermissionDenied: false, isLoading: true };
render(<PermissionsSection {...props} />);
const refreshBtn = screen.getByRole('button', { name: 'Refreshing…' });
expect(refreshBtn).toBeDisabled();
});
});
@@ -0,0 +1,29 @@
import { render, screen, waitFor } from '@testing-library/react';
import { HashRouter, Outlet, Route, Routes } from 'react-router-dom';
import { describe, expect, it } from 'vitest';
import { settingsRouteElements } from './settingsRouteElements';
describe.each([
'/settings/features',
'/settings/screen-intelligence',
'/settings/screen-awareness-debug',
])('retired screen settings route %s', route => {
it('normalizes to the settings index without rendering removed content', async () => {
window.location.hash = `#${route}`;
render(
<HashRouter>
<Routes>
<Route path="/settings" element={<Outlet />}>
{settingsRouteElements()}
</Route>
</Routes>
</HashRouter>
);
await waitFor(() => expect(window.location.hash).toBe('#/settings'));
expect(screen.queryByText(/screen intelligence/i)).not.toBeInTheDocument();
expect(screen.queryByText(/screen awareness debug/i)).not.toBeInTheDocument();
});
});
@@ -30,7 +30,6 @@ import ProfileEditorPage from './panels/ProfileEditorPage';
import ProfilesPanel from './panels/ProfilesPanel';
import RecoveryPhrasePanel from './panels/RecoveryPhrasePanel';
import SandboxSettingsPanel from './panels/SandboxSettingsPanel';
import ScreenAwarenessDebugPanel from './panels/ScreenAwarenessDebugPanel';
import SecurityPanel from './panels/SecurityPanel';
import TeamInvitesPanel from './panels/TeamInvitesPanel';
import TeamManagementPanel from './panels/TeamManagementPanel';
@@ -135,12 +134,6 @@ export function settingsRouteElements(): ReactNode {
{/* The Integrations settings section was retired; the composio/OAuth grid
lives on the Connections page. */}
<Route path="integrations" element={<Navigate to="/connections" replace />} />
{/* Screen Awareness / Desktop Agent / Desktop Companion moved to the
Connections page (Desktop group). */}
<Route
path="screen-intelligence"
element={<Navigate to="/connections?tab=screen-intelligence" replace />}
/>
<Route path="tools" element={wrapSettingsPage(<ToolsPanel />)} />
<Route path="companion" element={<Navigate to="/connections?tab=companion" replace />} />
{/* Meeting settings moved to the Connections page (meetings tab). */}
@@ -180,10 +173,6 @@ export function settingsRouteElements(): ReactNode {
{/* Dev Workflow panel retired — superseded by Workflows (/flows). */}
<Route path="dev-workflow" element={<Navigate to="/flows" replace />} />
<Route path="skills-runner" element={wrapSettingsPage(<WorkflowRunnerPanel />)} />
<Route
path="screen-awareness-debug"
element={wrapSettingsPage(<ScreenAwarenessDebugPanel />)}
/>
{/* Voice Debug page retired. */}
<Route path="voice-debug" element={<SettingsRedirect to="/settings/developer-options" />} />
{/* Local Model Debug is a chip on the Connections → LLM page. */}
@@ -214,10 +203,6 @@ export function settingsRouteElements(): ReactNode {
{/* Old hub pages */}
<Route path="ai" element={<Navigate to="/connections?tab=llm" replace />} />
<Route path="agents-settings" element={<SettingsRedirect to="/settings/agents" />} />
<Route
path="features"
element={<Navigate to="/connections?tab=screen-intelligence" replace />}
/>
<Route path="crypto" element={<Navigate to="/connections?tab=wallet" replace />} />
<Route path="notifications-hub" element={<SettingsRedirect to="/settings/notifications" />} />
{/* Composio (API key + routing) moved to Connections → API keys. */}
@@ -230,8 +230,8 @@ export const SETTINGS_ROUTE_REGISTRY: SettingsRegistryEntry[] = [
// --- Connections group ---
// The Integrations settings section was retired — the composio/OAuth grid
// lives on the Connections page and the task-source/webhook triage surface is
// no longer used. Screen Awareness, Desktop Agent and Desktop Companion moved
// to the Connections page's Desktop group; their slugs redirect there.
// no longer used. Desktop Agent and Desktop Companion moved to the
// Connections page's Desktop group; their slugs redirect there.
// Notifications-hub and crypto hub pages are retired — their slugs redirect
// to /settings/notifications and /settings/wallet-balances.
@@ -450,15 +450,6 @@ export const SETTINGS_ROUTE_REGISTRY: SettingsRegistryEntry[] = [
// =========================================================================
// FEATURES section leaf panels
// =========================================================================
{
// Surfaced on the Connections page (Desktop group); route redirects there.
// Entry kept for search + deep-link compatibility.
id: 'screen-intelligence',
titleKey: 'pages.settings.features.screenAwareness',
descriptionKey: 'pages.settings.features.screenAwarenessDesc',
section: 'features',
searchKeywords: ['screen', 'awareness', 'vision', 'capture'],
},
{
id: 'tools',
titleKey: 'pages.settings.features.tools',
@@ -472,7 +463,7 @@ export const SETTINGS_ROUTE_REGISTRY: SettingsRegistryEntry[] = [
// Surfaced on the Connections page (Desktop group); route redirects there.
id: 'companion',
titleKey: 'pages.settings.features.desktopCompanion',
descriptionKey: 'pages.settings.features.desktopCompanionDesc',
descriptionKey: 'settings.assistant.desktopCompanionDesc',
section: 'features',
searchKeywords: ['desktop', 'overlay', 'companion'],
},
@@ -538,7 +529,7 @@ export const SETTINGS_ROUTE_REGISTRY: SettingsRegistryEntry[] = [
// These live ONLY under Settings → Developer & Diagnostics.
// Items removed from this list compared to the old DeveloperOptionsPanel:
// agents, autonomy, agent-access, sandbox-settings, activity-level,
// tools, companion, screen-intelligence, voice, embeddings, heartbeat,
// tools, companion, voice, embeddings, heartbeat,
// ledger-usage, cost-dashboard, task-sources, composio-routing,
// webhooks-triggers, migration, security
// (all moved to their canonical section pages).
@@ -557,14 +548,6 @@ export const SETTINGS_ROUTE_REGISTRY: SettingsRegistryEntry[] = [
// Knowledge & Memory group retired entirely — memory surfaces live on the
// Brain page (graph / goals / sources / sync / subconscious).
// voice-debug retired from the settings UI.
{
id: 'screen-awareness-debug',
titleKey: 'settings.developerMenu.screenAwareness.title',
descriptionKey: 'settings.developerMenu.screenAwareness.desc',
section: 'developer',
devOnly: true,
navGroup: 'modelsInference',
},
{
id: 'event-log',
titleKey: 'settings.developerMenu.eventLog.title',
@@ -1,332 +0,0 @@
/**
* Screen Intelligence setup/enable modal.
*
* Guides the user through permission grants, enables the feature,
* and shows a success confirmation — matching the UX of third-party
* skill setup flows (Gmail, etc.).
*/
import { useEffect, useMemo, useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import { useScreenIntelligenceState } from '../../features/screen-intelligence/useScreenIntelligenceState';
import { useT } from '../../lib/i18n/I18nContext';
import { openhumanUpdateScreenIntelligenceSettings } from '../../utils/tauriCommands';
import { settingsNavState } from '../settings/modal/settingsOverlay';
import { CheckIcon } from '../ui';
import Button from '../ui/Button';
import {
SetupNotice,
SetupSettingRow,
SetupSuccess,
SkillSetupModalShell,
} from './SkillSetupPrimitives';
// ─── Types ────────────────────────────────────────────────────────────────────
type Step = 'permissions' | 'enable' | 'success';
interface Props {
onClose: () => void;
/** Skip straight to manage mode when permissions are already granted. */
initialStep?: Step;
}
// ─── Permission badge (reusable) ──────────────────────────────────────────────
const PermissionRow = ({
label,
value,
onRequest,
isRequesting,
}: {
label: string;
value: string;
onRequest: () => void;
isRequesting: boolean;
}) => {
const { t } = useT();
const granted = value === 'granted';
const badgeColor = granted
? 'bg-sage-50 text-sage-700 border-sage-200'
: value === 'denied'
? 'bg-coral-50 text-coral-700 border-coral-200'
: 'bg-surface-subtle text-content-secondary border-line';
return (
<div className="flex items-center justify-between rounded-xl border border-line bg-surface px-3 py-2.5">
<div className="flex items-center gap-2">
{granted ? (
<CheckIcon className="w-4 h-4 text-sage-500" />
) : (
<svg
className="w-4 h-4 text-content-faint"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24">
<circle cx="12" cy="12" r="10" strokeWidth={2} />
</svg>
)}
<span className="text-sm text-content-secondary">{label}</span>
</div>
{granted ? (
<span
className={`rounded-md border px-2 py-0.5 text-[10px] uppercase tracking-wide ${badgeColor}`}>
{t('skills.setup.screenIntel.granted')}
</span>
) : (
<Button variant="secondary" size="xs" disabled={isRequesting} onClick={onRequest}>
{isRequesting
? t('skills.setup.screenIntel.opening')
: t('skills.setup.screenIntel.grant')}
</Button>
)}
</div>
);
};
// ─── Modal ────────────────────────────────────────────────────────────────────
export default function ScreenIntelligenceSetupModal({ onClose, initialStep }: Props) {
const navigate = useNavigate();
const location = useLocation();
const { t } = useT();
const {
status,
isRequestingPermissions,
isRestartingCore,
lastRestartSummary,
lastError,
requestPermission,
refreshPermissionsWithRestart,
refreshStatus,
} = useScreenIntelligenceState({ loadVision: false });
const [isEnabling, setIsEnabling] = useState(false);
const [enableError, setEnableError] = useState<string | null>(null);
const allGranted = useMemo(() => {
if (!status) return false;
return (
status.permissions.screen_recording === 'granted' &&
status.permissions.accessibility === 'granted' &&
status.permissions.input_monitoring === 'granted'
);
}, [status]);
const anyDenied = useMemo(() => {
if (!status) return false;
return (
status.permissions.screen_recording === 'denied' ||
status.permissions.accessibility === 'denied' ||
status.permissions.input_monitoring === 'denied'
);
}, [status]);
// Derive current step
const [step, setStep] = useState<Step>(initialStep ?? 'permissions');
// Auto-advance: when permissions are all granted, move past the permissions step
useEffect(() => {
if (step === 'permissions' && allGranted) {
setStep('enable');
}
}, [step, allGranted]);
const handleEnable = async () => {
setIsEnabling(true);
setEnableError(null);
try {
await openhumanUpdateScreenIntelligenceSettings({ enabled: true });
await refreshStatus();
setStep('success');
} catch (error) {
setEnableError(
error instanceof Error ? error.message : t('skills.setup.screenIntel.enableError')
);
} finally {
setIsEnabling(false);
}
};
const handleGoToSettings = () => {
onClose();
navigate('/settings/screen-intelligence', settingsNavState(location));
};
if (status?.platform_supported === false) {
return (
<SkillSetupModalShell
onClose={onClose}
title={t('skills.setup.screenIntel.title')}
titleId="si-setup-title"
icon={
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={1.8}
d="M3 5h18v12H3zM8 21h8m-4-4v4"
/>
</svg>
}>
<div className="space-y-4 py-2">
<p className="text-sm text-content-secondary leading-relaxed">
{t('skills.setup.screenIntel.macosOnly')}
</p>
<Button variant="secondary" size="lg" onClick={onClose} className="w-full">
{t('common.close')}
</Button>
</div>
</SkillSetupModalShell>
);
}
return (
<SkillSetupModalShell
onClose={onClose}
title={t('skills.setup.screenIntel.title')}
titleId="si-setup-title"
subtitle={
<>
{step === 'permissions' && t('skills.setup.screenIntel.stepPermissions')}
{step === 'enable' && t('skills.setup.screenIntel.stepEnable')}
{step === 'success' && t('skills.setup.screenIntel.stepSuccess')}
</>
}
icon={
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={1.8}
d="M3 5h18v12H3zM8 21h8m-4-4v4"
/>
</svg>
}>
{/* ─── Step 1: Permissions ─── */}
{step === 'permissions' && (
<div className="space-y-3">
<p className="text-xs text-content-muted leading-relaxed">
{t('skills.setup.screenIntel.permissionsDesc')}
</p>
<div className="space-y-2">
<PermissionRow
label={t('skills.setup.screenIntel.permScreenRecording')}
value={status?.permissions.screen_recording ?? 'unknown'}
onRequest={() => void requestPermission('screen_recording')}
isRequesting={isRequestingPermissions}
/>
<PermissionRow
label={t('skills.setup.screenIntel.permAccessibility')}
value={status?.permissions.accessibility ?? 'unknown'}
onRequest={() => void requestPermission('accessibility')}
isRequesting={isRequestingPermissions}
/>
<PermissionRow
label={t('skills.setup.screenIntel.permInputMonitoring')}
value={status?.permissions.input_monitoring ?? 'unknown'}
onRequest={() => void requestPermission('input_monitoring')}
isRequesting={isRequestingPermissions}
/>
</div>
{anyDenied && (
<SetupNotice tone="amber" className="leading-relaxed">
<p>{t('skills.setup.screenIntel.deniedHint')}</p>
{status?.permission_check_process_path && (
<p className="mt-1 opacity-75 text-[10px]">
{t('skills.setup.screenIntel.permissionPathLabel')}{' '}
<span className="font-mono break-all text-content-secondary">
{status.permission_check_process_path}
</span>
</p>
)}
</SetupNotice>
)}
{lastRestartSummary && <SetupNotice tone="sage">{lastRestartSummary}</SetupNotice>}
{lastError && <SetupNotice tone="coral">{lastError}</SetupNotice>}
<div className="flex items-center gap-2 pt-1">
{anyDenied ? (
<button
type="button"
onClick={() => void refreshPermissionsWithRestart()}
disabled={isRestartingCore}
className="flex-1 rounded-xl border border-amber-300 bg-amber-50 px-3 py-2.5 text-sm font-medium text-amber-700 hover:bg-amber-100 disabled:opacity-50 transition-colors">
{isRestartingCore
? t('skills.setup.screenIntel.restarting')
: t('skills.setup.screenIntel.restartRefresh')}
</button>
) : (
<Button
variant="secondary"
size="lg"
onClick={() => void refreshStatus()}
disabled={isRestartingCore}
className="flex-1">
{t('skills.setup.screenIntel.refreshStatus')}
</Button>
)}
</div>
</div>
)}
{/* ─── Step 2: Enable ─── */}
{step === 'enable' && (
<div className="space-y-4">
<SetupNotice tone="sage" icon={<CheckIcon className="w-4 h-4 text-sage-500" />}>
{t('skills.setup.screenIntel.allGranted')}
</SetupNotice>
<p className="text-xs text-content-muted leading-relaxed">
{t('skills.setup.screenIntel.enableDesc')}
</p>
<div className="space-y-2">
<SetupSettingRow
label={t('skills.setup.screenIntel.captureMode')}
value={t('skills.setup.screenIntel.captureModeValue')}
/>
<SetupSettingRow
label={t('skills.setup.screenIntel.visionModel')}
value={t('common.enabled')}
/>
<SetupSettingRow
label={t('skills.setup.screenIntel.panicHotkey')}
value={status?.session.panic_hotkey ?? 'Cmd+Shift+.'}
mono
/>
</div>
{enableError && <SetupNotice tone="coral">{enableError}</SetupNotice>}
<Button
variant="primary"
size="lg"
onClick={() => void handleEnable()}
disabled={isEnabling}
className="w-full">
{isEnabling
? t('skills.setup.screenIntel.enabling')
: t('skills.setup.screenIntel.enableBtn')}
</Button>
</div>
)}
{/* ─── Step 3: Success ─── */}
{step === 'success' && (
<SetupSuccess
title={t('skills.setup.screenIntel.activeTitle')}
description={t('skills.setup.screenIntel.activeDesc')}
settingsLabel={t('skills.setup.screenIntel.advancedSettings')}
finishLabel={t('common.finish')}
onSettings={handleGoToSettings}
onFinish={onClose}
/>
)}
</SkillSetupModalShell>
);
}
@@ -1,147 +0,0 @@
import { render, screen } from '@testing-library/react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import {
type ScreenIntelligenceState,
useScreenIntelligenceState,
} from '../../../features/screen-intelligence/useScreenIntelligenceState';
import ScreenIntelligenceSetupModal from '../ScreenIntelligenceSetupModal';
vi.mock('../../../features/screen-intelligence/useScreenIntelligenceState', () => ({
useScreenIntelligenceState: vi.fn(),
}));
vi.mock('react-router-dom', async () => {
const actual = await vi.importActual<Record<string, unknown>>('react-router-dom');
return {
...actual,
useNavigate: vi.fn(() => vi.fn()),
useLocation: () => ({
pathname: '/connections',
search: '',
hash: '',
state: null,
key: 'test',
}),
};
});
vi.mock('../../../utils/tauriCommands', async () => {
const actual = await vi.importActual<Record<string, unknown>>('../../../utils/tauriCommands');
return {
...actual,
openhumanUpdateScreenIntelligenceSettings: vi.fn().mockResolvedValue(undefined),
};
});
const baseState: ScreenIntelligenceState = {
status: {
platform_supported: true,
permissions: {
screen_recording: 'unknown',
accessibility: 'unknown',
input_monitoring: 'unknown',
},
features: { screen_monitoring: false },
session: {
active: false,
started_at_ms: null,
expires_at_ms: null,
remaining_ms: null,
ttl_secs: 300,
panic_hotkey: 'Cmd+Shift+.',
stop_reason: null,
frames_in_memory: 0,
last_capture_at_ms: null,
last_context: null,
vision_enabled: true,
vision_state: 'idle',
vision_queue_depth: 0,
last_vision_at_ms: null,
last_vision_summary: null,
},
config: {
enabled: false,
capture_policy: 'hybrid',
policy_mode: 'all_except_blacklist',
baseline_fps: 1,
vision_enabled: true,
session_ttl_secs: 300,
panic_stop_hotkey: 'Cmd+Shift+.',
autocomplete_enabled: true,
use_vision_model: true,
keep_screenshots: false,
allowlist: [],
denylist: [],
},
denylist: [],
is_context_blocked: false,
},
lastRestartSummary: null,
recentVisionSummaries: [],
captureTestResult: null,
isCaptureTestRunning: false,
isLoading: false,
isRequestingPermissions: false,
isRestartingCore: false,
isStartingSession: false,
isStoppingSession: false,
isLoadingVision: false,
isFlushingVision: false,
lastError: null,
refreshStatus: vi.fn().mockResolvedValue(null),
requestPermission: vi.fn().mockResolvedValue(null),
refreshPermissionsWithRestart: vi.fn().mockResolvedValue(null),
startSession: vi.fn().mockResolvedValue(null),
stopSession: vi.fn().mockResolvedValue(null),
refreshVision: vi.fn().mockResolvedValue([]),
flushVision: vi.fn().mockResolvedValue(undefined),
runCaptureTest: vi.fn().mockResolvedValue(undefined),
clearError: vi.fn(),
};
describe('ScreenIntelligenceSetupModal', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('shows macOS-only message and a Close button when platform_supported is false', () => {
vi.mocked(useScreenIntelligenceState).mockReturnValue({
...baseState,
status: { ...baseState.status!, platform_supported: false },
});
render(<ScreenIntelligenceSetupModal onClose={vi.fn()} />);
expect(screen.getByText(/macOS only/i)).toBeInTheDocument();
expect(screen.getByText('Close', { selector: 'button' })).toBeInTheDocument();
expect(screen.queryByText('Grant permissions')).not.toBeInTheDocument();
expect(screen.queryByText('Screen Recording')).not.toBeInTheDocument();
});
it('calls onClose when the Close button is clicked on the unsupported-platform screen', () => {
const onClose = vi.fn();
vi.mocked(useScreenIntelligenceState).mockReturnValue({
...baseState,
status: { ...baseState.status!, platform_supported: false },
});
render(<ScreenIntelligenceSetupModal onClose={onClose} />);
screen.getByText('Close', { selector: 'button' }).click();
expect(onClose).toHaveBeenCalledTimes(1);
});
it('renders the permissions setup flow when platform_supported is true', () => {
vi.mocked(useScreenIntelligenceState).mockReturnValue(baseState);
render(<ScreenIntelligenceSetupModal onClose={vi.fn()} />);
expect(screen.getByText('Grant permissions')).toBeInTheDocument();
expect(screen.getByText('Screen Recording')).toBeInTheDocument();
expect(screen.getByText('Accessibility')).toBeInTheDocument();
expect(screen.getByText('Input Monitoring')).toBeInTheDocument();
expect(screen.queryByText(/macOS only/i)).not.toBeInTheDocument();
});
});
-2
View File
@@ -8,7 +8,6 @@ import {
LuKeyboard,
LuMessageSquareMore,
LuMic,
LuMonitor,
LuPlugZap,
LuShare2,
LuSparkles,
@@ -180,7 +179,6 @@ export function skillCategoryHeadingClassName(category: SkillCategory): string {
}
export const BUILT_IN_SKILL_ICONS = {
screenIntelligence: <LuMonitor className="h-5 w-5" aria-hidden="true" />,
textAutocomplete: <LuKeyboard className="h-5 w-5" aria-hidden="true" />,
voiceStt: <LuMic className="h-5 w-5" aria-hidden="true" />,
};
-12
View File
@@ -26,7 +26,6 @@ import { visemePath, VISEMES, type VisemeShape } from './visemes';
* - `celebrating` — success animation after meaningful work.
* - `writing` — agent is editing/creating files.
* - `reading` — agent is browsing or reading content.
* - `recording` — agent is capturing screen or recording.
* - `waving` — greeting or hello gesture.
* - `dancing` — celebratory/playful animation.
* - `drinking_coffee` — agent is processing / long-running task.
@@ -50,7 +49,6 @@ export type MascotFace =
| 'celebrating'
| 'writing'
| 'reading'
| 'recording'
| 'waving'
| 'dancing'
| 'drinking_coffee'
@@ -216,14 +214,6 @@ const FACE_PRESETS: Record<Exclude<MascotFace, 'normal'>, FacePreset> = {
showBrows: true,
blushOpacity: 0.75,
},
recording: {
eyeScaleY: 1,
eyeScaleX: 1,
browTilt: 0,
browDy: 0,
showBrows: false,
blushOpacity: 0.9,
},
waving: {
eyeScaleY: 0.5,
eyeScaleX: 1.1,
@@ -489,8 +479,6 @@ function restMouthPath(face: MascotFace): string {
case 'writing':
case 'reading':
return 'M488,585 Q520,595 552,585 Q520,592 488,585 Z';
case 'recording':
return 'M495,580 Q520,600 545,580 Q520,615 495,580 Z';
case 'drinking_coffee':
case 'drinking_boba':
return 'M500,578 Q520,598 540,578 Q520,610 500,578 Z';
+2 -4
View File
@@ -28,7 +28,6 @@ export const RIVE_POSES = [
'coffeedrink',
'writing',
'bobbateadrink',
'recording',
'hand_wave',
'dancing',
] as const;
@@ -53,7 +52,6 @@ export const FACE_TO_POSE: Record<MascotFace, RivePose> = {
celebrating: 'celebration',
writing: 'writing',
reading: 'bookreading',
recording: 'recording',
waving: 'hand_wave',
dancing: 'dancing',
drinking_coffee: 'coffeedrink',
@@ -159,8 +157,8 @@ export function toRiveVisemeCode(code: string): RiveVisemeCode {
/**
* Poses the mascot drifts through on its own while otherwise idle, to keep it
* feeling alive. Deliberately excludes `idle` (the resting state it returns to
* between picks) and `recording` (reads as an active screen-capture cue).
* feeling alive. Deliberately excludes `idle`, the resting state it returns to
* between picks.
*/
export const AMBIENT_POSES: readonly RivePose[] = [
'thinking',
@@ -336,6 +336,17 @@ describe('useHumanMascot state machine', () => {
expect(result.current.face).toBe('thinking');
});
it('does not expose a capture-specific activity face', () => {
const { result } = renderHook(() => useHumanMascot());
act(() => {
capturedListeners?.onInferenceStart?.(fakeEvent({}));
capturedListeners?.onToolCall?.(
fakeEvent({ tool_name: 'memory_capture', skill_id: 's', args: {}, round: 1 })
);
});
expect(result.current.face).toBe('thinking');
});
it('maps reading tools to reading face', () => {
const { result } = renderHook(() => useHumanMascot());
act(() => {
+1 -5
View File
@@ -254,10 +254,6 @@ function toolToActivityFace(toolName: string): MascotFace | null {
return 'reading';
}
if (name.includes('screen') || name.includes('screenshot') || name.includes('capture')) {
return 'recording';
}
return null;
}
@@ -284,7 +280,7 @@ interface UseHumanMascotResult {
*
* - `inference_start` → `thinking`
* - `iteration_start` round > 1 or `tool_call` → activity pose based on tool
* name (writing/reading/recording) or `confused` as fallback
* name (writing/reading) or `confused` as fallback
* - `tool_result success=false` → `concerned` (held briefly)
* - `text_delta` → `speaking`, pseudo-lipsync from the trailing letter
* - `chat_done` (no TTS) → message-aware ack face (held briefly), then `idle`
-156
View File
@@ -1,156 +0,0 @@
import {
type AccessibilityPermissionKind,
type AccessibilityStartSessionParams,
type AccessibilityStatus,
openhumanAccessibilityRequestPermission,
openhumanAccessibilityStartSession,
openhumanAccessibilityStatus,
openhumanAccessibilityStopSession,
openhumanAccessibilityVisionFlush,
openhumanAccessibilityVisionRecent,
openhumanScreenIntelligenceCaptureTest,
openhumanServiceRestart,
} from '../../utils/tauriCommands';
const ACCESSIBILITY_ERROR_PREFIX = '[screen-intelligence]';
const extractError = (error: unknown, fallback: string): string => {
if (error instanceof Error && error.message.trim()) {
return error.message;
}
if (typeof error === 'string' && error.trim()) {
return error;
}
if (error && typeof error === 'object') {
const msg = (error as { message?: unknown }).message;
if (typeof msg === 'string' && msg.trim()) {
return msg;
}
}
return fallback;
};
const formatCoreIdentity = (status: AccessibilityStatus | null | undefined): string | null => {
const process = status?.core_process;
if (!process) {
return null;
}
const startedAt = Number.isFinite(process.started_at_ms)
? new Date(process.started_at_ms).toLocaleTimeString()
: null;
return startedAt ? `PID ${process.pid} at ${startedAt}` : `PID ${process.pid}`;
};
interface RefreshPermissionsResult {
status: AccessibilityStatus;
restartSummary: string;
}
async function fetchScreenIntelligenceStatus(): Promise<AccessibilityStatus> {
const response = await openhumanAccessibilityStatus();
return response.result;
}
export async function requestScreenIntelligencePermission(
permission: AccessibilityPermissionKind
): Promise<AccessibilityStatus> {
await openhumanAccessibilityRequestPermission(permission);
return await fetchScreenIntelligenceStatus();
}
export async function refreshScreenIntelligencePermissionsWithRestart(
previousStatus: AccessibilityStatus | null
): Promise<RefreshPermissionsResult> {
try {
const previousProcess = previousStatus?.core_process;
console.debug(
`${ACCESSIBILITY_ERROR_PREFIX} refreshPermissionsWithRestart: requesting core self-restart`
);
await openhumanServiceRestart('screen-intelligence-ui', 'refresh_permissions');
console.debug(
`${ACCESSIBILITY_ERROR_PREFIX} refreshPermissionsWithRestart: waiting for sidecar ready`
);
await new Promise<void>(resolve => setTimeout(resolve, 400));
console.debug(
`${ACCESSIBILITY_ERROR_PREFIX} refreshPermissionsWithRestart: fetching updated status`
);
for (let attempt = 1; attempt <= 5; attempt += 1) {
try {
const status = await fetchScreenIntelligenceStatus();
console.debug(
`${ACCESSIBILITY_ERROR_PREFIX} refreshPermissionsWithRestart: done screen_recording=%s accessibility=%s input_monitoring=%s`,
status.permissions.screen_recording,
status.permissions.accessibility,
status.permissions.input_monitoring
);
const currentProcess = status.core_process;
if (
previousProcess &&
currentProcess &&
previousProcess.pid === currentProcess.pid &&
previousProcess.started_at_ms === currentProcess.started_at_ms
) {
throw new Error(
`Core restart command completed, but the same core instance is still serving requests (${formatCoreIdentity(status)}).`
);
}
const previousLabel = formatCoreIdentity(previousStatus);
const currentLabel = formatCoreIdentity(status);
const restartSummary =
previousLabel && currentLabel
? `Core restarted: ${previousLabel} -> ${currentLabel}.`
: currentLabel
? `Core restarted. Now serving from ${currentLabel}.`
: 'Core restarted and permissions refreshed.';
return { status, restartSummary };
} catch (error) {
if (attempt === 5) {
throw error;
}
console.debug(
`${ACCESSIBILITY_ERROR_PREFIX} refreshPermissionsWithRestart: status fetch failed (attempt %s), retrying`,
attempt
);
await new Promise<void>(resolve => setTimeout(resolve, 350 * attempt));
}
}
throw new Error('Failed to fetch accessibility status after core restart');
} catch (error) {
const message = extractError(error, 'Failed to restart core and refresh permissions');
console.error(`${ACCESSIBILITY_ERROR_PREFIX} refreshPermissionsWithRestart: error`, message);
throw new Error(message);
}
}
export async function startScreenIntelligenceSession(
params: AccessibilityStartSessionParams
): Promise<AccessibilityStatus> {
await openhumanAccessibilityStartSession(params);
return await fetchScreenIntelligenceStatus();
}
export async function stopScreenIntelligenceSession(reason?: string): Promise<AccessibilityStatus> {
await openhumanAccessibilityStopSession(reason ? { reason } : undefined);
return await fetchScreenIntelligenceStatus();
}
export async function fetchScreenIntelligenceVisionRecent(limit?: number) {
const response = await openhumanAccessibilityVisionRecent(limit);
return response.result.summaries;
}
export async function flushScreenIntelligenceVision() {
const response = await openhumanAccessibilityVisionFlush();
return response.result.summary;
}
export async function runScreenIntelligenceCaptureTest() {
const response = await openhumanScreenIntelligenceCaptureTest();
return response.result;
}
export { extractError };
@@ -1,66 +0,0 @@
/**
* Derives a skill-card-friendly status for Screen Intelligence,
* matching the state vocabulary used by third-party skills (Gmail, etc.).
*/
import { useMemo } from 'react';
import { useCoreState } from '../../providers/CoreStateProvider';
import {
activeStatus,
enabledStatus,
offlineStatus,
setupRequiredStatus,
type SkillCardStatusDescriptor,
unsupportedStatus,
} from '../skills/skillCardStatus';
interface ScreenIntelligenceSkillStatus extends SkillCardStatusDescriptor {
/** True when all three macOS permissions are granted. */
allPermissionsGranted: boolean;
/** True when the platform doesn't support screen intelligence. */
platformUnsupported: boolean;
}
export function useScreenIntelligenceSkillStatus(): ScreenIntelligenceSkillStatus {
const { snapshot } = useCoreState();
const status = snapshot.runtime.screenIntelligence;
return useMemo(() => {
// No status yet (core not ready or not in Tauri)
if (!status) {
return { ...offlineStatus(), allPermissionsGranted: false, platformUnsupported: false };
}
if (!status.platform_supported) {
return { ...unsupportedStatus(), allPermissionsGranted: false, platformUnsupported: true };
}
const { permissions, session, config } = status;
const allGranted =
permissions.screen_recording === 'granted' &&
permissions.accessibility === 'granted' &&
permissions.input_monitoring === 'granted';
// Permissions missing — needs setup
if (!allGranted) {
return { ...setupRequiredStatus(), allPermissionsGranted: false, platformUnsupported: false };
}
// Session active — fully connected
if (session.active) {
return { ...activeStatus(), allPermissionsGranted: true, platformUnsupported: false };
}
// Permissions granted, enabled in config, but session not active
if (config.enabled) {
return { ...enabledStatus(), allPermissionsGranted: true, platformUnsupported: false };
}
// Permissions granted but not enabled
return {
...offlineStatus('Disabled'),
allPermissionsGranted: true,
platformUnsupported: false,
};
}, [status]);
}
@@ -1,255 +0,0 @@
import { useCallback, useEffect, useState } from 'react';
import { getCoreStateSnapshot } from '../../lib/coreState/store';
import { useCoreState } from '../../providers/CoreStateProvider';
import type {
AccessibilityPermissionKind,
AccessibilityStartSessionParams,
AccessibilityStatus,
AccessibilityVisionSummary,
CaptureTestResult,
} from '../../utils/tauriCommands';
import {
extractError,
fetchScreenIntelligenceVisionRecent,
flushScreenIntelligenceVision,
refreshScreenIntelligencePermissionsWithRestart,
requestScreenIntelligencePermission,
runScreenIntelligenceCaptureTest,
startScreenIntelligenceSession,
stopScreenIntelligenceSession,
} from './api';
export interface ScreenIntelligenceState {
status: AccessibilityStatus | null;
lastRestartSummary: string | null;
recentVisionSummaries: AccessibilityVisionSummary[];
captureTestResult: CaptureTestResult | null;
isCaptureTestRunning: boolean;
isLoading: boolean;
isRequestingPermissions: boolean;
isRestartingCore: boolean;
isStartingSession: boolean;
isStoppingSession: boolean;
isLoadingVision: boolean;
isFlushingVision: boolean;
lastError: string | null;
refreshStatus: () => Promise<AccessibilityStatus | null>;
requestPermission: (
permission: AccessibilityPermissionKind
) => Promise<AccessibilityStatus | null>;
refreshPermissionsWithRestart: () => Promise<AccessibilityStatus | null>;
startSession: (params: AccessibilityStartSessionParams) => Promise<AccessibilityStatus | null>;
stopSession: (reason?: string) => Promise<AccessibilityStatus | null>;
refreshVision: (limit?: number) => Promise<AccessibilityVisionSummary[]>;
flushVision: () => Promise<void>;
runCaptureTest: () => Promise<void>;
clearError: () => void;
}
interface UseScreenIntelligenceStateOptions {
pollMs?: number;
visionLimit?: number;
loadVision?: boolean;
}
export function useScreenIntelligenceState(
options: UseScreenIntelligenceStateOptions = {}
): ScreenIntelligenceState {
const { pollMs = 2000, visionLimit = 10, loadVision = false } = options;
const { refresh: refreshCoreState, snapshot } = useCoreState();
const status = snapshot.runtime.screenIntelligence;
const [lastRestartSummary, setLastRestartSummary] = useState<string | null>(null);
const [recentVisionSummaries, setRecentVisionSummaries] = useState<AccessibilityVisionSummary[]>(
[]
);
const [captureTestResult, setCaptureTestResult] = useState<CaptureTestResult | null>(null);
const [isCaptureTestRunning, setIsCaptureTestRunning] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [isRequestingPermissions, setIsRequestingPermissions] = useState(false);
const [isRestartingCore, setIsRestartingCore] = useState(false);
const [isStartingSession, setIsStartingSession] = useState(false);
const [isStoppingSession, setIsStoppingSession] = useState(false);
const [isLoadingVision, setIsLoadingVision] = useState(false);
const [isFlushingVision, setIsFlushingVision] = useState(false);
const [lastError, setLastError] = useState<string | null>(null);
const latestScreenIntelligenceStatus = useCallback(
(): AccessibilityStatus | null => getCoreStateSnapshot().snapshot.runtime.screenIntelligence,
[]
);
const refreshStatus = useCallback(async () => {
setIsLoading(true);
setLastError(null);
try {
await refreshCoreState();
return latestScreenIntelligenceStatus();
} catch (error) {
setLastError(extractError(error, 'Failed to fetch accessibility status'));
return null;
} finally {
setIsLoading(false);
}
}, [latestScreenIntelligenceStatus, refreshCoreState]);
const refreshVision = useCallback(
async (limit = visionLimit) => {
setIsLoadingVision(true);
try {
const summaries = await fetchScreenIntelligenceVisionRecent(limit);
setRecentVisionSummaries(summaries);
return summaries;
} catch (error) {
setLastError(extractError(error, 'Failed to fetch accessibility vision summaries'));
return [];
} finally {
setIsLoadingVision(false);
}
},
[visionLimit]
);
const requestPermission = useCallback(
async (permission: AccessibilityPermissionKind) => {
setIsRequestingPermissions(true);
setLastError(null);
setLastRestartSummary(null);
try {
await requestScreenIntelligencePermission(permission);
await refreshCoreState();
return latestScreenIntelligenceStatus();
} catch (error) {
setLastError(extractError(error, 'Failed to request accessibility permission'));
return null;
} finally {
setIsRequestingPermissions(false);
}
},
[latestScreenIntelligenceStatus, refreshCoreState]
);
const refreshPermissionsWithRestart = useCallback(async () => {
setIsRestartingCore(true);
setLastError(null);
setLastRestartSummary(null);
try {
const result = await refreshScreenIntelligencePermissionsWithRestart(status);
setLastRestartSummary(result.restartSummary);
await refreshCoreState();
return latestScreenIntelligenceStatus() ?? result.status;
} catch (error) {
setLastError(extractError(error, 'Failed to restart core and refresh permissions'));
return null;
} finally {
setIsRestartingCore(false);
}
}, [latestScreenIntelligenceStatus, refreshCoreState, status]);
const startSession = useCallback(
async (params: AccessibilityStartSessionParams) => {
setIsStartingSession(true);
setLastError(null);
try {
await startScreenIntelligenceSession(params);
await refreshCoreState();
return latestScreenIntelligenceStatus();
} catch (error) {
setLastError(extractError(error, 'Failed to start accessibility session'));
return null;
} finally {
setIsStartingSession(false);
}
},
[latestScreenIntelligenceStatus, refreshCoreState]
);
const stopSession = useCallback(
async (reason?: string) => {
setIsStoppingSession(true);
setLastError(null);
try {
await stopScreenIntelligenceSession(reason);
await refreshCoreState();
return latestScreenIntelligenceStatus();
} catch (error) {
setLastError(extractError(error, 'Failed to stop accessibility session'));
return null;
} finally {
setIsStoppingSession(false);
}
},
[latestScreenIntelligenceStatus, refreshCoreState]
);
const flushVision = useCallback(async () => {
setIsFlushingVision(true);
try {
const summary = await flushScreenIntelligenceVision();
if (summary) {
setRecentVisionSummaries(current => [summary, ...current].slice(0, 30));
}
} catch (error) {
setLastError(extractError(error, 'Failed to flush accessibility vision'));
} finally {
setIsFlushingVision(false);
}
}, []);
const runCaptureTest = useCallback(async () => {
setIsCaptureTestRunning(true);
setCaptureTestResult(null);
setLastError(null);
try {
const result = await runScreenIntelligenceCaptureTest();
setCaptureTestResult(result);
} catch (error) {
setLastError(extractError(error, 'Failed to run capture test'));
} finally {
setIsCaptureTestRunning(false);
}
}, []);
useEffect(() => {
if (loadVision) {
void refreshVision(visionLimit);
}
}, [loadVision, refreshVision, visionLimit]);
useEffect(() => {
if (!loadVision) {
return;
}
const intervalId = window.setInterval(() => {
void refreshVision(visionLimit);
}, pollMs);
return () => window.clearInterval(intervalId);
}, [loadVision, pollMs, refreshVision, visionLimit]);
return {
status,
lastRestartSummary,
recentVisionSummaries,
captureTestResult,
isCaptureTestRunning,
isLoading,
isRequestingPermissions,
isRestartingCore,
isStartingSession,
isStoppingSession,
isLoadingVision,
isFlushingVision,
lastError,
refreshStatus,
requestPermission,
refreshPermissionsWithRestart,
startSession,
stopSession,
refreshVision,
flushVision,
runCaptureTest,
clearError: () => setLastError(null),
};
}
@@ -18,7 +18,7 @@ function makeSnapshot(overrides: Partial<CoreAppSnapshot> = {}): CoreAppSnapshot
activeMode: 'os_keyring',
backendName: 'os',
},
runtime: { screenIntelligence: null, localAi: null, service: null },
runtime: { localAi: null, service: null },
...overrides,
};
}
+1 -3
View File
@@ -1,6 +1,5 @@
import type { User } from '../../types/api';
import type { TeamInvite, TeamMember, TeamWithRole } from '../../types/team';
import type { AccessibilityStatus } from '../../utils/tauriCommands/accessibility';
import type { LocalAiStatus } from '../../utils/tauriCommands/localAi';
import type { ServiceStatus } from '../../utils/tauriCommands/service';
@@ -32,7 +31,6 @@ export interface CoreLocalState {
}
export interface CoreRuntimeSnapshot {
screenIntelligence: AccessibilityStatus | null;
localAi: LocalAiStatus | null;
service: ServiceStatus | null;
}
@@ -94,7 +92,7 @@ const emptySnapshot: CoreAppSnapshot = {
activeMode: 'os_keyring',
backendName: 'os',
},
runtime: { screenIntelligence: null, localAi: null, service: null },
runtime: { localAi: null, service: null },
};
let currentState: CoreState = {
-136
View File
@@ -7,7 +7,6 @@ const messages: TranslationMap = {
'settings.assistant.desktopCompanion': 'المرافق لسطح المكتب',
'settings.assistant.desktopCompanionDesc': 'وضع المرافق الدائم مع اختصار شريط النظام',
'pages.settings.features.desktopCompanion': 'الرفيق المكتبي',
'pages.settings.features.desktopCompanionDesc': 'مساعد صوتي يدرك الشاشة: يستمع ويرى ويتحدث ويشير',
'settings.companion.title': 'رفيق سطح المكتب',
'settings.companion.session': 'الجلسة',
'settings.companion.activeLabel': 'نشط',
@@ -23,8 +22,6 @@ const messages: TranslationMap = {
'settings.companion.hotkey': 'مفتاح التشغيل السريع',
'settings.companion.activationMode': 'وضع التنشيط',
'settings.companion.sessionTtl': 'مدة صلاحية الجلسة',
'settings.companion.screenCapture': 'لقطة الشاشة',
'settings.companion.appContext': 'سياق التطبيق',
// Share cards (#5006).
'share.button': 'مشاركة',
'share.modalTitle': 'شارك هذه اللحظة',
@@ -109,7 +106,6 @@ const messages: TranslationMap = {
'connections.header.embeddings': 'اختر نموذج التضمين المستخدم في البحث في الذاكرة.',
'connections.header.search': 'اضبط البحث على الويب لوكيلك.',
'connections.header.wallet': 'رصيد محفظتك المحلية وعبارة الاسترداد الخاصة بك.',
'connections.header.screen': 'دع وكيلك يرى شاشتك ليقدم مساعدة سياقية في الوقت المناسب.',
'connections.header.composioKey': 'أضف مفتاح API الخاص بـ Composio لفتح مئات تكاملات التطبيقات.',
'connections.llm.apiKeys': 'مفاتيح API والتوجيه',
'connections.welcome.nav': 'مرحبًا',
@@ -741,8 +737,6 @@ const messages: TranslationMap = {
'settings.assistant.faceMascot': 'الوجه / الشخصية',
'settings.assistant.backgroundActivity': 'اللاوعي',
'settings.assistant.backgroundActivityDesc': 'تحكم في مدى نشاط مساعدك في الخلفية',
'settings.assistant.screenAwareness': 'الوعي بالشاشة',
'settings.assistant.screenAwarenessDesc': 'اسمح للمساعد برؤية النافذة النشطة',
'settings.assistant.permissions': 'الأذونات',
'settings.assistant.permissionsDesc': 'اختر ما يمكن للمساعد فعله والمكان الذي يمكنه العمل فيه',
'settings.privacySecurity.privacy': 'الخصوصية',
@@ -822,7 +816,6 @@ const messages: TranslationMap = {
'settings.notifications.tabs.preferences': 'التفضيلات',
'settings.notifications.tabs.routing': 'التوجيه',
'settings.features': 'الميزات',
'settings.featuresDesc': 'وعي الشاشة والمراسلة والأدوات',
'settings.aiModels': 'الذكاء الاصطناعي والنماذج',
'settings.aiModelsDesc': 'إعداد نموذج الذكاء الاصطناعي المحلي وتنزيلاته ومزود LLM',
'settings.ai': 'إعداد الذكاء الاصطناعي',
@@ -914,8 +907,6 @@ const messages: TranslationMap = {
'settings.notifications.doNotDisturbDesc': 'إيقاف جميع الإشعارات مؤقتًا لفترة محددة',
'settings.notifications.channelControls': 'ضوابط لكل قناة',
'settings.notifications.channelControlsDesc': 'ضبط تفضيلات الإشعارات لكل قناة',
'settings.features.screenAwareness': 'وعي الشاشة',
'settings.features.screenAwarenessDesc': 'السماح للمساعد برؤية نافذتك النشطة',
'settings.features.messaging': 'المراسلة',
'settings.features.messagingDesc': 'إعدادات تكامل القنوات والمراسلة',
'settings.features.tools': 'الأدوات',
@@ -2157,21 +2148,6 @@ const messages: TranslationMap = {
'settings.tools.saveChanges': 'حفظ التغييرات',
'settings.tools.preferencesSaved': 'تم حفظ التفضيلات',
'settings.tools.saveFailed': 'فشل حفظ التفضيلات. حاول مرة أخرى.',
'settings.screenAwareness.mode': 'الوضع',
'settings.screenAwareness.allExceptBlacklist': 'الكل باستثناء القائمة السوداء',
'settings.screenAwareness.whitelistOnly': 'القائمة البيضاء فقط',
'settings.screenAwareness.screenMonitoring': 'مراقبة الشاشة',
'settings.screenAwareness.saveSettings': 'حفظ الإعدادات',
'settings.screenAwareness.session': 'الجلسة',
'settings.screenAwareness.status': 'الحالة',
'settings.screenAwareness.active': 'نشط',
'settings.screenAwareness.stopped': 'متوقف',
'settings.screenAwareness.remaining': 'المتبقي',
'settings.screenAwareness.startSession': 'بدء الجلسة',
'settings.screenAwareness.stopSession': 'إيقاف الجلسة',
'settings.screenAwareness.analyzeNow': 'تحليل الآن',
'settings.screenAwareness.macosOnly':
'التقاط سطح المكتب لوعي الشاشة وضوابط الأذونات مدعومة حاليًا على macOS فقط.',
'connections.comingSoon': 'قريبًا',
'connections.setUp': 'إعداد',
'connections.configured': 'مضبوط',
@@ -2518,36 +2494,6 @@ const messages: TranslationMap = {
'مفتاح رئيسي. معطّل افتراضيًا: Ollama يبقى خاملاً. عند التشغيل، يستخدم ملخّص الشجرة وذكاء الشاشة والإكمال التلقائي النموذجَ المحلي دائمًا.',
'localModel.advancedSettings': 'إعدادات متقدمة',
'localModel.debugTitle': 'تصحيح النموذج المحلي',
'screenAwareness.debugTitle': 'تصحيح وعي الشاشة',
'screenAwareness.debug.debugAndDiagnostics': 'التصحيح والتشخيص',
'screenAwareness.debug.collapse': 'طي',
'screenAwareness.debug.expand': 'توسيع',
'screenAwareness.debug.failedToSave': 'فشل حفظ ذكاء الشاشة',
'screenAwareness.debug.policyTitle': 'سياسة ذكاء الشاشة',
'screenAwareness.debug.baselineFps': 'FPS الأساسي',
'screenAwareness.debug.useVisionModel': 'استخدام نموذج الرؤية',
'screenAwareness.debug.useVisionModelDesc':
'أرسل لقطات الشاشة إلى نموذج رؤية LLM للحصول على سياق أغنى. عند التعطيل، يُستخدم نص OCR فقط مع نموذج نصي: أسرع ولا يتطلب نموذج رؤية.',
'screenAwareness.debug.keepScreenshots': 'الاحتفاظ بلقطات الشاشة',
'screenAwareness.debug.keepScreenshotsDesc':
'احفظ لقطات الشاشة الملتقطة في مساحة العمل بدلاً من حذفها بعد المعالجة',
'screenAwareness.debug.allowlist': 'القائمة المسموح بها (قاعدة واحدة لكل سطر)',
'screenAwareness.debug.denylist': 'قائمة الرفض (قاعدة واحدة لكل سطر)',
'screenAwareness.debug.saveSettings': 'حفظ إعدادات ذكاء الشاشة',
'screenAwareness.debug.sessionStats': 'إحصائيات الجلسة',
'screenAwareness.debug.framesEphemeral': 'الإطارات (عابرة)',
'screenAwareness.debug.panicStop': 'توقف الذعر',
'screenAwareness.debug.defaultPanicHotkey': 'Cmd+Shift+.',
'screenAwareness.debug.vision': 'الرؤية',
'screenAwareness.debug.idle': 'خاملة',
'screenAwareness.debug.visionQueue': 'قائمة انتظار الرؤية',
'screenAwareness.debug.lastVision': 'الرؤية الأخيرة',
'screenAwareness.debug.notAvailable': 'غير متوفر',
'screenAwareness.debug.visionSummaries': 'الرؤية الملخصات',
'screenAwareness.debug.refreshing': 'تحديث...',
'screenAwareness.debug.noSummaries': 'لا توجد ملخصات حتى الآن.',
'screenAwareness.debug.unknownApp': 'تطبيق غير معروف',
'screenAwareness.debug.macosOnly': 'ذكاء الشاشة V1 مدعوم على macOS فقط حالياً.',
'memory.debugTitle': 'تصحيح الذاكرة',
'memory.documents': 'المستندات',
'memory.filterByNamespace': 'التصفية حسب مساحة الاسم...',
@@ -3777,33 +3723,6 @@ const messages: TranslationMap = {
'intelligence.diagram.imageAlt': 'أحدث مخطط بنية OpenHuman المُولَّد',
'intelligence.diagram.refreshesEvery': 'يتجدد كل {seconds} ثانية',
'intelligence.memoryText.entityTypePrefix': 'نوع الكيان',
'intelligence.screenDebug.active': 'نشط',
'intelligence.screenDebug.app': 'التطبيق',
'intelligence.screenDebug.bounds': 'الحدود',
'intelligence.screenDebug.captureAlt': 'نتيجة اختبار التقاط',
'intelligence.screenDebug.captureFailed': 'فشل',
'intelligence.screenDebug.captureSuccess': 'نجاح',
'intelligence.screenDebug.captureTest': 'اختبار الالتقاط',
'intelligence.screenDebug.capturing': 'جارٍ الالتقاط',
'intelligence.screenDebug.frames': 'الإطارات',
'intelligence.screenDebug.idle': 'خامل',
'intelligence.screenDebug.lastApp': 'آخر تطبيق',
'intelligence.screenDebug.mode': 'الوضع',
'intelligence.screenDebug.permAccessibility': 'إذن إمكانية الوصول',
'intelligence.screenDebug.permInput': 'إذن الإدخال',
'intelligence.screenDebug.permScreen': 'إمكانية الوصول',
'intelligence.screenDebug.permissions': 'الأذونات',
'intelligence.screenDebug.platformNotSupported': 'المنصة غير مدعومة',
'intelligence.screenDebug.recentVisionSummaries': 'ملخصات الرؤية الأخيرة',
'intelligence.screenDebug.session': 'الجلسة',
'intelligence.screenDebug.size': 'الحجم',
'intelligence.screenDebug.status': 'الحالة',
'intelligence.screenDebug.testCapture': 'اختبار الالتقاط',
'intelligence.screenDebug.time': 'الوقت',
'intelligence.screenDebug.title': 'العنوان',
'intelligence.screenDebug.unknown': 'غير معروف',
'intelligence.screenDebug.visionQueue': 'قائمة انتظار الرؤية',
'intelligence.screenDebug.visionState': 'حالة الرؤية',
'intelligence.tasks.activeBoardOne': 'لوحة نشطة واحدة عبر المحادثات',
'intelligence.tasks.activeBoardOther': '{count} لوحات نشطة عبر المحادثات',
'intelligence.tasks.empty': 'لا توجد لوحات مهام للوكيل بعد',
@@ -4363,7 +4282,6 @@ const messages: TranslationMap = {
'overlay.ariaVoiceActive': 'إدخال الصوت نشط',
'overlay.companion.error': 'خطأ',
'overlay.companion.listening': 'يستمع…',
'overlay.companion.pointing': 'يشير…',
'overlay.companion.speaking': 'يتحدث…',
'overlay.companion.thinking': 'يفكر…',
'overlay.orbTitle': 'اسحب للتحريك · انقر مرتين لإعادة الضبط',
@@ -4393,12 +4311,8 @@ const messages: TranslationMap = {
'pages.settings.features.messagingChannelsDesc': 'وصف قنوات المراسلة',
'pages.settings.features.notifications': 'الإشعارات',
'pages.settings.features.notificationsDesc': 'وصف الإشعارات',
'pages.settings.features.screenAwareness': 'وعي الشاشة',
'pages.settings.features.screenAwarenessDesc':
'دع وكيلك يرى شاشتك ليقدم مساعدة سياقية في الوقت المناسب.',
'pages.settings.features.tools': 'الأدوات',
'pages.settings.features.toolsDesc': 'وصف الأدوات',
'pages.settings.featuresSection.description': 'وعي الشاشة والمراسلة والأدوات.',
'pages.settings.featuresSection.title': 'الميزات',
'privacy.dataKind.credentials': 'بيانات الاعتماد',
'privacy.dataKind.derived': 'مشتقة',
@@ -5145,9 +5059,6 @@ const messages: TranslationMap = {
'settings.localModel.status.working': 'جارٍ العمل...',
'settings.developerMenu.ai.title': 'إعدادات الذكاء الاصطناعي',
'settings.developerMenu.ai.desc': 'مزودو السحابة، ونماذج Ollama المحلية، والتوجيه لكل عبء عمل',
'settings.developerMenu.screenAwareness.title': 'الوعي بالشاشة',
'settings.developerMenu.screenAwareness.desc':
'أذونات التقاط الشاشة، وسياسة المراقبة، وعناصر التحكم في الجلسة',
'settings.developerMenu.messagingChannels.title': 'قنوات المراسلة',
'settings.developerMenu.messagingChannels.desc':
'تكوين أوضاع مصادقة Telegram/Discord وتوجيه القناة الافتراضي',
@@ -5776,20 +5687,6 @@ const messages: TranslationMap = {
'settings.modelHealth.modal.apply': 'استبدال التطبيق',
'settings.modelHealth.tag.cheaper': 'CHEAPER',
'settings.modelHealth.tag.better': 'BETTER',
'settings.screenIntel.permissions.accessibility': 'إمكانية الوصول',
'settings.screenIntel.permissions.grantHint': 'تلميح المنح',
'settings.screenIntel.permissions.inputMonitoring': 'مراقبة الإدخال',
'settings.screenIntel.permissions.macosAppliesPrivacy': 'macOS يطبّق الخصوصية',
'settings.screenIntel.permissions.openInputMonitoring': 'جارٍ الطلب…',
'settings.screenIntel.permissions.refreshStatus': 'جارٍ التحديث…',
'settings.screenIntel.permissions.refreshing': 'جارٍ التحديث…',
'settings.screenIntel.permissions.requestAccessibility': 'جارٍ الطلب…',
'settings.screenIntel.permissions.requestScreenRecording': 'جارٍ الطلب…',
'settings.screenIntel.permissions.requesting': 'جارٍ الطلب…',
'settings.screenIntel.permissions.restartRefresh': 'جارٍ إعادة تشغيل النواة…',
'settings.screenIntel.permissions.restartingCore': 'جارٍ إعادة تشغيل النواة…',
'settings.screenIntel.permissions.screenRecording': 'تسجيل الشاشة',
'settings.screenIntel.permissions.title': 'الأذونات',
'skills.card.moreActions': 'مزيد من الإجراءات',
'skills.channelIcon.discord': 'Discord',
'skills.channelIcon.imessage': 'iMessage',
@@ -6046,36 +5943,6 @@ const messages: TranslationMap = {
'skills.resource.tree.templates': 'القوالب',
'skills.resource.tree.empty': 'لا توجد موارد مجمّعة.',
'skills.search.placeholder': 'بحث',
'skills.setup.screenIntel.activeDesc': 'وصف النشاط',
'skills.setup.screenIntel.activeTitle': 'ذكاء الشاشة مفعّل',
'skills.setup.screenIntel.advancedSettings': 'إعدادات متقدمة',
'skills.setup.screenIntel.allGranted': 'تم منح جميع الأذونات',
'skills.setup.screenIntel.captureMode': 'وضع الالتقاط',
'skills.setup.screenIntel.captureModeValue': 'جميع النوافذ (قابل للضبط لاحقًا)',
'skills.setup.screenIntel.deniedHint':
'بعد منح الأذونات في إعدادات النظام، انقر أدناه لإعادة التشغيل وتطبيق التغييرات.',
'skills.setup.screenIntel.enableBtn': 'جارٍ التفعيل...',
'skills.setup.screenIntel.enableDesc': 'على شاشتك وتغذية سياق مفيد في وكيلك',
'skills.setup.screenIntel.enableError': 'فشل تفعيل ذكاء الشاشة',
'skills.setup.screenIntel.enabling': 'جارٍ التفعيل...',
'skills.setup.screenIntel.grant': 'جارٍ الفتح...',
'skills.setup.screenIntel.granted': 'تم المنح',
'skills.setup.screenIntel.macosOnly': 'macOS فقط',
'skills.setup.screenIntel.opening': 'جارٍ الفتح...',
'skills.setup.screenIntel.panicHotkey': 'مفتاح ساخن للطوارئ',
'skills.setup.screenIntel.permAccessibility': 'إمكانية الوصول',
'skills.setup.screenIntel.permInputMonitoring': 'مراقبة الإدخال',
'skills.setup.screenIntel.permScreenRecording': 'تسجيل الشاشة',
'skills.setup.screenIntel.permissionsDesc': 'وصف الأذونات',
'skills.setup.screenIntel.permissionPathLabel': 'يطبق macOS الخصوصية على:',
'skills.setup.screenIntel.refreshStatus': 'تحديث الحالة',
'skills.setup.screenIntel.restartRefresh': 'جارٍ إعادة التشغيل...',
'skills.setup.screenIntel.restarting': 'جارٍ إعادة التشغيل...',
'skills.setup.screenIntel.stepEnable': 'تفعيل المهارة',
'skills.setup.screenIntel.stepPermissions': 'منح الأذونات',
'skills.setup.screenIntel.stepSuccess': 'جاهز للانطلاق',
'skills.setup.screenIntel.title': 'ذكاء الشاشة',
'skills.setup.screenIntel.visionModel': 'نموذج الرؤية',
'skills.setup.voice.activation': 'التفعيل',
'skills.setup.voice.activeDescPrefix': 'Fn',
'skills.setup.voice.activeDescSuffix': 'Fn',
@@ -6233,9 +6100,6 @@ const messages: TranslationMap = {
'للحصول على تعليمات الإعداد، ثم أعد المحاولة عندما يصبح وقت التشغيل قابلاً للوصول.',
'devOptions.menuAi': 'تكوين الذكاء الاصطناعي',
'devOptions.menuAiDesc': 'موفرو السحابة، ونماذج Ollama المحلية، والتوجيه لكل حمل عمل',
'devOptions.menuScreenAware': 'التعرف على الشاشة',
'devOptions.menuScreenAwareDesc':
'أذونات التقاط الشاشة، وسياسة المراقبة، وعناصر التحكم في الجلسة',
'devOptions.menuMessaging': 'قنوات المراسلة',
'devOptions.menuMessagingDesc': 'تكوين أوضاع المصادقة Telegram/Discord وتوجيه القناة الافتراضية',
'devOptions.menuTools': 'الأدوات',
-137
View File
@@ -7,8 +7,6 @@ const messages: TranslationMap = {
'settings.assistant.desktopCompanion': 'ডেস্কটপ সঙ্গী',
'settings.assistant.desktopCompanionDesc': 'সিস্টেম ট্রে শর্টকাট সহ সর্বদা-চালু সঙ্গী মোড',
'pages.settings.features.desktopCompanion': 'ডেস্কটপ কম্প্যানিয়ন',
'pages.settings.features.desktopCompanionDesc':
'স্ক্রিন সচেতনতা সহ ভয়েস সহকারী: শোনে, দেখে, কথা বলে, নির্দেশ করে',
'settings.companion.title': 'ডেস্কটপ সঙ্গী',
'settings.companion.session': 'সেশন',
'settings.companion.activeLabel': 'সক্রিয়',
@@ -24,8 +22,6 @@ const messages: TranslationMap = {
'settings.companion.hotkey': 'হটকি',
'settings.companion.activationMode': 'সক্রিয়করণ মোড',
'settings.companion.sessionTtl': 'সেশন TTL',
'settings.companion.screenCapture': 'স্ক্রিন ক্যাপচার',
'settings.companion.appContext': 'অ্যাপ প্রসঙ্গ',
// Share cards (#5006).
'share.button': 'শেয়ার',
'share.modalTitle': 'এই মুহূর্তটি শেয়ার করুন',
@@ -111,8 +107,6 @@ const messages: TranslationMap = {
'connections.header.embeddings': 'মেমরি অনুসন্ধানে ব্যবহৃত এমবেডিং মডেল বেছে নিন।',
'connections.header.search': 'আপনার এজেন্টের জন্য ওয়েব অনুসন্ধান কনফিগার করুন।',
'connections.header.wallet': 'আপনার স্থানীয় ওয়ালেটের ব্যালেন্স এবং পুনরুদ্ধার বাক্যাংশ।',
'connections.header.screen':
'সময়োপযোগী, প্রাসঙ্গিক সহায়তা দিতে আপনার এজেন্টকে আপনার স্ক্রিন দেখতে দিন।',
'connections.header.composioKey':
'শত শত অ্যাপ ইন্টিগ্রেশন আনলক করতে একটি Composio API কী যোগ করুন।',
'connections.llm.apiKeys': 'API কী ও রাউটিং',
@@ -760,8 +754,6 @@ const messages: TranslationMap = {
'settings.assistant.backgroundActivity': 'সাবকনশাস',
'settings.assistant.backgroundActivityDesc':
'আপনার সহকারী পটভূমিতে কতটা সক্রিয় তা নিয়ন্ত্রণ করুন',
'settings.assistant.screenAwareness': 'স্ক্রিন সচেতনতা',
'settings.assistant.screenAwarenessDesc': 'সহকারীকে আপনার সক্রিয় উইন্ডো দেখতে দিন',
'settings.assistant.permissions': 'অনুমতি',
'settings.assistant.permissionsDesc': 'সহকারী কী করতে পারে এবং কোথায় কাজ করতে পারে তা বেছে নিন',
'settings.privacySecurity.privacy': 'গোপনীয়তা',
@@ -842,7 +834,6 @@ const messages: TranslationMap = {
'settings.notifications.tabs.preferences': 'পছন্দ',
'settings.notifications.tabs.routing': 'রাউটিং',
'settings.features': 'ফিচার',
'settings.featuresDesc': 'স্ক্রিন সচেতনতা, মেসেজিং এবং টুলস',
'settings.aiModels': 'AI ও মডেল',
'settings.aiModelsDesc': 'লোকাল AI মডেল সেটআপ, ডাউনলোড এবং LLM প্রোভাইডার',
'settings.ai': 'AI কনফিগারেশন',
@@ -934,8 +925,6 @@ const messages: TranslationMap = {
'settings.notifications.doNotDisturbDesc': 'নির্দিষ্ট সময়ের জন্য সব বিজ্ঞপ্তি বন্ধ রাখুন',
'settings.notifications.channelControls': 'চ্যানেল-ভিত্তিক নিয়ন্ত্রণ',
'settings.notifications.channelControlsDesc': 'প্রতিটি চ্যানেলের জন্য বিজ্ঞপ্তি পছন্দ সেট করুন',
'settings.features.screenAwareness': 'স্ক্রিন সচেতনতা',
'settings.features.screenAwarenessDesc': 'অ্যাসিস্ট্যান্টকে আপনার সক্রিয় উইন্ডো দেখতে দিন',
'settings.features.messaging': 'মেসেজিং',
'settings.features.messagingDesc': 'চ্যানেল ও মেসেজিং ইন্টিগ্রেশন সেটিংস',
'settings.features.tools': 'টুলস',
@@ -2205,21 +2194,6 @@ const messages: TranslationMap = {
'settings.tools.saveChanges': 'পরিবর্তন সংরক্ষণ করুন',
'settings.tools.preferencesSaved': 'পছন্দ সংরক্ষিত',
'settings.tools.saveFailed': 'পছন্দ সংরক্ষণ ব্যর্থ। আবার চেষ্টা করুন।',
'settings.screenAwareness.mode': 'মোড',
'settings.screenAwareness.allExceptBlacklist': 'ব্ল্যাকলিস্ট ছাড়া সব',
'settings.screenAwareness.whitelistOnly': 'শুধু হোয়াইটলিস্ট',
'settings.screenAwareness.screenMonitoring': 'স্ক্রিন মনিটরিং',
'settings.screenAwareness.saveSettings': 'সেটিংস সংরক্ষণ করুন',
'settings.screenAwareness.session': 'সেশন',
'settings.screenAwareness.status': 'স্ট্যাটাস',
'settings.screenAwareness.active': 'সক্রিয়',
'settings.screenAwareness.stopped': 'বন্ধ',
'settings.screenAwareness.remaining': 'অবশিষ্ট',
'settings.screenAwareness.startSession': 'সেশন শুরু করুন',
'settings.screenAwareness.stopSession': 'সেশন বন্ধ করুন',
'settings.screenAwareness.analyzeNow': 'এখনই বিশ্লেষণ করুন',
'settings.screenAwareness.macosOnly':
'স্ক্রিন সচেতনতা ডেস্কটপ ক্যাপচার এবং অনুমতি নিয়ন্ত্রণ বর্তমানে শুধু macOS-এ সমর্থিত।',
'connections.comingSoon': 'শীঘ্রই আসছে',
'connections.setUp': 'সেটআপ করুন',
'connections.configured': 'কনফিগার করা হয়েছে',
@@ -2582,36 +2556,6 @@ const messages: TranslationMap = {
'মাস্টার সুইচ। ডিফল্টে বন্ধ: Ollama নিষ্ক্রিয় থাকে। চালু হলে, ট্রি সামারাইজার, স্ক্রিন ইন্টেলিজেন্স এবং অটোকমপ্লিট সর্বদা লোকাল মডেল ব্যবহার করে।',
'localModel.advancedSettings': 'অ্যাডভান্সড সেটিংস',
'localModel.debugTitle': 'লোকাল মডেল ডিবাগ',
'screenAwareness.debugTitle': 'স্ক্রিন সচেতনতা ডিবাগ',
'screenAwareness.debug.debugAndDiagnostics': 'ডিবাগ এবং ডায়াগনস্টিকস',
'screenAwareness.debug.collapse': 'সঙ্কুচিত',
'screenAwareness.debug.expand': 'প্রসারিত করুন',
'screenAwareness.debug.failedToSave': 'স্ক্রীন ইন্টেলিজেন্স সংরক্ষণ করতে ব্যর্থ',
'screenAwareness.debug.policyTitle': 'স্ক্রীন ইন্টেলিজেন্স নীতি',
'screenAwareness.debug.baselineFps': 'বেসলাইন FPS',
'screenAwareness.debug.useVisionModel': 'ভিআইপি ব্যবহার করুন',
'screenAwareness.debug.useVisionModelDesc':
'আরও সমৃদ্ধ প্রেক্ষাপটের জন্য একটি ভিশন LLM-এ স্ক্রিনশট পাঠান। বন্ধ থাকলে, শুধুমাত্র OCR টেক্সট একটি টেক্সট LLM-এর সাথে ব্যবহার করা হয়: দ্রুততর এবং কোনো ভিশন মডেল প্রয়োজন নেই।',
'screenAwareness.debug.keepScreenshots': 'স্ক্রিনশটগুলি রাখুন',
'screenAwareness.debug.keepScreenshotsDesc':
'প্রক্রিয়াকরণের পরে মুছে না দিয়ে ক্যাপচার করা স্ক্রিনশটগুলি ওয়ার্কস্পেসে সংরক্ষণ করুন',
'screenAwareness.debug.allowlist': 'অনুমতি তালিকা (প্রতি লাইনে একটি নিয়ম)',
'screenAwareness.debug.denylist': 'অস্বীকারকারী (প্রতি লাইনে একটি নিয়ম)',
'screenAwareness.debug.saveSettings': 'স্ক্রীন ইন্টেলিজেন্স সেটিংস সংরক্ষণ করুন',
'screenAwareness.debug.sessionStats': 'সেশন পরিসংখ্যান',
'screenAwareness.debug.framesEphemeral': 'ফ্রেম (ক্ষণস্থায়ী)',
'screenAwareness.debug.panicStop': 'প্যানিক স্টপ',
'screenAwareness.debug.defaultPanicHotkey': 'Cmd+Shift+.',
'screenAwareness.debug.vision': 'দৃষ্টি',
'screenAwareness.debug.idle': 'নিষ্ক্রিয়',
'screenAwareness.debug.visionQueue': 'দৃষ্টি সারি',
'screenAwareness.debug.lastVision': 'শেষ দর্শন',
'screenAwareness.debug.notAvailable': 'n/a',
'screenAwareness.debug.visionSummaries': 'ভিশন সারাংশ',
'screenAwareness.debug.refreshing': 'রিফ্রেশ করা হচ্ছে...',
'screenAwareness.debug.noSummaries': 'এখনও কোনো সারসংক্ষেপ নেই।',
'screenAwareness.debug.unknownApp': 'অজানা অ্যাপ',
'screenAwareness.debug.macosOnly': 'স্ক্রীন ইন্টেলিজেন্স V1 বর্তমানে শুধুমাত্র macOS-এ সমর্থিত।',
'memory.debugTitle': 'মেমোরি ডিবাগ',
'memory.documents': 'নথি',
'memory.filterByNamespace': 'নামস্থান দ্বারা ফিল্টার করুন...',
@@ -3863,33 +3807,6 @@ const messages: TranslationMap = {
'intelligence.diagram.imageAlt': 'সর্বশেষ তৈরি OpenHuman আর্কিটেকচার ডায়াগ্রাম',
'intelligence.diagram.refreshesEvery': 'প্রতি {seconds}s-এ রিফ্রেশ হয়',
'intelligence.memoryText.entityTypePrefix': 'এনটিটি ধরন',
'intelligence.screenDebug.active': 'সক্রিয়',
'intelligence.screenDebug.app': 'অ্যাপ',
'intelligence.screenDebug.bounds': 'সীমানা',
'intelligence.screenDebug.captureAlt': 'ক্যাপচার পরীক্ষার ফলাফল',
'intelligence.screenDebug.captureFailed': 'ব্যর্থ',
'intelligence.screenDebug.captureSuccess': 'সফল',
'intelligence.screenDebug.captureTest': 'ক্যাপচার পরীক্ষা',
'intelligence.screenDebug.capturing': 'ক্যাপচার হচ্ছে',
'intelligence.screenDebug.frames': 'ফ্রেম',
'intelligence.screenDebug.idle': 'নিষ্ক্রিয়',
'intelligence.screenDebug.lastApp': 'শেষ অ্যাপ',
'intelligence.screenDebug.mode': 'মোড',
'intelligence.screenDebug.permAccessibility': 'অ্যাক্সেসিবিলিটি অনুমতি',
'intelligence.screenDebug.permInput': 'ইনপুট অনুমতি',
'intelligence.screenDebug.permScreen': 'অ্যাক্সেসিবিলিটি',
'intelligence.screenDebug.permissions': 'অনুমতি',
'intelligence.screenDebug.platformNotSupported': 'প্ল্যাটফর্ম সমর্থিত নয়',
'intelligence.screenDebug.recentVisionSummaries': 'সাম্প্রতিক ভিশন সারসংক্ষেপ',
'intelligence.screenDebug.session': 'সেশন',
'intelligence.screenDebug.size': 'আকার',
'intelligence.screenDebug.status': 'স্ট্যাটাস',
'intelligence.screenDebug.testCapture': 'ক্যাপচার পরীক্ষা করুন',
'intelligence.screenDebug.time': 'সময়',
'intelligence.screenDebug.title': 'শিরোনাম',
'intelligence.screenDebug.unknown': 'অজানা',
'intelligence.screenDebug.visionQueue': 'ভিশন কিউ',
'intelligence.screenDebug.visionState': 'ভিশন স্টেট',
'intelligence.tasks.activeBoardOne': 'কথোপকথনজুড়ে ১টি সক্রিয় বোর্ড',
'intelligence.tasks.activeBoardOther': 'কথোপকথনজুড়ে {count}টি সক্রিয় বোর্ড',
'intelligence.tasks.empty': 'এখনো কোনো এজেন্ট টাস্ক বোর্ড নেই',
@@ -4476,7 +4393,6 @@ const messages: TranslationMap = {
'overlay.ariaVoiceActive': 'ভয়েস ইনপুট সক্রিয়',
'overlay.companion.error': 'ত্রুটি',
'overlay.companion.listening': 'শুনছে…',
'overlay.companion.pointing': 'নির্দেশ করছে…',
'overlay.companion.speaking': 'বলছে…',
'overlay.companion.thinking': 'ভাবছে…',
'overlay.orbTitle': 'সরাতে টেনে আনুন · পজিশন রিসেট করতে ডাবল-ক্লিক করুন',
@@ -4506,12 +4422,8 @@ const messages: TranslationMap = {
'pages.settings.features.messagingChannelsDesc': 'মেসেজিং চ্যানেলের বিবরণ',
'pages.settings.features.notifications': 'বিজ্ঞপ্তি',
'pages.settings.features.notificationsDesc': 'বিজ্ঞপ্তির বিবরণ',
'pages.settings.features.screenAwareness': 'স্ক্রিন সচেতনতা',
'pages.settings.features.screenAwarenessDesc':
'সময়োপযোগী, প্রাসঙ্গিক সহায়তা দিতে আপনার এজেন্টকে আপনার স্ক্রিন দেখতে দিন।',
'pages.settings.features.tools': 'টুলস',
'pages.settings.features.toolsDesc': 'টুলসের বিবরণ',
'pages.settings.featuresSection.description': 'স্ক্রিন সচেতনতা, মেসেজিং এবং টুলস।',
'pages.settings.featuresSection.title': 'ফিচার',
'privacy.dataKind.credentials': 'ক্রেডেনশিয়াল',
'privacy.dataKind.derived': 'ডেরাইভড',
@@ -5263,9 +5175,6 @@ const messages: TranslationMap = {
'settings.developerMenu.ai.title': 'AI কনফিগারেশন',
'settings.developerMenu.ai.desc':
'ক্লাউড প্রদানকারী, স্থানীয় Ollama মডেল এবং প্রতি-ওয়ার্কলোড রাউটিং',
'settings.developerMenu.screenAwareness.title': 'স্ক্রিন সচেতনতা',
'settings.developerMenu.screenAwareness.desc':
'স্ক্রিন ক্যাপচার অনুমতি, মনিটরিং নীতি এবং সেশন নিয়ন্ত্রণ',
'settings.developerMenu.messagingChannels.title': 'মেসেজিং চ্যানেল',
'settings.developerMenu.messagingChannels.desc':
'Telegram/Discord অথেন্টিকেশন মোড এবং ডিফল্ট চ্যানেল রাউটিং কনফিগার করুন',
@@ -5910,20 +5819,6 @@ const messages: TranslationMap = {
'settings.modelHealth.modal.apply': '& বদলাও',
'settings.modelHealth.tag.cheaper': 'CHEAPER',
'settings.modelHealth.tag.better': 'BETTER',
'settings.screenIntel.permissions.accessibility': 'অ্যাক্সেসিবিলিটি',
'settings.screenIntel.permissions.grantHint': 'গ্রান্ট হিন্ট',
'settings.screenIntel.permissions.inputMonitoring': 'ইনপুট মনিটরিং',
'settings.screenIntel.permissions.macosAppliesPrivacy': 'macOS গোপনীয়তা প্রয়োগ করে',
'settings.screenIntel.permissions.openInputMonitoring': 'অনুরোধ করা হচ্ছে…',
'settings.screenIntel.permissions.refreshStatus': 'রিফ্রেশ হচ্ছে…',
'settings.screenIntel.permissions.refreshing': 'রিফ্রেশ হচ্ছে…',
'settings.screenIntel.permissions.requestAccessibility': 'অনুরোধ করা হচ্ছে…',
'settings.screenIntel.permissions.requestScreenRecording': 'অনুরোধ করা হচ্ছে…',
'settings.screenIntel.permissions.requesting': 'অনুরোধ করা হচ্ছে…',
'settings.screenIntel.permissions.restartRefresh': 'কোর রিস্টার্ট হচ্ছে…',
'settings.screenIntel.permissions.restartingCore': 'কোর রিস্টার্ট হচ্ছে…',
'settings.screenIntel.permissions.screenRecording': 'স্ক্রিন রেকর্ডিং',
'settings.screenIntel.permissions.title': 'অনুমতি',
'skills.card.moreActions': 'আরও কাজ',
'skills.channelIcon.discord': 'Discord',
'skills.channelIcon.imessage': 'iMessage',
@@ -6185,36 +6080,6 @@ const messages: TranslationMap = {
'skills.resource.tree.templates': 'টেমপ্লেট',
'skills.resource.tree.empty': 'কোনো বান্ডেলড রিসোর্স নেই।',
'skills.search.placeholder': 'প্লেসহোল্ডার',
'skills.setup.screenIntel.activeDesc': 'সক্রিয় বিবরণ',
'skills.setup.screenIntel.activeTitle': 'স্ক্রিন ইন্টেলিজেন্স সক্রিয়',
'skills.setup.screenIntel.advancedSettings': 'অ্যাডভান্সড সেটিংস',
'skills.setup.screenIntel.allGranted': 'সব অনুমতি দেওয়া হয়েছে',
'skills.setup.screenIntel.captureMode': 'ক্যাপচার মোড',
'skills.setup.screenIntel.captureModeValue': 'সব উইন্ডো (পরে কনফিগারযোগ্য)',
'skills.setup.screenIntel.deniedHint':
'সিস্টেম সেটিংসে অনুমতি দেওয়ার পর, পরিবর্তন নিতে নিচে ক্লিক করে রিস্টার্ট করুন।',
'skills.setup.screenIntel.enableBtn': 'সক্রিয় হচ্ছে...',
'skills.setup.screenIntel.enableDesc': 'আপনার স্ক্রিনে এবং এজেন্টে দরকারী কন্টেক্সট ফিড করুন',
'skills.setup.screenIntel.enableError': 'স্ক্রিন ইন্টেলিজেন্স সক্রিয় করতে ব্যর্থ',
'skills.setup.screenIntel.enabling': 'সক্রিয় হচ্ছে...',
'skills.setup.screenIntel.grant': 'খোলা হচ্ছে...',
'skills.setup.screenIntel.granted': 'দেওয়া হয়েছে',
'skills.setup.screenIntel.macosOnly': 'শুধু macOS',
'skills.setup.screenIntel.opening': 'খোলা হচ্ছে...',
'skills.setup.screenIntel.panicHotkey': 'প্যানিক হটকি',
'skills.setup.screenIntel.permAccessibility': 'অ্যাক্সেসিবিলিটি',
'skills.setup.screenIntel.permInputMonitoring': 'ইনপুট মনিটরিং',
'skills.setup.screenIntel.permScreenRecording': 'স্ক্রিন রেকর্ডিং',
'skills.setup.screenIntel.permissionsDesc': 'অনুমতির বিবরণ',
'skills.setup.screenIntel.permissionPathLabel': 'macOS গোপনীয়তা প্রযোজ্য:',
'skills.setup.screenIntel.refreshStatus': 'স্ট্যাটাস রিফ্রেশ করুন',
'skills.setup.screenIntel.restartRefresh': 'রিস্টার্ট হচ্ছে...',
'skills.setup.screenIntel.restarting': 'রিস্টার্ট হচ্ছে...',
'skills.setup.screenIntel.stepEnable': 'স্কিল সক্রিয় করুন',
'skills.setup.screenIntel.stepPermissions': 'অনুমতি দিন',
'skills.setup.screenIntel.stepSuccess': 'যেতে প্রস্তুত',
'skills.setup.screenIntel.title': 'স্ক্রিন ইন্টেলিজেন্স',
'skills.setup.screenIntel.visionModel': 'ভিশন মডেল',
'skills.setup.voice.activation': 'অ্যাক্টিভেশন',
'skills.setup.voice.activeDescPrefix': 'Fn',
'skills.setup.voice.activeDescSuffix': 'Fn',
@@ -6373,8 +6238,6 @@ const messages: TranslationMap = {
'সেটআপ নির্দেশনার জন্য, তারপর আপনার রানটাইম পৌঁছানো গেলে পুনরায় চেষ্টা করুন।',
'devOptions.menuAi': 'AI কনফিগারেশন',
'devOptions.menuAiDesc': 'ক্লাউড প্রদানকারী, স্থানীয় Ollama মডেল, এবং প্রতি-ওয়ার্কলোড রাউটিং',
'devOptions.menuScreenAware': 'স্ক্রীন সচেতনতা',
'devOptions.menuScreenAwareDesc': 'স্ক্রীন ক্যাপচার অনুমতি এবং সেশন নিয়ন্ত্রণ, নিরীক্ষণ নীতি',
'devOptions.menuMessaging': 'মেসেজিং চ্যানেলগুলি',
'devOptions.menuMessagingDesc':
'কনফিগার করুন Telegram/Discord প্রমাণীকরণ মোড এবং ডিফল্ট চ্যানেল রাউটিং',
-143
View File
@@ -9,8 +9,6 @@ const messages: TranslationMap = {
'settings.assistant.desktopCompanionDesc':
'Immer aktiver Begleiter-Modus mit System-Tray-Shortcut',
'pages.settings.features.desktopCompanion': 'Desktop-Begleiter',
'pages.settings.features.desktopCompanionDesc':
'Sprachassistent mit Bildschirmerkennung hört zu, sieht, spricht, zeigt',
'settings.companion.title': 'Desktop-Begleiter',
'settings.companion.session': 'Sitzung',
'settings.companion.activeLabel': 'Aktiv',
@@ -26,8 +24,6 @@ const messages: TranslationMap = {
'settings.companion.hotkey': 'Hotkey',
'settings.companion.activationMode': 'Aktivierungsmodus',
'settings.companion.sessionTtl': 'Sitzungs-TTL',
'settings.companion.screenCapture': 'Bildschirmaufnahme',
'settings.companion.appContext': 'App-Kontext',
// Share cards (#5006).
'share.button': 'Teilen',
'share.modalTitle': 'Diesen Moment teilen',
@@ -124,8 +120,6 @@ const messages: TranslationMap = {
'connections.header.search': 'Konfigurieren Sie die Websuche für Ihren Agenten.',
'connections.header.wallet':
'Das Guthaben Ihrer lokalen Wallet und Ihre Wiederherstellungsphrase.',
'connections.header.screen':
'Lassen Sie Ihren Agenten Ihren Bildschirm sehen, um zeitnahe, kontextbezogene Hilfe zu bieten.',
'connections.header.composioKey':
'Fügen Sie einen Composio-API-Schlüssel hinzu, um Hunderte von App-Integrationen freizuschalten.',
'connections.llm.apiKeys': 'API-Schlüssel & Routing',
@@ -792,8 +786,6 @@ const messages: TranslationMap = {
'settings.assistant.backgroundActivity': 'Unterbewusstsein',
'settings.assistant.backgroundActivityDesc':
'Steuern, wie aktiv Ihr Assistent im Hintergrund arbeitet',
'settings.assistant.screenAwareness': 'Bildschirmbewusstsein',
'settings.assistant.screenAwarenessDesc': 'Dem Assistenten das aktive Fenster zeigen',
'settings.assistant.permissions': 'Berechtigungen',
'settings.assistant.permissionsDesc': 'Wähle, was der Assistent tun darf und wo er arbeiten kann',
'settings.privacySecurity.privacy': 'Datenschutz',
@@ -875,7 +867,6 @@ const messages: TranslationMap = {
'settings.notifications.tabs.preferences': 'Einstellungen',
'settings.notifications.tabs.routing': 'Routenführung',
'settings.features': 'Funktionen',
'settings.featuresDesc': 'Bildschirmbewusstsein, Nachrichten und Tools',
'settings.aiModels': 'KI & Modelle',
'settings.aiModelsDesc': 'Lokales KI-Modell-Setup, Downloads und LLM-Anbieter',
'settings.ai': 'KI-Konfiguration',
@@ -973,8 +964,6 @@ const messages: TranslationMap = {
'settings.notifications.channelControls': 'Steuerung pro Kanal',
'settings.notifications.channelControlsDesc':
'Konfiguriere Benachrichtigungseinstellungen für jeden Kanal',
'settings.features.screenAwareness': 'Bildschirmbewusstsein',
'settings.features.screenAwarenessDesc': 'Lass den Assistenten dein aktives Fenster sehen',
'settings.features.messaging': 'Nachrichten',
'settings.features.messagingDesc': 'Einstellungen für die Kanal- und Messaging-Integration',
'settings.features.tools': 'Werkzeuge',
@@ -2277,21 +2266,6 @@ const messages: TranslationMap = {
'settings.tools.preferencesSaved': 'Einstellungen gespeichert',
'settings.tools.saveFailed':
'Einstellungen konnten nicht gespeichert werden. Versuche es erneut.',
'settings.screenAwareness.mode': 'Modus',
'settings.screenAwareness.allExceptBlacklist': 'Alle außer Blacklist',
'settings.screenAwareness.whitelistOnly': 'Nur Whitelist',
'settings.screenAwareness.screenMonitoring': 'Bildschirmüberwachung',
'settings.screenAwareness.saveSettings': 'Einstellungen speichern',
'settings.screenAwareness.session': 'Sitzung',
'settings.screenAwareness.status': 'Status',
'settings.screenAwareness.active': 'Aktiv',
'settings.screenAwareness.stopped': 'Angehalten',
'settings.screenAwareness.remaining': 'Übrig',
'settings.screenAwareness.startSession': 'Sitzung starten',
'settings.screenAwareness.stopSession': 'Sitzung beenden',
'settings.screenAwareness.analyzeNow': 'Jetzt analysieren',
'settings.screenAwareness.macosOnly':
'Die Desktop-Erfassung und Berechtigungssteuerung von Screen Awareness wird derzeit nur auf macOS unterstützt.',
'connections.comingSoon': 'Kommt bald',
'connections.setUp': 'Einrichten',
'connections.configured': 'Konfiguriert',
@@ -2657,37 +2631,6 @@ const messages: TranslationMap = {
'Hauptschalter. Standardmäßig deaktiviert Ollama bleibt inaktiv. Wenn diese Option aktiviert ist, verwenden die Baumzusammenfassung, die Bildschirmintelligenz und die automatische Vervollständigung immer das lokale Modell.',
'localModel.advancedSettings': 'Erweiterte Einstellungen',
'localModel.debugTitle': 'Lokales Modell-Debug',
'screenAwareness.debugTitle': 'Debuggen der Bildschirmerkennung',
'screenAwareness.debug.debugAndDiagnostics': 'Debug & Diagnose',
'screenAwareness.debug.collapse': 'Reduzieren',
'screenAwareness.debug.expand': 'Erweitern',
'screenAwareness.debug.failedToSave': 'Bildschirmintelligenz konnte nicht gespeichert werden.',
'screenAwareness.debug.policyTitle': 'Bildschirmintelligenzrichtlinie',
'screenAwareness.debug.baselineFps': 'Basis-FPS',
'screenAwareness.debug.useVisionModel': 'Vision-Modell verwenden',
'screenAwareness.debug.useVisionModelDesc':
'Screenshots an ein Vision-LLM senden, um reichhaltigeren Kontext zu erhalten. Wenn deaktiviert, wird nur OCR-Text mit einem Text-LLM verwendet schneller und ohne Vision-Modell.',
'screenAwareness.debug.keepScreenshots': 'Screenshots behalten',
'screenAwareness.debug.keepScreenshotsDesc':
'Aufgenommene Screenshots im Arbeitsbereich speichern, anstatt sie nach der Verarbeitung zu löschen',
'screenAwareness.debug.allowlist': 'Zulassungsliste (eine Regel pro Zeile)',
'screenAwareness.debug.denylist': 'Sperrliste (eine Regel pro Zeile)',
'screenAwareness.debug.saveSettings': 'Bildschirmintelligenzeinstellungen speichern',
'screenAwareness.debug.sessionStats': 'Sitzungsstatistiken',
'screenAwareness.debug.framesEphemeral': 'Frames (flüchtig)',
'screenAwareness.debug.panicStop': 'Panikstopp',
'screenAwareness.debug.defaultPanicHotkey': 'Befehl+Umschalt+.',
'screenAwareness.debug.vision': 'Vision',
'screenAwareness.debug.idle': 'inaktiv',
'screenAwareness.debug.visionQueue': 'Vision-Warteschlange',
'screenAwareness.debug.lastVision': 'Letzte Vision',
'screenAwareness.debug.notAvailable': 'n/a',
'screenAwareness.debug.visionSummaries': 'Vision-Zusammenfassungen',
'screenAwareness.debug.refreshing': 'Erfrischend…',
'screenAwareness.debug.noSummaries': 'Noch keine Zusammenfassungen.',
'screenAwareness.debug.unknownApp': 'Unbekannte App',
'screenAwareness.debug.macosOnly':
'Screen Intelligence V1 wird derzeit nur unter macOS unterstützt.',
'memory.debugTitle': 'Speicher-Debug',
'memory.documents': 'Dokumente',
'memory.filterByNamespace': 'Nach Namespace filtern...',
@@ -3970,33 +3913,6 @@ const messages: TranslationMap = {
'intelligence.diagram.imageAlt': 'Zuletzt generiertes OpenHuman-Architekturdiagramm',
'intelligence.diagram.refreshesEvery': 'Aktualisiert alle {seconds} s',
'intelligence.memoryText.entityTypePrefix': 'Entitätstyp',
'intelligence.screenDebug.active': 'Aktiv',
'intelligence.screenDebug.app': 'App',
'intelligence.screenDebug.bounds': 'Grenzen',
'intelligence.screenDebug.captureAlt': 'Testergebnis erfassen',
'intelligence.screenDebug.captureFailed': 'Fehlgeschlagen',
'intelligence.screenDebug.captureSuccess': 'Erfolg',
'intelligence.screenDebug.captureTest': 'Capture-Test',
'intelligence.screenDebug.capturing': 'Erfassen',
'intelligence.screenDebug.frames': 'Rahmen',
'intelligence.screenDebug.idle': 'Leerlauf',
'intelligence.screenDebug.lastApp': 'Letzte App',
'intelligence.screenDebug.mode': 'Modus',
'intelligence.screenDebug.permAccessibility': 'Zugänglichkeit aufrechterhalten',
'intelligence.screenDebug.permInput': 'Perm-Eingabe',
'intelligence.screenDebug.permScreen': 'Barrierefreiheit',
'intelligence.screenDebug.permissions': 'Berechtigungen',
'intelligence.screenDebug.platformNotSupported': 'Plattform nicht unterstützt',
'intelligence.screenDebug.recentVisionSummaries': 'Aktuelle Visionszusammenfassungen',
'intelligence.screenDebug.session': 'Sitzung',
'intelligence.screenDebug.size': 'Größe',
'intelligence.screenDebug.status': 'Status',
'intelligence.screenDebug.testCapture': 'Testaufnahme',
'intelligence.screenDebug.time': 'Zeit',
'intelligence.screenDebug.title': 'Titel',
'intelligence.screenDebug.unknown': 'Unbekannt',
'intelligence.screenDebug.visionQueue': 'Vision-Warteschlange',
'intelligence.screenDebug.visionState': 'Visionszustand',
'intelligence.tasks.activeBoardOne': '1 aktives Forum für alle Gespräche',
'intelligence.tasks.activeBoardOther': '{count} aktive Boards in allen Gesprächen',
'intelligence.tasks.empty': 'Noch keine Agenten-Taskboards',
@@ -4599,7 +4515,6 @@ const messages: TranslationMap = {
'overlay.ariaVoiceActive': 'Spracheingabe aktiv',
'overlay.companion.error': 'Fehler',
'overlay.companion.listening': 'Zuhören…',
'overlay.companion.pointing': 'Zeigen…',
'overlay.companion.speaking': 'Apropos…',
'overlay.companion.thinking': 'Denken…',
'overlay.orbTitle': 'Zum Verschieben ziehen · Doppelklicken, um die Position zurückzusetzen',
@@ -4633,12 +4548,8 @@ const messages: TranslationMap = {
'pages.settings.features.messagingChannelsDesc': 'Nachrichtenkanäle, Abschn',
'pages.settings.features.notifications': 'Benachrichtigungen',
'pages.settings.features.notificationsDesc': 'Benachrichtigungen absch',
'pages.settings.features.screenAwareness': 'Bildschirmbewusstsein',
'pages.settings.features.screenAwarenessDesc':
'Lassen Sie Ihren Agenten Ihren Bildschirm sehen, um zeitnahe, kontextbezogene Hilfe zu bieten.',
'pages.settings.features.tools': 'Werkzeuge',
'pages.settings.features.toolsDesc': 'Werkzeugbeschr',
'pages.settings.featuresSection.description': 'Bildschirmbewusstsein, Nachrichten und Tools.',
'pages.settings.featuresSection.title': 'Funktionen',
'privacy.dataKind.credentials': 'Anmeldeinformationen',
'privacy.dataKind.derived': 'Abgeleitet',
@@ -5410,9 +5321,6 @@ const messages: TranslationMap = {
'settings.developerMenu.ai.title': 'KI-Konfiguration',
'settings.developerMenu.ai.desc':
'Cloud-Anbieter, lokale Ollama-Modelle und Routing pro Workload',
'settings.developerMenu.screenAwareness.title': 'Bildschirmbewusstsein',
'settings.developerMenu.screenAwareness.desc':
'Bildschirmaufnahmeberechtigungen, Überwachungsrichtlinien und Sitzungskontrollen',
'settings.developerMenu.messagingChannels.title': 'Messaging-Kanäle',
'settings.developerMenu.messagingChannels.desc':
'Konfiguriere die Authentifizierungsmodi Telegram/Discord und das Standardkanalrouting',
@@ -6075,21 +5983,6 @@ const messages: TranslationMap = {
'settings.modelHealth.modal.apply': 'Ersatz anwenden',
'settings.modelHealth.tag.cheaper': 'CHEAPER',
'settings.modelHealth.tag.better': 'BETTER',
'settings.screenIntel.permissions.accessibility': 'Barrierefreiheit',
'settings.screenIntel.permissions.grantHint': 'Grant-Hinweis',
'settings.screenIntel.permissions.inputMonitoring': 'Eingabeüberwachung',
'settings.screenIntel.permissions.macosAppliesPrivacy':
'macOS verwaltet diese Berechtigungen unter Systemeinstellungen → Datenschutz & Sicherheit.',
'settings.screenIntel.permissions.openInputMonitoring': 'Bitte um …',
'settings.screenIntel.permissions.refreshStatus': 'Erfrischend…',
'settings.screenIntel.permissions.refreshing': 'Erfrischend…',
'settings.screenIntel.permissions.requestAccessibility': 'Bitte um …',
'settings.screenIntel.permissions.requestScreenRecording': 'Bitte um …',
'settings.screenIntel.permissions.requesting': 'Bitte um …',
'settings.screenIntel.permissions.restartRefresh': 'Kern wird neu gestartet...',
'settings.screenIntel.permissions.restartingCore': 'Kern wird neu gestartet...',
'settings.screenIntel.permissions.screenRecording': 'Bildschirmaufzeichnung',
'settings.screenIntel.permissions.title': 'Berechtigungen',
'skills.card.moreActions': 'Weitere Aktionen',
'skills.channelIcon.discord': 'Discord',
'skills.channelIcon.imessage': 'iMessage',
@@ -6353,39 +6246,6 @@ const messages: TranslationMap = {
'skills.resource.tree.templates': 'Vorlagen',
'skills.resource.tree.empty': 'Keine gebündelten Ressourcen.',
'skills.search.placeholder': 'Platzhalter',
'skills.setup.screenIntel.activeDesc':
'Screen Intelligence läuft und liest dein aktives Fenster.',
'skills.setup.screenIntel.activeTitle': 'Bildschirmintelligenz ist aktiviert',
'skills.setup.screenIntel.advancedSettings': 'Erweiterte Einstellungen',
'skills.setup.screenIntel.allGranted': 'Alle Berechtigungen erteilt',
'skills.setup.screenIntel.captureMode': 'Aufnahmemodus',
'skills.setup.screenIntel.captureModeValue': 'Alle Fenster (später konfigurierbar)',
'skills.setup.screenIntel.deniedHint':
'Nachdem du in den Systemeinstellungen Berechtigungen erteilt hast, klicke unten, um neu zu starten und die Änderungen zu übernehmen.',
'skills.setup.screenIntel.enableBtn': 'Aktivieren...',
'skills.setup.screenIntel.enableDesc':
'Liest, was auf deinem Bildschirm ist, und gibt deinem Agenten nützlichen Kontext.',
'skills.setup.screenIntel.enableError': 'Screen Intelligence konnte nicht aktiviert werden',
'skills.setup.screenIntel.enabling': 'Aktivieren...',
'skills.setup.screenIntel.grant': 'Öffnen...',
'skills.setup.screenIntel.granted': 'Erteilt',
'skills.setup.screenIntel.macosOnly': 'Nur macOS',
'skills.setup.screenIntel.opening': 'Öffnen...',
'skills.setup.screenIntel.panicHotkey': 'Panik-Hotkey',
'skills.setup.screenIntel.permAccessibility': 'Barrierefreiheit',
'skills.setup.screenIntel.permInputMonitoring': 'Eingabeüberwachung',
'skills.setup.screenIntel.permScreenRecording': 'Bildschirmaufzeichnung',
'skills.setup.screenIntel.permissionsDesc':
'Screen Intelligence benötigt Berechtigungen für Bedienungshilfen, Eingabeüberwachung und Bildschirmaufnahme.',
'skills.setup.screenIntel.permissionPathLabel': 'macOS wendet Datenschutz an:',
'skills.setup.screenIntel.refreshStatus': 'Status aktualisieren',
'skills.setup.screenIntel.restartRefresh': 'Neustart...',
'skills.setup.screenIntel.restarting': 'Neustart...',
'skills.setup.screenIntel.stepEnable': 'Aktiviere den Skill',
'skills.setup.screenIntel.stepPermissions': 'Berechtigungen erteilen',
'skills.setup.screenIntel.stepSuccess': 'Bereit zu gehen',
'skills.setup.screenIntel.title': 'Bildschirmintelligenz',
'skills.setup.screenIntel.visionModel': 'Vision-Modell',
'skills.setup.voice.activation': 'Aktivierung',
'skills.setup.voice.activeDescPrefix': 'Fn',
'skills.setup.voice.activeDescSuffix': 'Fn',
@@ -6547,9 +6407,6 @@ const messages: TranslationMap = {
'für Setup-Anweisungen, dann versuche es erneut, sobald deine Laufzeit erreichbar ist.',
'devOptions.menuAi': 'KI-Konfiguration',
'devOptions.menuAiDesc': 'Cloud-Anbieter, lokale Ollama-Modelle und Routing pro Workload',
'devOptions.menuScreenAware': 'Bildschirmbewusstsein',
'devOptions.menuScreenAwareDesc':
'Bildschirmaufnahmeberechtigungen, Überwachungsrichtlinien und Sitzungskontrollen',
'devOptions.menuMessaging': 'Messaging-Kanäle',
'devOptions.menuMessagingDesc':
'Konfiguriere die Authentifizierungsmodi Telegram/Discord und das Standardkanalrouting',
+1 -147
View File
@@ -5,8 +5,6 @@ const en: TranslationMap = {
'settings.assistant.desktopCompanionDesc': 'Always-on companion mode with a system-tray shortcut',
'connections.header.companion': 'A floating desktop companion for quick, always-on help.',
'pages.settings.features.desktopCompanion': 'Desktop Companion',
'pages.settings.features.desktopCompanionDesc':
'Voice assistant with screen awareness: listens, sees, speaks, points',
'settings.companion.title': 'Desktop Companion',
'settings.companion.session': 'Session',
'settings.companion.activeLabel': 'Active',
@@ -22,8 +20,6 @@ const en: TranslationMap = {
'settings.companion.hotkey': 'Hotkey',
'settings.companion.activationMode': 'Activation Mode',
'settings.companion.sessionTtl': 'Session TTL',
'settings.companion.screenCapture': 'Screen Capture',
'settings.companion.appContext': 'App Context',
'skills.recallCalendar.title': 'Google Calendar',
'skills.recallCalendar.description': 'Auto-join Google Meet calls via Recall.ai',
// Navigation
@@ -520,8 +516,6 @@ const en: TranslationMap = {
'settings.assistant.backgroundActivity': 'Subconscious',
'settings.assistant.backgroundActivityDesc':
'Control how actively your assistant works in the background',
'settings.assistant.screenAwareness': 'Screen awareness',
'settings.assistant.screenAwarenessDesc': 'Let the assistant see your active window',
'settings.assistant.permissions': 'Permissions',
'settings.assistant.permissionsDesc': 'Choose what the assistant can do and where it can work',
@@ -599,7 +593,6 @@ const en: TranslationMap = {
'settings.notifications.tabs.preferences': 'Preferences',
'settings.notifications.tabs.routing': 'Routing',
'settings.features': 'Features',
'settings.featuresDesc': 'Screen awareness, messaging, and tools',
'settings.aiModels': 'AI & Models',
'settings.aiModelsDesc': 'Local AI model setup, downloads, and LLM provider',
'settings.ai': 'AI Configuration',
@@ -703,8 +696,6 @@ const en: TranslationMap = {
'Configure notification preferences for each channel',
// Settings: Features
'settings.features.screenAwareness': 'Screen Awareness',
'settings.features.screenAwarenessDesc': 'Let the assistant see your active window',
'settings.features.messaging': 'Messaging',
'settings.features.messagingDesc': 'Channel and messaging integration settings',
'settings.features.tools': 'Tools',
@@ -930,7 +921,6 @@ const en: TranslationMap = {
'connections.header.embeddings': 'Choose the embedding model used for memory search.',
'connections.header.search': 'Configure web search for your agent.',
'connections.header.wallet': 'Your local wallet balance and recovery phrase.',
'connections.header.screen': 'Let your agent see your screen to offer timely, in-context help.',
'connections.header.composioKey':
'Add a Composio API key to unlock hundreds of app integrations.',
'connections.llm.apiKeys': 'API keys & routing',
@@ -2306,23 +2296,6 @@ const en: TranslationMap = {
'settings.tools.preferencesSaved': 'Preferences saved',
'settings.tools.saveFailed': 'Failed to save preferences. Try again.',
// Settings: Screen Awareness
'settings.screenAwareness.mode': 'Mode',
'settings.screenAwareness.allExceptBlacklist': 'All Except Blacklist',
'settings.screenAwareness.whitelistOnly': 'Whitelist Only',
'settings.screenAwareness.screenMonitoring': 'Screen Monitoring',
'settings.screenAwareness.saveSettings': 'Save Settings',
'settings.screenAwareness.session': 'Session',
'settings.screenAwareness.status': 'Status',
'settings.screenAwareness.active': 'Active',
'settings.screenAwareness.stopped': 'Stopped',
'settings.screenAwareness.remaining': 'Remaining',
'settings.screenAwareness.startSession': 'Start Session',
'settings.screenAwareness.stopSession': 'Stop Session',
'settings.screenAwareness.analyzeNow': 'Analyze Now',
'settings.screenAwareness.macosOnly':
'Screen Awareness desktop capture and permission controls are currently supported on macOS only.',
// Connections
'connections.comingSoon': 'Coming soon',
'connections.setUp': 'Set up',
@@ -2706,42 +2679,10 @@ const en: TranslationMap = {
'Choose which subsystems run on the local model. Anything off uses the cloud.',
'localModel.enableRuntime': 'Enable local AI runtime',
'localModel.enableRuntimeDesc':
'Master switch. Off by default: Ollama stays idle. When on, the tree summarizer, screen intelligence, and autocomplete always use the local model.',
'Master switch. Off by default: Ollama stays idle. When on, the tree summarizer and autocomplete always use the local model.',
'localModel.advancedSettings': 'Advanced settings',
'localModel.debugTitle': 'Local Model Debug',
// Screen Awareness
'screenAwareness.debugTitle': 'Screen Awareness Debug',
'screenAwareness.debug.debugAndDiagnostics': 'Debug & Diagnostics',
'screenAwareness.debug.collapse': 'Collapse',
'screenAwareness.debug.expand': 'Expand',
'screenAwareness.debug.failedToSave': 'Failed to save screen intelligence',
'screenAwareness.debug.policyTitle': 'Screen Intelligence Policy',
'screenAwareness.debug.baselineFps': 'Baseline FPS',
'screenAwareness.debug.useVisionModel': 'Use Vision Model',
'screenAwareness.debug.useVisionModelDesc':
'Send screenshots to a vision LLM for richer context. When off, only OCR text is used with a text LLM: faster and no vision model required.',
'screenAwareness.debug.keepScreenshots': 'Keep Screenshots',
'screenAwareness.debug.keepScreenshotsDesc':
'Save captured screenshots to the workspace instead of deleting after processing',
'screenAwareness.debug.allowlist': 'Allowlist (one rule per line)',
'screenAwareness.debug.denylist': 'Denylist (one rule per line)',
'screenAwareness.debug.saveSettings': 'Save Screen Intelligence Settings',
'screenAwareness.debug.sessionStats': 'Session Stats',
'screenAwareness.debug.framesEphemeral': 'Frames (ephemeral)',
'screenAwareness.debug.panicStop': 'Panic stop',
'screenAwareness.debug.defaultPanicHotkey': 'Cmd+Shift+.',
'screenAwareness.debug.vision': 'Vision',
'screenAwareness.debug.idle': 'idle',
'screenAwareness.debug.visionQueue': 'Vision queue',
'screenAwareness.debug.lastVision': 'Last vision',
'screenAwareness.debug.notAvailable': 'n/a',
'screenAwareness.debug.visionSummaries': 'Vision Summaries',
'screenAwareness.debug.refreshing': 'Refreshing…',
'screenAwareness.debug.noSummaries': 'No summaries yet.',
'screenAwareness.debug.unknownApp': 'Unknown App',
'screenAwareness.debug.macosOnly': 'Screen Intelligence V1 is currently supported on macOS only.',
// Memory
'memory.debugTitle': 'Memory Debug',
'memory.documents': 'Documents',
@@ -4252,33 +4193,6 @@ const en: TranslationMap = {
'intelligence.diagram.imageAlt': 'Latest generated OpenHuman architecture diagram',
'intelligence.diagram.refreshesEvery': 'Refreshes every {seconds}s',
'intelligence.memoryText.entityTypePrefix': 'Entity type',
'intelligence.screenDebug.active': 'Active',
'intelligence.screenDebug.app': 'App',
'intelligence.screenDebug.bounds': 'Bounds',
'intelligence.screenDebug.captureAlt': 'Capture test result',
'intelligence.screenDebug.captureFailed': 'Failed',
'intelligence.screenDebug.captureSuccess': 'Success',
'intelligence.screenDebug.captureTest': 'Capture test',
'intelligence.screenDebug.capturing': 'Capturing',
'intelligence.screenDebug.frames': 'Frames',
'intelligence.screenDebug.idle': 'Idle',
'intelligence.screenDebug.lastApp': 'Last App',
'intelligence.screenDebug.mode': 'Mode',
'intelligence.screenDebug.permAccessibility': 'Perm accessibility',
'intelligence.screenDebug.permInput': 'Perm input',
'intelligence.screenDebug.permScreen': 'Accessibility',
'intelligence.screenDebug.permissions': 'Permissions',
'intelligence.screenDebug.platformNotSupported': 'Platform not supported',
'intelligence.screenDebug.recentVisionSummaries': 'Recent vision summaries',
'intelligence.screenDebug.session': 'Session',
'intelligence.screenDebug.size': 'Size',
'intelligence.screenDebug.status': 'Status',
'intelligence.screenDebug.testCapture': 'Test capture',
'intelligence.screenDebug.time': 'Time',
'intelligence.screenDebug.title': 'Title',
'intelligence.screenDebug.unknown': 'Unknown',
'intelligence.screenDebug.visionQueue': 'Vision Queue',
'intelligence.screenDebug.visionState': 'Vision State',
'intelligence.tasks.activeBoardOne': '1 active board across conversations',
'intelligence.tasks.activeBoardOther': '{count} active boards across conversations',
'intelligence.tasks.empty': 'No agent task boards yet',
@@ -5063,7 +4977,6 @@ const en: TranslationMap = {
'overlay.ariaVoiceActive': 'Voice input active',
'overlay.companion.error': 'Error',
'overlay.companion.listening': 'Listening…',
'overlay.companion.pointing': 'Pointing…',
'overlay.companion.speaking': 'Speaking…',
'overlay.companion.thinking': 'Thinking…',
'overlay.orbTitle': 'Drag to move · Double-click to reset position',
@@ -5095,12 +5008,8 @@ const en: TranslationMap = {
'pages.settings.features.messagingChannelsDesc': 'Messaging channels desc',
'pages.settings.features.notifications': 'Notifications',
'pages.settings.features.notificationsDesc': 'Notifications desc',
'pages.settings.features.screenAwareness': 'Screen awareness',
'pages.settings.features.screenAwarenessDesc':
'Let your agent see your screen to offer timely, in-context help.',
'pages.settings.features.tools': 'Tools',
'pages.settings.features.toolsDesc': 'Tools desc',
'pages.settings.featuresSection.description': 'Screen awareness, messaging, and tools.',
'pages.settings.featuresSection.title': 'Features',
'privacy.dataKind.credentials': 'Credentials',
'privacy.dataKind.derived': 'Derived',
@@ -5856,9 +5765,6 @@ const en: TranslationMap = {
'settings.developerMenu.ai.title': 'AI Configuration',
'settings.developerMenu.ai.desc':
'Cloud providers, local Ollama models, and per-workload routing',
'settings.developerMenu.screenAwareness.title': 'Screen Awareness',
'settings.developerMenu.screenAwareness.desc':
'Screen capture permissions, monitoring policy, and session controls',
'settings.developerMenu.messagingChannels.title': 'Messaging Channels',
'settings.developerMenu.messagingChannels.desc':
'Configure Telegram/Discord auth modes and default channel routing',
@@ -6502,22 +6408,6 @@ const en: TranslationMap = {
'settings.modelHealth.modal.apply': 'Apply Replacement',
'settings.modelHealth.tag.cheaper': 'CHEAPER',
'settings.modelHealth.tag.better': 'BETTER',
'settings.screenIntel.permissions.accessibility': 'Accessibility',
'settings.screenIntel.permissions.grantHint':
'Grant these permissions in System Settings, then restart the core.',
'settings.screenIntel.permissions.inputMonitoring': 'Input Monitoring',
'settings.screenIntel.permissions.macosAppliesPrivacy':
'macOS controls these under System Settings → Privacy & Security.',
'settings.screenIntel.permissions.openInputMonitoring': 'Open Input Monitoring',
'settings.screenIntel.permissions.refreshStatus': 'Refreshing…',
'settings.screenIntel.permissions.refreshing': 'Refreshing…',
'settings.screenIntel.permissions.requestAccessibility': 'Request Accessibility',
'settings.screenIntel.permissions.requestScreenRecording': 'Request Screen Recording',
'settings.screenIntel.permissions.requesting': 'Requesting…',
'settings.screenIntel.permissions.restartRefresh': 'Restarting core…',
'settings.screenIntel.permissions.restartingCore': 'Restarting core…',
'settings.screenIntel.permissions.screenRecording': 'Screen Recording',
'settings.screenIntel.permissions.title': 'Permissions',
'skills.card.moreActions': 'More actions',
'skills.channelIcon.discord': 'Discord',
'skills.channelIcon.imessage': 'iMessage',
@@ -6787,39 +6677,6 @@ const en: TranslationMap = {
'skills.resource.tree.scripts': 'Scripts',
'skills.resource.tree.templates': 'Templates',
'skills.search.placeholder': 'Search skills…',
'skills.setup.screenIntel.activeDesc':
'Screen Intelligence is running and reading your active window.',
'skills.setup.screenIntel.activeTitle': 'Screen Intelligence is Enabled',
'skills.setup.screenIntel.advancedSettings': 'Advanced settings',
'skills.setup.screenIntel.allGranted': 'All permissions granted',
'skills.setup.screenIntel.captureMode': 'Capture mode',
'skills.setup.screenIntel.captureModeValue': 'All windows (configurable later)',
'skills.setup.screenIntel.deniedHint':
'After granting permissions in System Settings, click below to restart and pick up the changes.',
'skills.setup.screenIntel.enableBtn': 'Enabling...',
'skills.setup.screenIntel.enableDesc':
'Reads what is on your screen and feeds useful context into your agent.',
'skills.setup.screenIntel.enableError': 'Failed to enable Screen Intelligence',
'skills.setup.screenIntel.enabling': 'Enabling...',
'skills.setup.screenIntel.grant': 'Opening...',
'skills.setup.screenIntel.granted': 'Granted',
'skills.setup.screenIntel.macosOnly': 'macOS only',
'skills.setup.screenIntel.opening': 'Opening...',
'skills.setup.screenIntel.panicHotkey': 'Panic hotkey',
'skills.setup.screenIntel.permAccessibility': 'Accessibility',
'skills.setup.screenIntel.permInputMonitoring': 'Input Monitoring',
'skills.setup.screenIntel.permScreenRecording': 'Screen Recording',
'skills.setup.screenIntel.permissionsDesc':
'Screen Intelligence requires accessibility, input monitoring, and screen recording permissions.',
'skills.setup.screenIntel.permissionPathLabel': 'macOS applies privacy to:',
'skills.setup.screenIntel.refreshStatus': 'Refresh status',
'skills.setup.screenIntel.restartRefresh': 'Restarting...',
'skills.setup.screenIntel.restarting': 'Restarting...',
'skills.setup.screenIntel.stepEnable': 'Enable the skill',
'skills.setup.screenIntel.stepPermissions': 'Grant permissions',
'skills.setup.screenIntel.stepSuccess': 'Ready to go',
'skills.setup.screenIntel.title': 'Screen Intelligence',
'skills.setup.screenIntel.visionModel': 'Vision model',
'skills.setup.voice.activation': 'Activation',
'skills.setup.voice.activeDescPrefix': 'Fn',
'skills.setup.voice.activeDescSuffix': 'Fn',
@@ -6978,9 +6835,6 @@ const en: TranslationMap = {
// Developer options menu items (#2225)
'devOptions.menuAi': 'AI Configuration',
'devOptions.menuAiDesc': 'Cloud providers, local Ollama models, and per-workload routing',
'devOptions.menuScreenAware': 'Screen Awareness',
'devOptions.menuScreenAwareDesc':
'Screen capture permissions, monitoring policy, and session controls',
'devOptions.menuMessaging': 'Messaging Channels',
'devOptions.menuMessagingDesc':
'Configure Telegram/Discord auth modes and default channel routing',
-140
View File
@@ -9,8 +9,6 @@ const messages: TranslationMap = {
'settings.assistant.desktopCompanionDesc':
'Modo compañero siempre activo con acceso directo en la bandeja del sistema',
'pages.settings.features.desktopCompanion': 'Acompañante de escritorio',
'pages.settings.features.desktopCompanionDesc':
'Asistente de voz con conciencia de pantalla: escucha, ve, habla, señala',
'settings.companion.title': 'Compañero de escritorio',
'settings.companion.session': 'Sesión',
'settings.companion.activeLabel': 'Activo',
@@ -26,8 +24,6 @@ const messages: TranslationMap = {
'settings.companion.hotkey': 'Atajo de teclado',
'settings.companion.activationMode': 'Modo de activación',
'settings.companion.sessionTtl': 'TTL de sesión',
'settings.companion.screenCapture': 'Captura de pantalla',
'settings.companion.appContext': 'Contexto de la aplicación',
// Share cards (#5006).
'share.button': 'Compartir',
'share.modalTitle': 'Comparte este momento',
@@ -116,8 +112,6 @@ const messages: TranslationMap = {
'Elige el modelo de embeddings usado para la búsqueda en la memoria.',
'connections.header.search': 'Configura la búsqueda web para tu agente.',
'connections.header.wallet': 'El saldo de tu monedero local y tu frase de recuperación.',
'connections.header.screen':
'Deja que tu agente vea tu pantalla para ofrecerte ayuda oportuna y en contexto.',
'connections.header.composioKey':
'Añade una clave de API de Composio para desbloquear cientos de integraciones de apps.',
'connections.llm.apiKeys': 'Claves de API y enrutamiento',
@@ -774,8 +768,6 @@ const messages: TranslationMap = {
'settings.assistant.backgroundActivity': 'Subconsciente',
'settings.assistant.backgroundActivityDesc':
'Controla qué tan activo trabaja tu asistente en segundo plano',
'settings.assistant.screenAwareness': 'Conciencia de pantalla',
'settings.assistant.screenAwarenessDesc': 'Permite al asistente ver tu ventana activa',
'settings.assistant.permissions': 'Permisos',
'settings.assistant.permissionsDesc': 'Elige qué puede hacer el asistente y dónde puede trabajar',
'settings.privacySecurity.privacy': 'Privacidad',
@@ -856,7 +848,6 @@ const messages: TranslationMap = {
'settings.notifications.tabs.preferences': 'Preferencias',
'settings.notifications.tabs.routing': 'Enrutamiento',
'settings.features': 'Funciones',
'settings.featuresDesc': 'Conciencia de pantalla, mensajería y herramientas',
'settings.aiModels': 'IA y modelos',
'settings.aiModelsDesc': 'Configuración de modelos de IA locales, descargas y proveedor LLM',
'settings.ai': 'Configuración de IA',
@@ -956,8 +947,6 @@ const messages: TranslationMap = {
'settings.notifications.channelControls': 'Controles por canal',
'settings.notifications.channelControlsDesc':
'Configura las preferencias de notificación para cada canal',
'settings.features.screenAwareness': 'Conciencia de pantalla',
'settings.features.screenAwarenessDesc': 'Permite que el asistente vea tu ventana activa',
'settings.features.messaging': 'Mensajería',
'settings.features.messagingDesc': 'Configuración de integración de canales y mensajería',
'settings.features.tools': 'Herramientas',
@@ -2249,21 +2238,6 @@ const messages: TranslationMap = {
'settings.tools.saveChanges': 'Guardar cambios',
'settings.tools.preferencesSaved': 'Preferencias guardadas',
'settings.tools.saveFailed': 'No se pudieron guardar las preferencias. Inténtalo de nuevo.',
'settings.screenAwareness.mode': 'Modo',
'settings.screenAwareness.allExceptBlacklist': 'Todo excepto lista negra',
'settings.screenAwareness.whitelistOnly': 'Solo lista blanca',
'settings.screenAwareness.screenMonitoring': 'Monitoreo de pantalla',
'settings.screenAwareness.saveSettings': 'Guardar configuración',
'settings.screenAwareness.session': 'Sesión',
'settings.screenAwareness.status': 'Estado',
'settings.screenAwareness.active': 'Activo',
'settings.screenAwareness.stopped': 'Detenido',
'settings.screenAwareness.remaining': 'Restante',
'settings.screenAwareness.startSession': 'Iniciar sesión',
'settings.screenAwareness.stopSession': 'Detener sesión',
'settings.screenAwareness.analyzeNow': 'Analizar ahora',
'settings.screenAwareness.macosOnly':
'La captura de pantalla y los controles de permisos de Conciencia de pantalla actualmente solo son compatibles con macOS.',
'connections.comingSoon': 'Próximamente',
'connections.setUp': 'Configurar',
'connections.configured': 'Configurado',
@@ -2629,37 +2603,6 @@ const messages: TranslationMap = {
'Interruptor principal. Desactivado por defecto: Ollama permanece inactivo. Cuando está activado, el resumidor de árbol, la inteligencia de pantalla y el autocompletado siempre usan el modelo local.',
'localModel.advancedSettings': 'Configuración avanzada',
'localModel.debugTitle': 'Depuración de modelo local',
'screenAwareness.debugTitle': 'Depuración de Conciencia de pantalla',
'screenAwareness.debug.debugAndDiagnostics': 'Depuración y diagnóstico',
'screenAwareness.debug.collapse': 'Colapso',
'screenAwareness.debug.expand': 'Expandir',
'screenAwareness.debug.failedToSave': 'No se pudo guardar la inteligencia de pantalla',
'screenAwareness.debug.policyTitle': 'Política de inteligencia de pantalla',
'screenAwareness.debug.baselineFps': 'FPS de referencia',
'screenAwareness.debug.useVisionModel': 'Usar modelo de visión',
'screenAwareness.debug.useVisionModelDesc':
'Envía capturas de pantalla a un LLM de visión para obtener más contexto. Cuando está desactivado, solo se usa texto OCR con un LLM de texto, lo que es más rápido y no requiere modelo de visión.',
'screenAwareness.debug.keepScreenshots': 'Mantener capturas de pantalla',
'screenAwareness.debug.keepScreenshotsDesc':
'Guarda las capturas de pantalla en el espacio de trabajo en lugar de eliminarlas tras el procesamiento',
'screenAwareness.debug.allowlist': 'Lista de permitidos (una regla por línea)',
'screenAwareness.debug.denylist': 'Lista de rechazados (una regla por línea)',
'screenAwareness.debug.saveSettings': 'Guardar configuración de inteligencia de pantalla',
'screenAwareness.debug.sessionStats': 'Estadísticas de sesión',
'screenAwareness.debug.framesEphemeral': 'Marcos (efímeros)',
'screenAwareness.debug.panicStop': 'parada de pánico',
'screenAwareness.debug.defaultPanicHotkey': 'Cmd+Mayús+.',
'screenAwareness.debug.vision': 'Visión',
'screenAwareness.debug.idle': 'inactivo',
'screenAwareness.debug.visionQueue': 'Cola de visión',
'screenAwareness.debug.lastVision': 'ultima vision',
'screenAwareness.debug.notAvailable': 'n/a',
'screenAwareness.debug.visionSummaries': 'Resúmenes de visión',
'screenAwareness.debug.refreshing': 'Refrescante…',
'screenAwareness.debug.noSummaries': 'Aún no hay resúmenes.',
'screenAwareness.debug.unknownApp': 'Aplicación desconocida',
'screenAwareness.debug.macosOnly':
'Screen Intelligence V1 actualmente solo es compatible con macOS.',
'memory.debugTitle': 'Depuración de memoria',
'memory.documents': 'Documentos',
'memory.filterByNamespace': 'Filtrar por espacio de nombres...',
@@ -3929,33 +3872,6 @@ const messages: TranslationMap = {
'intelligence.diagram.imageAlt': 'Último diagrama de arquitectura de OpenHuman generado',
'intelligence.diagram.refreshesEvery': 'Se actualiza cada {seconds}s',
'intelligence.memoryText.entityTypePrefix': 'Tipo de entidad',
'intelligence.screenDebug.active': 'Activo',
'intelligence.screenDebug.app': 'Aplicación',
'intelligence.screenDebug.bounds': 'Límites',
'intelligence.screenDebug.captureAlt': 'Resultado de prueba de captura',
'intelligence.screenDebug.captureFailed': 'Falló',
'intelligence.screenDebug.captureSuccess': 'Éxito',
'intelligence.screenDebug.captureTest': 'Prueba de captura',
'intelligence.screenDebug.capturing': 'Capturando',
'intelligence.screenDebug.frames': 'Fotogramas',
'intelligence.screenDebug.idle': 'Inactivo',
'intelligence.screenDebug.lastApp': 'Última app',
'intelligence.screenDebug.mode': 'Modo',
'intelligence.screenDebug.permAccessibility': 'Permiso de accesibilidad',
'intelligence.screenDebug.permInput': 'Permiso de entrada',
'intelligence.screenDebug.permScreen': 'Accesibilidad',
'intelligence.screenDebug.permissions': 'Permisos',
'intelligence.screenDebug.platformNotSupported': 'Plataforma no compatible',
'intelligence.screenDebug.recentVisionSummaries': 'Resúmenes de visión recientes',
'intelligence.screenDebug.session': 'Sesión',
'intelligence.screenDebug.size': 'Tamaño',
'intelligence.screenDebug.status': 'Estado',
'intelligence.screenDebug.testCapture': 'Prueba de captura',
'intelligence.screenDebug.time': 'Tiempo',
'intelligence.screenDebug.title': 'Título',
'intelligence.screenDebug.unknown': 'Desconocido',
'intelligence.screenDebug.visionQueue': 'Cola de visión',
'intelligence.screenDebug.visionState': 'Estado de visión',
'intelligence.tasks.activeBoardOne': '1 tablero activo entre conversaciones',
'intelligence.tasks.activeBoardOther': '{count} tableros activos entre conversaciones',
'intelligence.tasks.empty': 'Sin tableros de tareas de agente aún',
@@ -4550,7 +4466,6 @@ const messages: TranslationMap = {
'overlay.ariaVoiceActive': 'Entrada de voz activa',
'overlay.companion.error': 'error',
'overlay.companion.listening': 'Escuchando…',
'overlay.companion.pointing': 'Señalando…',
'overlay.companion.speaking': 'Hablando…',
'overlay.companion.thinking': 'Pensando…',
'overlay.orbTitle': 'Arrastra para mover · Doble clic para restablecer posición',
@@ -4582,13 +4497,8 @@ const messages: TranslationMap = {
'pages.settings.features.messagingChannelsDesc': 'Descripción de canales de mensajería',
'pages.settings.features.notifications': 'Notificaciones',
'pages.settings.features.notificationsDesc': 'Descripción de notificaciones',
'pages.settings.features.screenAwareness': 'Conciencia de pantalla',
'pages.settings.features.screenAwarenessDesc':
'Deja que tu agente vea tu pantalla para ofrecerte ayuda oportuna y en contexto.',
'pages.settings.features.tools': 'Herramientas',
'pages.settings.features.toolsDesc': 'Descripción de herramientas',
'pages.settings.featuresSection.description':
'Conciencia de pantalla, mensajería y herramientas.',
'pages.settings.featuresSection.title': 'Funciones',
'privacy.dataKind.credentials': 'Credenciales',
'privacy.dataKind.derived': 'Derivado',
@@ -5355,9 +5265,6 @@ const messages: TranslationMap = {
'settings.developerMenu.ai.title': 'Configuración de IA',
'settings.developerMenu.ai.desc':
'Proveedores en la nube, modelos locales de Ollama y enrutamiento por carga de trabajo',
'settings.developerMenu.screenAwareness.title': 'Conciencia de pantalla',
'settings.developerMenu.screenAwareness.desc':
'Permisos de captura de pantalla, política de supervisión y controles de sesión',
'settings.developerMenu.messagingChannels.title': 'Canales de mensajería',
'settings.developerMenu.messagingChannels.desc':
'Configura los modos de autenticación de Telegram/Discord y el enrutamiento de canal predeterminado',
@@ -6022,20 +5929,6 @@ const messages: TranslationMap = {
'settings.modelHealth.modal.apply': 'Aplicar reemplazo',
'settings.modelHealth.tag.cheaper': 'CHEAPER',
'settings.modelHealth.tag.better': 'BETTER',
'settings.screenIntel.permissions.accessibility': 'Accesibilidad',
'settings.screenIntel.permissions.grantHint': 'Sugerencia de permiso',
'settings.screenIntel.permissions.inputMonitoring': 'Monitoreo de entrada',
'settings.screenIntel.permissions.macosAppliesPrivacy': 'macOS aplica privacidad',
'settings.screenIntel.permissions.openInputMonitoring': 'Solicitando…',
'settings.screenIntel.permissions.refreshStatus': 'Actualizando…',
'settings.screenIntel.permissions.refreshing': 'Actualizando…',
'settings.screenIntel.permissions.requestAccessibility': 'Solicitando…',
'settings.screenIntel.permissions.requestScreenRecording': 'Solicitando…',
'settings.screenIntel.permissions.requesting': 'Solicitando…',
'settings.screenIntel.permissions.restartRefresh': 'Reiniciando core…',
'settings.screenIntel.permissions.restartingCore': 'Reiniciando core…',
'settings.screenIntel.permissions.screenRecording': 'Grabación de pantalla',
'settings.screenIntel.permissions.title': 'Permisos',
'skills.card.moreActions': 'Más acciones',
'skills.channelIcon.discord': 'Discord',
'skills.channelIcon.imessage': 'iMessage',
@@ -6300,36 +6193,6 @@ const messages: TranslationMap = {
'skills.resource.tree.templates': 'Plantillas',
'skills.resource.tree.empty': 'Sin recursos incluidos.',
'skills.search.placeholder': 'Marcador de posición',
'skills.setup.screenIntel.activeDesc': 'Descripción activa',
'skills.setup.screenIntel.activeTitle': 'Inteligencia de pantalla está activada',
'skills.setup.screenIntel.advancedSettings': 'Configuración avanzada',
'skills.setup.screenIntel.allGranted': 'Todos los permisos otorgados',
'skills.setup.screenIntel.captureMode': 'Modo de captura',
'skills.setup.screenIntel.captureModeValue': 'Todas las ventanas (configurable después)',
'skills.setup.screenIntel.deniedHint':
'Después de otorgar permisos en Configuración del sistema, haz clic abajo para reiniciar y aplicar los cambios.',
'skills.setup.screenIntel.enableBtn': 'Activando...',
'skills.setup.screenIntel.enableDesc': 's en tu pantalla y añade contexto útil a tu agente',
'skills.setup.screenIntel.enableError': 'No se pudo activar la Inteligencia de pantalla',
'skills.setup.screenIntel.enabling': 'Activando...',
'skills.setup.screenIntel.grant': 'Abriendo...',
'skills.setup.screenIntel.granted': 'Otorgado',
'skills.setup.screenIntel.macosOnly': 'Solo macOS',
'skills.setup.screenIntel.opening': 'Abriendo...',
'skills.setup.screenIntel.panicHotkey': 'Tecla de pánico',
'skills.setup.screenIntel.permAccessibility': 'Accesibilidad',
'skills.setup.screenIntel.permInputMonitoring': 'Monitoreo de entrada',
'skills.setup.screenIntel.permScreenRecording': 'Grabación de pantalla',
'skills.setup.screenIntel.permissionsDesc': 'Descripción de permisos',
'skills.setup.screenIntel.permissionPathLabel': 'macOS aplica la privacidad a:',
'skills.setup.screenIntel.refreshStatus': 'Actualizar estado',
'skills.setup.screenIntel.restartRefresh': 'Reiniciando...',
'skills.setup.screenIntel.restarting': 'Reiniciando...',
'skills.setup.screenIntel.stepEnable': 'Activar el skill',
'skills.setup.screenIntel.stepPermissions': 'Otorgar permisos',
'skills.setup.screenIntel.stepSuccess': 'Listo para usar',
'skills.setup.screenIntel.title': 'Inteligencia de pantalla',
'skills.setup.screenIntel.visionModel': 'Modelo de visión',
'skills.setup.voice.activation': 'Activación',
'skills.setup.voice.activeDescPrefix': 'fn',
'skills.setup.voice.activeDescSuffix': 'fn',
@@ -6491,9 +6354,6 @@ const messages: TranslationMap = {
'devOptions.menuAi': 'Configuración de IA',
'devOptions.menuAiDesc':
'Proveedores de nube, modelos Ollama locales y enrutamiento por carga de trabajo',
'devOptions.menuScreenAware': 'Conciencia de pantalla',
'devOptions.menuScreenAwareDesc':
'Permisos de captura de pantalla, política de monitoreo y controles de sesión',
'devOptions.menuMessaging': 'Canales de mensajería',
'devOptions.menuMessagingDesc':
'Configurar los modos de autenticación Telegram/Discord y el enrutamiento de canales predeterminado',
-140
View File
@@ -9,8 +9,6 @@ const messages: TranslationMap = {
'settings.assistant.desktopCompanionDesc':
'Mode compagnon toujours actif avec raccourci dans la barre système',
'pages.settings.features.desktopCompanion': 'Compagnon de bureau',
'pages.settings.features.desktopCompanionDesc':
"Assistant vocal avec conscience de l'écran: écoute, voit, parle, pointe",
'settings.companion.title': 'Compagnon de bureau',
'settings.companion.session': 'Session',
'settings.companion.activeLabel': 'Actif',
@@ -26,8 +24,6 @@ const messages: TranslationMap = {
'settings.companion.hotkey': 'Raccourci clavier',
'settings.companion.activationMode': "Mode d'activation",
'settings.companion.sessionTtl': 'Durée de vie de la session',
'settings.companion.screenCapture': "Capture d'écran",
'settings.companion.appContext': "Contexte de l'application",
// Share cards (#5006).
'share.button': 'Partager',
'share.modalTitle': 'Partagez ce moment',
@@ -122,8 +118,6 @@ const messages: TranslationMap = {
'connections.header.search': 'Configurez la recherche web pour votre agent.',
'connections.header.wallet':
'Le solde de votre portefeuille local et votre phrase de récupération.',
'connections.header.screen':
'Laissez votre agent voir votre écran pour offrir une aide opportune et contextuelle.',
'connections.header.composioKey':
'Ajoutez une clé API Composio pour débloquer des centaines dintégrations dapplications.',
'connections.llm.apiKeys': 'Clés API et routage',
@@ -784,8 +778,6 @@ const messages: TranslationMap = {
'settings.assistant.backgroundActivity': 'Subconscient',
'settings.assistant.backgroundActivityDesc':
"Contrôler l'activité en arrière-plan de votre assistant",
'settings.assistant.screenAwareness': "Conscience de l'écran",
'settings.assistant.screenAwarenessDesc': "Permettre à l'assistant de voir votre fenêtre active",
'settings.assistant.permissions': 'Autorisations',
'settings.assistant.permissionsDesc':
"Choisissez ce que l'assistant peut faire et où il peut travailler",
@@ -869,7 +861,6 @@ const messages: TranslationMap = {
'settings.notifications.tabs.preferences': 'Préférences',
'settings.notifications.tabs.routing': 'Routage',
'settings.features': 'Fonctionnalités',
'settings.featuresDesc': "Surveillance de l'écran, messagerie et outils",
'settings.aiModels': 'IA & Modèles',
'settings.aiModelsDesc':
'Configuration des modèles IA locaux, téléchargements et fournisseur LLM',
@@ -968,8 +959,6 @@ const messages: TranslationMap = {
'settings.notifications.channelControls': 'Contrôles par canal',
'settings.notifications.channelControlsDesc':
'Configurer les préférences de notifications pour chaque canal',
'settings.features.screenAwareness': "Surveillance de l'écran",
'settings.features.screenAwarenessDesc': "Permettre à l'assistant de voir ta fenêtre active",
'settings.features.messaging': 'Messagerie',
'settings.features.messagingDesc': "Paramètres d'intégration des canaux et de la messagerie",
'settings.features.tools': 'Outils',
@@ -2271,21 +2260,6 @@ const messages: TranslationMap = {
'settings.tools.saveChanges': 'Enregistrer les modifications',
'settings.tools.preferencesSaved': 'Préférences enregistrées',
'settings.tools.saveFailed': "Échec de l'enregistrement des préférences. Réessaie.",
'settings.screenAwareness.mode': 'Mode',
'settings.screenAwareness.allExceptBlacklist': 'Tout sauf la liste noire',
'settings.screenAwareness.whitelistOnly': 'Liste blanche uniquement',
'settings.screenAwareness.screenMonitoring': "Surveillance de l'écran",
'settings.screenAwareness.saveSettings': 'Enregistrer les paramètres',
'settings.screenAwareness.session': 'Session',
'settings.screenAwareness.status': 'État',
'settings.screenAwareness.active': 'Actif',
'settings.screenAwareness.stopped': 'Arrêté',
'settings.screenAwareness.remaining': 'Restant',
'settings.screenAwareness.startSession': 'Démarrer la session',
'settings.screenAwareness.stopSession': 'Arrêter la session',
'settings.screenAwareness.analyzeNow': 'Analyser maintenant',
'settings.screenAwareness.macosOnly':
"La capture d'écran et les contrôles d'autorisation de la surveillance de l'écran sont actuellement pris en charge sur macOS uniquement.",
'connections.comingSoon': 'Bientôt disponible',
'connections.setUp': 'Configurer',
'connections.configured': 'Configuré',
@@ -2652,37 +2626,6 @@ const messages: TranslationMap = {
"Interrupteur principal. Désactivé par défaut: Ollama reste en veille. Quand activé, le résumeur d'arbre, l'intelligence d'écran et l'autocomplétion utilisent toujours le modèle local.",
'localModel.advancedSettings': 'Paramètres avancés',
'localModel.debugTitle': 'Débogage du modèle local',
'screenAwareness.debugTitle': "Débogage de la surveillance de l'écran",
'screenAwareness.debug.debugAndDiagnostics': 'Débogage et diagnostics',
'screenAwareness.debug.collapse': 'Réduire',
'screenAwareness.debug.expand': 'Développer',
'screenAwareness.debug.failedToSave': "Échec de la sauvegarde de l'intelligence écran",
'screenAwareness.debug.policyTitle': "Politique d'intelligence écran",
'screenAwareness.debug.baselineFps': 'FPS de référence',
'screenAwareness.debug.useVisionModel': 'Utiliser le modèle de vision',
'screenAwareness.debug.useVisionModelDesc':
"Envoyer les captures d'écran à un LLM vision pour un contexte plus riche. Désactivé, seul le texte OCR est utilisé avec un LLM texte: plus rapide et sans modèle vision requis.",
'screenAwareness.debug.keepScreenshots': "Conserver les captures d'écran",
'screenAwareness.debug.keepScreenshotsDesc':
"Enregistrer les captures dans l'espace de travail au lieu de les supprimer après traitement",
'screenAwareness.debug.allowlist': "Liste d'autorisation (une règle par ligne)",
'screenAwareness.debug.denylist': 'Liste de refus (une règle par ligne)',
'screenAwareness.debug.saveSettings': "Enregistrer les paramètres d'intelligence écran",
'screenAwareness.debug.sessionStats': 'Statistiques de session',
'screenAwareness.debug.framesEphemeral': 'Trames (éphémères)',
'screenAwareness.debug.panicStop': 'Arrêt de panique',
'screenAwareness.debug.defaultPanicHotkey': 'Cmd+Shift+.',
'screenAwareness.debug.vision': 'Vision',
'screenAwareness.debug.idle': 'inactif',
'screenAwareness.debug.visionQueue': "File d'attente vision",
'screenAwareness.debug.lastVision': 'Dernière vision',
'screenAwareness.debug.notAvailable': 'n/a',
'screenAwareness.debug.visionSummaries': 'Résumés de vision',
'screenAwareness.debug.refreshing': 'Actualisation…',
'screenAwareness.debug.noSummaries': "Aucun résumé pour l'instant.",
'screenAwareness.debug.unknownApp': 'Application inconnue',
'screenAwareness.debug.macosOnly':
'Screen Intelligence V1 est actuellement pris en charge sur macOS uniquement.',
'memory.debugTitle': 'Débogage de la mémoire',
'memory.documents': 'Documents',
'memory.filterByNamespace': 'Filtrer par espace de noms...',
@@ -3956,33 +3899,6 @@ const messages: TranslationMap = {
'intelligence.diagram.imageAlt': "Dernier diagramme d'architecture OpenHuman généré",
'intelligence.diagram.refreshesEvery': 'Actualise toutes les {seconds}s',
'intelligence.memoryText.entityTypePrefix': "Type d'entité",
'intelligence.screenDebug.active': 'Actif',
'intelligence.screenDebug.app': 'Application',
'intelligence.screenDebug.bounds': 'Limites',
'intelligence.screenDebug.captureAlt': 'Résultat du test de capture',
'intelligence.screenDebug.captureFailed': 'Échec',
'intelligence.screenDebug.captureSuccess': 'Succès',
'intelligence.screenDebug.captureTest': 'Test de capture',
'intelligence.screenDebug.capturing': 'Capture en cours',
'intelligence.screenDebug.frames': 'Images',
'intelligence.screenDebug.idle': 'En veille',
'intelligence.screenDebug.lastApp': 'Dernière app',
'intelligence.screenDebug.mode': 'Mode',
'intelligence.screenDebug.permAccessibility': 'Permission accessibilité',
'intelligence.screenDebug.permInput': 'Permission saisie',
'intelligence.screenDebug.permScreen': 'Accessibilité',
'intelligence.screenDebug.permissions': 'Autorisations',
'intelligence.screenDebug.platformNotSupported': 'Plateforme non prise en charge',
'intelligence.screenDebug.recentVisionSummaries': 'Résumés de vision récents',
'intelligence.screenDebug.session': 'Session',
'intelligence.screenDebug.size': 'Taille',
'intelligence.screenDebug.status': 'État',
'intelligence.screenDebug.testCapture': 'Test de capture',
'intelligence.screenDebug.time': 'Temps',
'intelligence.screenDebug.title': 'Titre',
'intelligence.screenDebug.unknown': 'Inconnu',
'intelligence.screenDebug.visionQueue': 'File de vision',
'intelligence.screenDebug.visionState': 'État de la vision',
'intelligence.tasks.activeBoardOne': '1 tableau actif dans les conversations',
'intelligence.tasks.activeBoardOther': '{count} tableaux actifs dans les conversations',
'intelligence.tasks.empty': "Aucun tableau de tâches agent pour l'instant",
@@ -4579,7 +4495,6 @@ const messages: TranslationMap = {
'overlay.ariaVoiceActive': 'Saisie vocale active',
'overlay.companion.error': 'Erreur',
'overlay.companion.listening': 'À l’écoute…',
'overlay.companion.pointing': 'En train de pointer…',
'overlay.companion.speaking': 'En train de parler…',
'overlay.companion.thinking': 'Réflexion…',
'overlay.orbTitle': 'Glisse pour déplacer · Double-clique pour réinitialiser la position',
@@ -4611,12 +4526,8 @@ const messages: TranslationMap = {
'pages.settings.features.messagingChannelsDesc': 'Description des canaux de messagerie',
'pages.settings.features.notifications': 'Notifications',
'pages.settings.features.notificationsDesc': 'Description des notifications',
'pages.settings.features.screenAwareness': "Surveillance de l'écran",
'pages.settings.features.screenAwarenessDesc':
'Laissez votre agent voir votre écran pour offrir une aide opportune et contextuelle.',
'pages.settings.features.tools': 'Outils',
'pages.settings.features.toolsDesc': 'Description des outils',
'pages.settings.featuresSection.description': "Surveillance de l'écran, messagerie et outils.",
'pages.settings.featuresSection.title': 'Fonctionnalités',
'privacy.dataKind.credentials': 'Identifiants',
'privacy.dataKind.derived': 'Dérivé',
@@ -5387,9 +5298,6 @@ const messages: TranslationMap = {
'settings.developerMenu.ai.title': 'Configuration IA',
'settings.developerMenu.ai.desc':
'Fournisseurs cloud, modèles Ollama locaux et routage par charge de travail',
'settings.developerMenu.screenAwareness.title': "Conscience de l'écran",
'settings.developerMenu.screenAwareness.desc':
"Autorisations de capture d'écran, politique de surveillance et contrôles de session",
'settings.developerMenu.messagingChannels.title': 'Canaux de messagerie',
'settings.developerMenu.messagingChannels.desc':
"Configure les modes d'authentification Telegram/Discord et le routage de canal par défaut",
@@ -6054,20 +5962,6 @@ const messages: TranslationMap = {
'settings.modelHealth.modal.apply': 'Appliquer le remplacement',
'settings.modelHealth.tag.cheaper': 'CHEAPER',
'settings.modelHealth.tag.better': 'BETTER',
'settings.screenIntel.permissions.accessibility': 'Accessibilité',
'settings.screenIntel.permissions.grantHint': "Indice d'autorisation",
'settings.screenIntel.permissions.inputMonitoring': 'Surveillance des entrées',
'settings.screenIntel.permissions.macosAppliesPrivacy': 'macOS applique la confidentialité',
'settings.screenIntel.permissions.openInputMonitoring': 'Demande en cours…',
'settings.screenIntel.permissions.refreshStatus': 'Actualisation…',
'settings.screenIntel.permissions.refreshing': 'Actualisation…',
'settings.screenIntel.permissions.requestAccessibility': 'Demande en cours…',
'settings.screenIntel.permissions.requestScreenRecording': 'Demande en cours…',
'settings.screenIntel.permissions.requesting': 'Demande en cours…',
'settings.screenIntel.permissions.restartRefresh': 'Redémarrage du core…',
'settings.screenIntel.permissions.restartingCore': 'Redémarrage du core…',
'settings.screenIntel.permissions.screenRecording': "Enregistrement d'écran",
'settings.screenIntel.permissions.title': 'Autorisations',
'skills.card.moreActions': "Plus d'actions",
'skills.channelIcon.discord': 'Discord',
'skills.channelIcon.imessage': 'iMessage',
@@ -6332,37 +6226,6 @@ const messages: TranslationMap = {
'skills.resource.tree.templates': 'Modèles',
'skills.resource.tree.empty': 'Aucune ressource groupée.',
'skills.search.placeholder': 'Espace réservé',
'skills.setup.screenIntel.activeDesc': 'Description active',
'skills.setup.screenIntel.activeTitle': "L'intelligence d'écran est activée",
'skills.setup.screenIntel.advancedSettings': 'Paramètres avancés',
'skills.setup.screenIntel.allGranted': 'Toutes les permissions accordées',
'skills.setup.screenIntel.captureMode': 'Mode de capture',
'skills.setup.screenIntel.captureModeValue': 'Toutes les fenêtres (configurable plus tard)',
'skills.setup.screenIntel.deniedHint':
'Après avoir accordé les permissions dans les Paramètres système, clique ci-dessous pour redémarrer et prendre en compte les modifications.',
'skills.setup.screenIntel.enableBtn': 'Activation…',
'skills.setup.screenIntel.enableDesc':
's sur ton écran et injecte du contexte utile dans ton agent',
'skills.setup.screenIntel.enableError': "Échec de l'activation de l'intelligence d'écran",
'skills.setup.screenIntel.enabling': 'Activation…',
'skills.setup.screenIntel.grant': 'Ouverture…',
'skills.setup.screenIntel.granted': 'Accordé',
'skills.setup.screenIntel.macosOnly': 'macOS uniquement',
'skills.setup.screenIntel.opening': 'Ouverture…',
'skills.setup.screenIntel.panicHotkey': 'Raccourci de panique',
'skills.setup.screenIntel.permAccessibility': 'Accessibilité',
'skills.setup.screenIntel.permInputMonitoring': 'Surveillance des entrées',
'skills.setup.screenIntel.permScreenRecording': "Enregistrement d'écran",
'skills.setup.screenIntel.permissionsDesc': 'Description des permissions',
'skills.setup.screenIntel.permissionPathLabel': 'macOS applique la confidentialité à :',
'skills.setup.screenIntel.refreshStatus': 'Actualiser le statut',
'skills.setup.screenIntel.restartRefresh': 'Redémarrage…',
'skills.setup.screenIntel.restarting': 'Redémarrage…',
'skills.setup.screenIntel.stepEnable': 'Activer la compétence',
'skills.setup.screenIntel.stepPermissions': 'Accorder les permissions',
'skills.setup.screenIntel.stepSuccess': "Prêt à l'emploi",
'skills.setup.screenIntel.title': "Intelligence d'écran",
'skills.setup.screenIntel.visionModel': 'Modèle de vision',
'skills.setup.voice.activation': 'Activation',
'skills.setup.voice.activeDescPrefix': 'Fn',
'skills.setup.voice.activeDescSuffix': 'Fn',
@@ -6524,9 +6387,6 @@ const messages: TranslationMap = {
'devOptions.menuAi': "Configuration de l'IA",
'devOptions.menuAiDesc':
'Fournisseurs de cloud, modèles Ollama locaux et routage par charge de travail',
'devOptions.menuScreenAware': "Connaissance de l'écran",
'devOptions.menuScreenAwareDesc':
"Autorisations de capture d'écran, stratégie de surveillance et contrôles de session",
'devOptions.menuMessaging': 'Canaux de messagerie',
'devOptions.menuMessagingDesc':
"Configurer les modes d'authentification Telegram/Discord et le routage des canaux par défaut",
+1 -138
View File
@@ -7,8 +7,6 @@ const messages: TranslationMap = {
'settings.assistant.desktopCompanion': 'डेस्कटॉप साथी',
'settings.assistant.desktopCompanionDesc': 'सिस्टम ट्रे शॉर्टकट के साथ हमेशा-चालू साथी मोड',
'pages.settings.features.desktopCompanion': 'डेस्कटॉप कंपैनियन',
'pages.settings.features.desktopCompanionDesc':
'स्क्रीन जागरूकता के साथ वॉयस सहायक: सुनता है, देखता है, बोलता है, इशारा करता है',
'settings.companion.title': 'डेस्कटॉप साथी',
'settings.companion.session': 'सत्र',
'settings.companion.activeLabel': 'सक्रिय',
@@ -24,8 +22,6 @@ const messages: TranslationMap = {
'settings.companion.hotkey': 'हॉटकी',
'settings.companion.activationMode': 'सक्रियण मोड',
'settings.companion.sessionTtl': 'सत्र टीटीएल',
'settings.companion.screenCapture': 'स्क्रीन कैप्चर',
'settings.companion.appContext': 'ऐप संदर्भ',
// Share cards (#5006).
'share.button': 'साझा करें',
'share.modalTitle': 'इस पल को साझा करें',
@@ -111,8 +107,6 @@ const messages: TranslationMap = {
'connections.header.embeddings': 'मेमोरी खोज के लिए उपयोग किया जाने वाला एम्बेडिंग मॉडल चुनें।',
'connections.header.search': 'अपने एजेंट के लिए वेब खोज कॉन्फ़िगर करें।',
'connections.header.wallet': 'आपके स्थानीय वॉलेट का बैलेंस और रिकवरी वाक्यांश।',
'connections.header.screen':
'समय पर, संदर्भ के अनुरूप मदद देने के लिए अपने एजेंट को अपनी स्क्रीन देखने दें।',
'connections.header.composioKey':
'सैकड़ों ऐप एकीकरण अनलॉक करने के लिए एक Composio API कुंजी जोड़ें।',
'connections.llm.apiKeys': 'API कुंजियाँ और रूटिंग',
@@ -760,8 +754,6 @@ const messages: TranslationMap = {
'settings.assistant.backgroundActivity': 'सबकॉन्शस',
'settings.assistant.backgroundActivityDesc':
'नियंत्रित करें कि आपका सहायक पृष्ठभूमि में कितना सक्रिय काम करे',
'settings.assistant.screenAwareness': 'स्क्रीन जागरूकता',
'settings.assistant.screenAwarenessDesc': 'सहायक को आपकी सक्रिय विंडो देखने दें',
'settings.assistant.permissions': 'अनुमतियाँ',
'settings.assistant.permissionsDesc': 'चुनें कि सहायक क्या कर सकता है और कहाँ काम कर सकता है',
'settings.privacySecurity.privacy': 'गोपनीयता',
@@ -841,7 +833,6 @@ const messages: TranslationMap = {
'settings.notifications.tabs.preferences': 'प्राथमिकताएँ',
'settings.notifications.tabs.routing': 'रूटिंग',
'settings.features': 'फीचर्स',
'settings.featuresDesc': 'स्क्रीन अवेयरनेस, मैसेजिंग और टूल्स',
'settings.aiModels': 'AI और मॉडल्स',
'settings.aiModelsDesc': 'लोकल AI मॉडल सेटअप, डाउनलोड और LLM प्रोवाइडर',
'settings.ai': 'AI कॉन्फिगरेशन',
@@ -933,8 +924,6 @@ const messages: TranslationMap = {
'settings.notifications.doNotDisturbDesc': 'तय समय के लिए सभी नोटिफिकेशन रोकें',
'settings.notifications.channelControls': 'चैनल-वाइज़ कंट्रोल',
'settings.notifications.channelControlsDesc': 'हर चैनल के लिए नोटिफिकेशन प्रेफरेंस सेट करें',
'settings.features.screenAwareness': 'स्क्रीन अवेयरनेस',
'settings.features.screenAwarenessDesc': 'असिस्टेंट को आपकी एक्टिव विंडो देखने दें',
'settings.features.messaging': 'मैसेजिंग',
'settings.features.messagingDesc': 'चैनल और मैसेजिंग इंटीग्रेशन सेटिंग्स',
'settings.features.tools': 'टूल्स',
@@ -2203,21 +2192,6 @@ const messages: TranslationMap = {
'settings.tools.saveChanges': 'बदलाव सेव करें',
'settings.tools.preferencesSaved': 'प्रेफरेंस सेव हो गई',
'settings.tools.saveFailed': 'प्रेफरेंस सेव नहीं हो पाई। दोबारा कोशिश करें।',
'settings.screenAwareness.mode': 'मोड',
'settings.screenAwareness.allExceptBlacklist': 'ब्लैकलिस्ट छोड़कर सब',
'settings.screenAwareness.whitelistOnly': 'केवल व्हाइटलिस्ट',
'settings.screenAwareness.screenMonitoring': 'स्क्रीन मॉनिटरिंग',
'settings.screenAwareness.saveSettings': 'सेटिंग्स सेव करें',
'settings.screenAwareness.session': 'सेशन',
'settings.screenAwareness.status': 'स्टेटस',
'settings.screenAwareness.active': 'एक्टिव',
'settings.screenAwareness.stopped': 'रुका हुआ',
'settings.screenAwareness.remaining': 'बचा हुआ',
'settings.screenAwareness.startSession': 'सेशन शुरू करें',
'settings.screenAwareness.stopSession': 'सेशन रोकें',
'settings.screenAwareness.analyzeNow': 'अभी एनालाइज़ करें',
'settings.screenAwareness.macosOnly':
'स्क्रीन अवेयरनेस डेस्कटॉप कैप्चर और परमिशन कंट्रोल अभी केवल macOS पर सपोर्ट हैं।',
'connections.comingSoon': 'जल्द आ रहा है',
'connections.setUp': 'सेट अप करें',
'connections.configured': 'कॉन्फिगर हो गया',
@@ -2574,39 +2548,9 @@ const messages: TranslationMap = {
'चुनें कि कौन से सबसिस्टम लोकल मॉडल पर चलें। बंद होने पर क्लाउड इस्तेमाल होगा।',
'localModel.enableRuntime': 'लोकल AI रनटाइम चालू करें',
'localModel.enableRuntimeDesc':
'मास्टर स्विच। डिफ़ॉल्ट रूप से बंद: Ollama आइडल रहता है। चालू होने पर tree summarizer, screen intelligence और autocomplete हमेशा लोकल मॉडल इस्तेमाल करते हैं।',
'मास्टर स्विच। डिफ़ॉल्ट रूप से बंद: Ollama आइडल रहता है। चालू होने पर tree summarizer और autocomplete हमेशा लोकल मॉडल इस्तेमाल करते हैं।',
'localModel.advancedSettings': 'एडवांस्ड सेटिंग्स',
'localModel.debugTitle': 'लोकल मॉडल डिबग',
'screenAwareness.debugTitle': 'स्क्रीन अवेयरनेस डिबग',
'screenAwareness.debug.debugAndDiagnostics': 'डिबग और डायग्नोस्टिक्स',
'screenAwareness.debug.collapse': 'पतन',
'screenAwareness.debug.expand': 'विस्तार करें',
'screenAwareness.debug.failedToSave': 'स्क्रीन इंटेलिजेंस सहेजने में विफल',
'screenAwareness.debug.policyTitle': 'स्क्रीन इंटेलिजेंस नीति',
'screenAwareness.debug.baselineFps': 'बेसलाइन एफपीएस',
'screenAwareness.debug.useVisionModel': 'विज़न मॉडल का उपयोग करें',
'screenAwareness.debug.useVisionModelDesc':
'समृद्ध संदर्भ के लिए स्क्रीनशॉट को विज़न LLM को भेजें। बंद होने पर केवल OCR टेक्स्ट का उपयोग टेक्स्ट LLM के साथ किया जाता है: तेज़ और विज़न मॉडल की आवश्यकता नहीं।',
'screenAwareness.debug.keepScreenshots': 'स्क्रीनशॉट रखें',
'screenAwareness.debug.keepScreenshotsDesc':
'प्रोसेसिंग के बाद हटाने की बजाय कैप्चर किए गए स्क्रीनशॉट वर्कस्पेस में सहेजें',
'screenAwareness.debug.allowlist': 'अनुमति सूची (प्रति पंक्ति एक नियम)',
'screenAwareness.debug.denylist': 'अस्वीकृत सूची (प्रति पंक्ति एक नियम)',
'screenAwareness.debug.saveSettings': 'स्क्रीन इंटेलिजेंस सेटिंग्स सहेजें',
'screenAwareness.debug.sessionStats': 'सत्र आँकड़े',
'screenAwareness.debug.framesEphemeral': 'फ़्रेम (क्षणिक)',
'screenAwareness.debug.panicStop': 'घबराना बंद करो',
'screenAwareness.debug.defaultPanicHotkey': 'सीएमडी+शिफ्ट+.',
'screenAwareness.debug.vision': 'दृष्टि',
'screenAwareness.debug.idle': 'निष्क्रिय',
'screenAwareness.debug.visionQueue': 'दृष्टि कतार',
'screenAwareness.debug.lastVision': 'अंतिम दर्शन',
'screenAwareness.debug.notAvailable': 'एन/ए',
'screenAwareness.debug.visionSummaries': 'दृष्टि सारांश',
'screenAwareness.debug.refreshing': 'ताज़ा...',
'screenAwareness.debug.noSummaries': 'अभी तक कोई सारांश नहीं.',
'screenAwareness.debug.unknownApp': 'अज्ञात ऐप',
'screenAwareness.debug.macosOnly': 'स्क्रीन इंटेलिजेंस V1 वर्तमान में केवल macOS पर समर्थित है।',
'memory.debugTitle': 'मेमोरी डिबग',
'memory.documents': 'दस्तावेज़',
'memory.filterByNamespace': 'नामस्थान के अनुसार फ़िल्टर करें...',
@@ -3862,33 +3806,6 @@ const messages: TranslationMap = {
'intelligence.diagram.imageAlt': 'OpenHuman का नवीनतम जेनरेटेड आर्किटेक्चर डायग्राम',
'intelligence.diagram.refreshesEvery': 'हर {seconds}s पर रीफ्रेश होता है',
'intelligence.memoryText.entityTypePrefix': 'इकाई प्रकार',
'intelligence.screenDebug.active': 'एक्टिव',
'intelligence.screenDebug.app': 'ऐप',
'intelligence.screenDebug.bounds': 'बाउंड्स',
'intelligence.screenDebug.captureAlt': 'कैप्चर टेस्ट रिज़ल्ट',
'intelligence.screenDebug.captureFailed': 'विफल',
'intelligence.screenDebug.captureSuccess': 'सफल',
'intelligence.screenDebug.captureTest': 'कैप्चर टेस्ट',
'intelligence.screenDebug.capturing': 'कैप्चर हो रहा है',
'intelligence.screenDebug.frames': 'फ्रेम',
'intelligence.screenDebug.idle': 'आइडल',
'intelligence.screenDebug.lastApp': 'आखिरी ऐप',
'intelligence.screenDebug.mode': 'मोड',
'intelligence.screenDebug.permAccessibility': 'Accessibility परमिशन',
'intelligence.screenDebug.permInput': 'इनपुट परमिशन',
'intelligence.screenDebug.permScreen': 'स्क्रीन रिकॉर्डिंग अनुमति',
'intelligence.screenDebug.permissions': 'परमिशन',
'intelligence.screenDebug.platformNotSupported': 'प्लेटफॉर्म सपोर्टेड नहीं',
'intelligence.screenDebug.recentVisionSummaries': 'हाल के विज़न समरीज़',
'intelligence.screenDebug.session': 'सेशन',
'intelligence.screenDebug.size': 'साइज़',
'intelligence.screenDebug.status': 'स्टेटस',
'intelligence.screenDebug.testCapture': 'टेस्ट कैप्चर',
'intelligence.screenDebug.time': 'समय',
'intelligence.screenDebug.title': 'टाइटल',
'intelligence.screenDebug.unknown': 'अज्ञात',
'intelligence.screenDebug.visionQueue': 'विज़न क्यू',
'intelligence.screenDebug.visionState': 'विज़न स्टेट',
'intelligence.tasks.activeBoardOne': 'बातचीतों में 1 सक्रिय बोर्ड',
'intelligence.tasks.activeBoardOther': 'बातचीतों में {count} सक्रिय बोर्ड',
'intelligence.tasks.empty': 'अभी कोई एजेंट टास्क बोर्ड नहीं',
@@ -4472,7 +4389,6 @@ const messages: TranslationMap = {
'overlay.ariaVoiceActive': 'वॉइस इनपुट एक्टिव',
'overlay.companion.error': 'त्रुटि',
'overlay.companion.listening': 'सुन रहा है…',
'overlay.companion.pointing': 'इशारा कर रहा है…',
'overlay.companion.speaking': 'बोल रहा है…',
'overlay.companion.thinking': 'सोच रहा है…',
'overlay.orbTitle': 'मूव करने के लिए खींचें · पोज़िशन रीसेट के लिए डबल-क्लिक करें',
@@ -4502,12 +4418,8 @@ const messages: TranslationMap = {
'pages.settings.features.messagingChannelsDesc': 'मैसेजिंग चैनल विवरण',
'pages.settings.features.notifications': 'नोटिफिकेशन',
'pages.settings.features.notificationsDesc': 'नोटिफ़िकेशन विवरण',
'pages.settings.features.screenAwareness': 'स्क्रीन अवेयरनेस',
'pages.settings.features.screenAwarenessDesc':
'समय पर, संदर्भ के अनुरूप मदद देने के लिए अपने एजेंट को अपनी स्क्रीन देखने दें।',
'pages.settings.features.tools': 'टूल्स',
'pages.settings.features.toolsDesc': 'टूल्स विवरण',
'pages.settings.featuresSection.description': 'स्क्रीन अवेयरनेस, मैसेजिंग और टूल्स।',
'pages.settings.featuresSection.title': 'फीचर्स',
'privacy.dataKind.credentials': 'क्रेडेंशियल',
'privacy.dataKind.derived': 'डिराइव्ड',
@@ -5263,9 +5175,6 @@ const messages: TranslationMap = {
'settings.localModel.status.working': 'काम हो रहा है...',
'settings.developerMenu.ai.title': 'AI कॉन्फ़िगरेशन',
'settings.developerMenu.ai.desc': 'क्लाउड प्रदाता, स्थानीय Ollama मॉडल, और प्रति-वर्कलोड रूटिंग',
'settings.developerMenu.screenAwareness.title': 'स्क्रीन अवेयरनेस',
'settings.developerMenu.screenAwareness.desc':
'स्क्रीन कैप्चर अनुमतियां, मॉनिटरिंग नीति, और सत्र नियंत्रण',
'settings.developerMenu.messagingChannels.title': 'मैसेजिंग चैनल',
'settings.developerMenu.messagingChannels.desc':
'Telegram/Discord प्रमाणीकरण मोड और डिफ़ॉल्ट चैनल रूटिंग कॉन्फ़िगर करें',
@@ -5908,20 +5817,6 @@ const messages: TranslationMap = {
'settings.modelHealth.modal.apply': 'प्रतिस्थापन लागू करें',
'settings.modelHealth.tag.cheaper': 'CHEAPER',
'settings.modelHealth.tag.better': 'BETTER',
'settings.screenIntel.permissions.accessibility': 'एक्सेसिबिलिटी',
'settings.screenIntel.permissions.grantHint': 'स्वीकृति संकेत',
'settings.screenIntel.permissions.inputMonitoring': 'इनपुट मॉनिटरिंग',
'settings.screenIntel.permissions.macosAppliesPrivacy': 'macOS गोपनीयता लागू करता है',
'settings.screenIntel.permissions.openInputMonitoring': 'रिक्वेस्ट हो रही है…',
'settings.screenIntel.permissions.refreshStatus': 'रिफ्रेश हो रहा है…',
'settings.screenIntel.permissions.refreshing': 'रिफ्रेश हो रहा है…',
'settings.screenIntel.permissions.requestAccessibility': 'रिक्वेस्ट हो रही है…',
'settings.screenIntel.permissions.requestScreenRecording': 'रिक्वेस्ट हो रही है…',
'settings.screenIntel.permissions.requesting': 'रिक्वेस्ट हो रही है…',
'settings.screenIntel.permissions.restartRefresh': 'कोर रीस्टार्ट हो रहा है…',
'settings.screenIntel.permissions.restartingCore': 'कोर रीस्टार्ट हो रहा है…',
'settings.screenIntel.permissions.screenRecording': 'स्क्रीन रिकॉर्डिंग',
'settings.screenIntel.permissions.title': 'परमिशन',
'skills.card.moreActions': 'और एक्शन',
'skills.channelIcon.discord': 'Discord',
'skills.channelIcon.imessage': 'iMessage',
@@ -6183,36 +6078,6 @@ const messages: TranslationMap = {
'skills.resource.tree.templates': 'टेम्पलेट',
'skills.resource.tree.empty': 'कोई बंडल्ड रिसोर्स नहीं।',
'skills.search.placeholder': 'प्लेसहोल्डर',
'skills.setup.screenIntel.activeDesc': 'सक्रिय विवरण',
'skills.setup.screenIntel.activeTitle': 'Screen Intelligence चालू है',
'skills.setup.screenIntel.advancedSettings': 'एडवांस्ड सेटिंग्स',
'skills.setup.screenIntel.allGranted': 'सभी परमिशन मिल गईं',
'skills.setup.screenIntel.captureMode': 'कैप्चर मोड',
'skills.setup.screenIntel.captureModeValue': 'सभी विंडो (बाद में बदल सकते हैं)',
'skills.setup.screenIntel.deniedHint':
'System Settings में परमिशन देने के बाद, बदलाव लागू करने के लिए नीचे क्लिक करके रीस्टार्ट करें।',
'skills.setup.screenIntel.enableBtn': 'चालू हो रहा है...',
'skills.setup.screenIntel.enableDesc': 'आपकी स्क्रीन पर और एजेंट को उपयोगी कॉन्टेक्स्ट दें',
'skills.setup.screenIntel.enableError': 'Screen Intelligence चालू नहीं हो पाई',
'skills.setup.screenIntel.enabling': 'चालू हो रहा है...',
'skills.setup.screenIntel.grant': 'खुल रहा है...',
'skills.setup.screenIntel.granted': 'मिल गई',
'skills.setup.screenIntel.macosOnly': 'केवल macOS',
'skills.setup.screenIntel.opening': 'खुल रहा है...',
'skills.setup.screenIntel.panicHotkey': 'पैनिक हॉटकी',
'skills.setup.screenIntel.permAccessibility': 'एक्सेसिबिलिटी',
'skills.setup.screenIntel.permInputMonitoring': 'इनपुट मॉनिटरिंग',
'skills.setup.screenIntel.permScreenRecording': 'स्क्रीन रिकॉर्डिंग',
'skills.setup.screenIntel.permissionsDesc': 'अनुमतियाँ विवरण',
'skills.setup.screenIntel.permissionPathLabel': 'macOS निम्नलिखित पर गोपनीयता लागू करता है:',
'skills.setup.screenIntel.refreshStatus': 'स्टेटस रिफ्रेश करें',
'skills.setup.screenIntel.restartRefresh': 'रीस्टार्ट हो रहा है...',
'skills.setup.screenIntel.restarting': 'रीस्टार्ट हो रहा है...',
'skills.setup.screenIntel.stepEnable': 'स्किल चालू करें',
'skills.setup.screenIntel.stepPermissions': 'परमिशन दें',
'skills.setup.screenIntel.stepSuccess': 'तैयार है',
'skills.setup.screenIntel.title': 'स्क्रीन इंटेलिजेंस',
'skills.setup.screenIntel.visionModel': 'विज़न मॉडल',
'skills.setup.voice.activation': 'एक्टिवेशन',
'skills.setup.voice.activeDescPrefix': 'एफ.एन',
'skills.setup.voice.activeDescSuffix': 'एफ.एन',
@@ -6372,8 +6237,6 @@ const messages: TranslationMap = {
'सेटअप निर्देशों के लिए, फिर आपका रनटाइम पहुँच योग्य होने के बाद पुनः प्रयास करें।',
'devOptions.menuAi': 'एआई कॉन्फ़िगरेशन',
'devOptions.menuAiDesc': 'क्लाउड प्रदाता, स्थानीय Ollama मॉडल, और प्रति-वर्कलोड रूटिंग',
'devOptions.menuScreenAware': 'स्क्रीन जागरूकता',
'devOptions.menuScreenAwareDesc': 'स्क्रीन कैप्चर अनुमतियाँ, निगरानी नीति और सत्र नियंत्रण',
'devOptions.menuMessaging': 'मैसेजिंग चैनल',
'devOptions.menuMessagingDesc':
'Telegram/Discord प्रमाणीकरण मोड और डिफ़ॉल्ट चैनल रूटिंग कॉन्फ़िगर करें',
-138
View File
@@ -9,8 +9,6 @@ const messages: TranslationMap = {
'settings.assistant.desktopCompanionDesc':
'Mode pendamping selalu aktif dengan pintasan baki sistem',
'pages.settings.features.desktopCompanion': 'Pendamping Desktop',
'pages.settings.features.desktopCompanionDesc':
'Asisten suara dengan kesadaran layar: mendengar, melihat, berbicara, menunjuk',
'settings.companion.title': 'Pendamping Desktop',
'settings.companion.session': 'Sesi',
'settings.companion.activeLabel': 'Aktif',
@@ -26,8 +24,6 @@ const messages: TranslationMap = {
'settings.companion.hotkey': 'Tombol Pintas',
'settings.companion.activationMode': 'Mode Aktivasi',
'settings.companion.sessionTtl': 'TTL Sesi',
'settings.companion.screenCapture': 'Tangkapan Layar',
'settings.companion.appContext': 'Konteks Aplikasi',
// Share cards (#5006).
'share.button': 'Bagikan',
'share.modalTitle': 'Bagikan momen ini',
@@ -116,8 +112,6 @@ const messages: TranslationMap = {
'connections.header.embeddings': 'Pilih model embedding yang digunakan untuk pencarian memori.',
'connections.header.search': 'Konfigurasikan pencarian web untuk agen Anda.',
'connections.header.wallet': 'Saldo dompet lokal Anda dan frasa pemulihan Anda.',
'connections.header.screen':
'Biarkan agen Anda melihat layar Anda untuk memberikan bantuan tepat waktu dan sesuai konteks.',
'connections.header.composioKey':
'Tambahkan kunci API Composio untuk membuka ratusan integrasi aplikasi.',
'connections.llm.apiKeys': 'Kunci API & perutean',
@@ -767,8 +761,6 @@ const messages: TranslationMap = {
'settings.assistant.backgroundActivity': 'Bawah sadar',
'settings.assistant.backgroundActivityDesc':
'Kontrol seberapa aktif asisten Anda bekerja di latar belakang',
'settings.assistant.screenAwareness': 'Kesadaran layar',
'settings.assistant.screenAwarenessDesc': 'Biarkan asisten melihat jendela aktif Anda',
'settings.assistant.permissions': 'Izin',
'settings.assistant.permissionsDesc':
'Pilih apa yang dapat dilakukan asisten dan di mana ia dapat bekerja',
@@ -851,7 +843,6 @@ const messages: TranslationMap = {
'settings.notifications.tabs.preferences': 'Preferensi',
'settings.notifications.tabs.routing': 'Perutean',
'settings.features': 'Fitur',
'settings.featuresDesc': 'Kesadaran layar, pesan, dan alat',
'settings.aiModels': 'AI & Model',
'settings.aiModelsDesc': 'Pengaturan model AI lokal, unduhan, dan penyedia LLM',
'settings.ai': 'AI',
@@ -944,8 +935,6 @@ const messages: TranslationMap = {
'settings.notifications.doNotDisturbDesc': 'Jeda semua notifikasi selama periode tertentu',
'settings.notifications.channelControls': 'Kontrol Per Kanal',
'settings.notifications.channelControlsDesc': 'Atur preferensi notifikasi untuk setiap kanal',
'settings.features.screenAwareness': 'Kesadaran Layar',
'settings.features.screenAwarenessDesc': 'Izinkan asisten melihat jendela aktif Anda',
'settings.features.messaging': 'Pesan',
'settings.features.messagingDesc': 'Pengaturan kanal dan integrasi pesan',
'settings.features.tools': 'Alat',
@@ -2219,21 +2208,6 @@ const messages: TranslationMap = {
'settings.tools.saveChanges': 'Simpan Perubahan',
'settings.tools.preferencesSaved': 'Preferensi tersimpan',
'settings.tools.saveFailed': 'Gagal menyimpan preferensi. Coba lagi.',
'settings.screenAwareness.mode': 'Mode',
'settings.screenAwareness.allExceptBlacklist': 'Semua Kecuali Blacklist',
'settings.screenAwareness.whitelistOnly': 'Whitelist Saja',
'settings.screenAwareness.screenMonitoring': 'Pemantauan Layar',
'settings.screenAwareness.saveSettings': 'Simpan Pengaturan',
'settings.screenAwareness.session': 'Sesi',
'settings.screenAwareness.status': 'Status',
'settings.screenAwareness.active': 'Aktif',
'settings.screenAwareness.stopped': 'Berhenti',
'settings.screenAwareness.remaining': 'Tersisa',
'settings.screenAwareness.startSession': 'Mulai Sesi',
'settings.screenAwareness.stopSession': 'Hentikan Sesi',
'settings.screenAwareness.analyzeNow': 'Analisis Sekarang',
'settings.screenAwareness.macosOnly':
'Tangkapan layar desktop dan kontrol izin Screen Awareness saat ini hanya didukung di macOS.',
'connections.comingSoon': 'Segera hadir',
'connections.setUp': 'Atur',
'connections.configured': 'Dikonfigurasi',
@@ -2591,36 +2565,6 @@ const messages: TranslationMap = {
'Sakelar utama. Nonaktif secara default; Ollama tetap idle. Saat aktif, peringkas tree, kecerdasan layar, dan autocomplete selalu memakai model lokal.',
'localModel.advancedSettings': 'Pengaturan lanjutan',
'localModel.debugTitle': 'Debug Model Lokal',
'screenAwareness.debugTitle': 'Debug Kesadaran Layar',
'screenAwareness.debug.debugAndDiagnostics': 'Debug & Diagnostik',
'screenAwareness.debug.collapse': 'Ciutkan',
'screenAwareness.debug.expand': 'Perluas',
'screenAwareness.debug.failedToSave': 'Gagal menyimpan kecerdasan layar',
'screenAwareness.debug.policyTitle': 'Kebijakan Intelijen Layar',
'screenAwareness.debug.baselineFps': 'FPS Dasar',
'screenAwareness.debug.useVisionModel': 'Gunakan Model Visi',
'screenAwareness.debug.useVisionModelDesc':
'Kirim tangkapan layar ke LLM vision untuk konteks yang lebih kaya. Jika dinonaktifkan, hanya teks OCR yang digunakan dengan LLM teks: lebih cepat dan tidak memerlukan model vision.',
'screenAwareness.debug.keepScreenshots': 'Simpan Tangkapan Layar',
'screenAwareness.debug.keepScreenshotsDesc':
'Simpan tangkapan layar yang diambil ke workspace alih-alih menghapusnya setelah diproses',
'screenAwareness.debug.allowlist': 'Daftar yang diizinkan (satu aturan per baris)',
'screenAwareness.debug.denylist': 'Daftar Tolak (satu aturan per baris)',
'screenAwareness.debug.saveSettings': 'Simpan Pengaturan Kecerdasan Layar',
'screenAwareness.debug.sessionStats': 'Statistik Sesi',
'screenAwareness.debug.framesEphemeral': 'Bingkai (sementara)',
'screenAwareness.debug.panicStop': 'Berhenti panik',
'screenAwareness.debug.defaultPanicHotkey': 'Cmd+Shift+.',
'screenAwareness.debug.vision': 'Vision',
'screenAwareness.debug.idle': 'idle',
'screenAwareness.debug.visionQueue': 'Antrean vision',
'screenAwareness.debug.lastVision': 'Vision terakhir',
'screenAwareness.debug.notAvailable': 'n/a',
'screenAwareness.debug.visionSummaries': 'Ringkasan Vision',
'screenAwareness.debug.refreshing': 'Menyegarkan…',
'screenAwareness.debug.noSummaries': 'Belum ada ringkasan.',
'screenAwareness.debug.unknownApp': 'Aplikasi Tidak Dikenal',
'screenAwareness.debug.macosOnly': 'Screen Intelligence V1 saat ini hanya didukung di macOS.',
'memory.debugTitle': 'Debug Memori',
'memory.documents': 'Dokumen',
'memory.filterByNamespace': 'Filter berdasarkan namespace...',
@@ -3878,33 +3822,6 @@ const messages: TranslationMap = {
'intelligence.diagram.imageAlt': 'Diagram arsitektur OpenHuman terbaru yang dihasilkan',
'intelligence.diagram.refreshesEvery': 'Diperbarui setiap {seconds}d',
'intelligence.memoryText.entityTypePrefix': 'Tipe entitas',
'intelligence.screenDebug.active': 'Aktif',
'intelligence.screenDebug.app': 'Aplikasi',
'intelligence.screenDebug.bounds': 'Batas',
'intelligence.screenDebug.captureAlt': 'Hasil tes tangkapan',
'intelligence.screenDebug.captureFailed': 'Gagal',
'intelligence.screenDebug.captureSuccess': 'Berhasil',
'intelligence.screenDebug.captureTest': 'Tes tangkapan',
'intelligence.screenDebug.capturing': 'Menangkap',
'intelligence.screenDebug.frames': 'Frame',
'intelligence.screenDebug.idle': 'Siaga',
'intelligence.screenDebug.lastApp': 'Aplikasi Terakhir',
'intelligence.screenDebug.mode': 'Mode',
'intelligence.screenDebug.permAccessibility': 'Izin aksesibilitas',
'intelligence.screenDebug.permInput': 'Izin input',
'intelligence.screenDebug.permScreen': 'Aksesibilitas',
'intelligence.screenDebug.permissions': 'Izin',
'intelligence.screenDebug.platformNotSupported': 'Platform tidak didukung',
'intelligence.screenDebug.recentVisionSummaries': 'Ringkasan visi terbaru',
'intelligence.screenDebug.session': 'Sesi',
'intelligence.screenDebug.size': 'Ukuran',
'intelligence.screenDebug.status': 'Status',
'intelligence.screenDebug.testCapture': 'Tes tangkapan',
'intelligence.screenDebug.time': 'Waktu',
'intelligence.screenDebug.title': 'Judul',
'intelligence.screenDebug.unknown': 'Tidak diketahui',
'intelligence.screenDebug.visionQueue': 'Antrean Visi',
'intelligence.screenDebug.visionState': 'Status Visi',
'intelligence.tasks.activeBoardOne': '1 board aktif di seluruh percakapan',
'intelligence.tasks.activeBoardOther': '{count} board aktif di seluruh percakapan',
'intelligence.tasks.empty': 'Belum ada papan tugas agen',
@@ -4489,7 +4406,6 @@ const messages: TranslationMap = {
'overlay.ariaVoiceActive': 'Input suara aktif',
'overlay.companion.error': 'Kesalahan',
'overlay.companion.listening': 'Mendengarkan…',
'overlay.companion.pointing': 'Menunjuk…',
'overlay.companion.speaking': 'Berbicara…',
'overlay.companion.thinking': 'Berpikir…',
'overlay.orbTitle': 'Seret untuk memindahkan · Klik dua kali untuk mereset posisi',
@@ -4521,12 +4437,8 @@ const messages: TranslationMap = {
'pages.settings.features.messagingChannelsDesc': 'Deskripsi kanal pesan',
'pages.settings.features.notifications': 'Notifikasi',
'pages.settings.features.notificationsDesc': 'Deskripsi notifikasi',
'pages.settings.features.screenAwareness': 'Kesadaran layar',
'pages.settings.features.screenAwarenessDesc':
'Biarkan agen Anda melihat layar Anda untuk memberikan bantuan tepat waktu dan sesuai konteks.',
'pages.settings.features.tools': 'Alat',
'pages.settings.features.toolsDesc': 'Deskripsi alat',
'pages.settings.featuresSection.description': 'Kesadaran layar, pesan, dan alat.',
'pages.settings.featuresSection.title': 'Fitur',
'privacy.dataKind.credentials': 'Kredensial',
'privacy.dataKind.derived': 'Turunan',
@@ -5287,9 +5199,6 @@ const messages: TranslationMap = {
'settings.developerMenu.ai.title': 'Konfigurasi AI',
'settings.developerMenu.ai.desc':
'Penyedia cloud, model Ollama lokal, dan routing per beban kerja',
'settings.developerMenu.screenAwareness.title': 'Kesadaran Layar',
'settings.developerMenu.screenAwareness.desc':
'Izin tangkapan layar, kebijakan pemantauan, dan kontrol sesi',
'settings.developerMenu.messagingChannels.title': 'Kanal Pesan',
'settings.developerMenu.messagingChannels.desc':
'Atur mode autentikasi Telegram/Discord dan routing kanal default',
@@ -5937,20 +5846,6 @@ const messages: TranslationMap = {
'settings.modelHealth.modal.apply': 'Terapkan Penggantian',
'settings.modelHealth.tag.cheaper': 'CHEAPER',
'settings.modelHealth.tag.better': 'BETTER',
'settings.screenIntel.permissions.accessibility': 'Aksesibilitas',
'settings.screenIntel.permissions.grantHint': 'Petunjuk izin',
'settings.screenIntel.permissions.inputMonitoring': 'Pemantauan Input',
'settings.screenIntel.permissions.macosAppliesPrivacy': 'macOS menerapkan privasi',
'settings.screenIntel.permissions.openInputMonitoring': 'Meminta...',
'settings.screenIntel.permissions.refreshStatus': 'Menyegarkan...',
'settings.screenIntel.permissions.refreshing': 'Menyegarkan...',
'settings.screenIntel.permissions.requestAccessibility': 'Meminta...',
'settings.screenIntel.permissions.requestScreenRecording': 'Meminta...',
'settings.screenIntel.permissions.requesting': 'Meminta...',
'settings.screenIntel.permissions.restartRefresh': 'Memulai ulang core...',
'settings.screenIntel.permissions.restartingCore': 'Memulai ulang core...',
'settings.screenIntel.permissions.screenRecording': 'Perekaman Layar',
'settings.screenIntel.permissions.title': 'Izin',
'skills.card.moreActions': 'Tindakan lainnya',
'skills.channelIcon.discord': 'Discord',
'skills.channelIcon.imessage': 'iMessage',
@@ -6212,37 +6107,6 @@ const messages: TranslationMap = {
'skills.resource.tree.templates': 'Templat',
'skills.resource.tree.empty': 'Tidak ada sumber daya bundel.',
'skills.search.placeholder': 'Teks placeholder',
'skills.setup.screenIntel.activeDesc': 'Deskripsi aktif',
'skills.setup.screenIntel.activeTitle': 'Kecerdasan Layar Diaktifkan',
'skills.setup.screenIntel.advancedSettings': 'Pengaturan lanjutan',
'skills.setup.screenIntel.allGranted': 'Semua izin diberikan',
'skills.setup.screenIntel.captureMode': 'Mode tangkap',
'skills.setup.screenIntel.captureModeValue': 'Semua jendela (dapat dikonfigurasi nanti)',
'skills.setup.screenIntel.deniedHint':
'Setelah memberikan izin di Pengaturan Sistem, klik di bawah untuk memulai ulang dan mengambil perubahan.',
'skills.setup.screenIntel.enableBtn': 'Mengaktifkan...',
'skills.setup.screenIntel.enableDesc':
's di layar Anda dan memberikan konteks berguna ke agen Anda',
'skills.setup.screenIntel.enableError': 'Gagal mengaktifkan Kecerdasan Layar',
'skills.setup.screenIntel.enabling': 'Mengaktifkan...',
'skills.setup.screenIntel.grant': 'Membuka...',
'skills.setup.screenIntel.granted': 'Diberikan',
'skills.setup.screenIntel.macosOnly': 'Hanya macOS',
'skills.setup.screenIntel.opening': 'Membuka...',
'skills.setup.screenIntel.panicHotkey': 'Hotkey panik',
'skills.setup.screenIntel.permAccessibility': 'Aksesibilitas',
'skills.setup.screenIntel.permInputMonitoring': 'Pemantauan Input',
'skills.setup.screenIntel.permScreenRecording': 'Perekaman Layar',
'skills.setup.screenIntel.permissionsDesc': 'Deskripsi izin',
'skills.setup.screenIntel.permissionPathLabel': 'macOS menerapkan privasi ke:',
'skills.setup.screenIntel.refreshStatus': 'Segarkan status',
'skills.setup.screenIntel.restartRefresh': 'Memulai ulang...',
'skills.setup.screenIntel.restarting': 'Memulai ulang...',
'skills.setup.screenIntel.stepEnable': 'Aktifkan skill',
'skills.setup.screenIntel.stepPermissions': 'Berikan izin',
'skills.setup.screenIntel.stepSuccess': 'Siap digunakan',
'skills.setup.screenIntel.title': 'Kecerdasan Layar',
'skills.setup.screenIntel.visionModel': 'Model visi',
'skills.setup.voice.activation': 'Aktivasi',
'skills.setup.voice.activeDescPrefix': 'Fn',
'skills.setup.voice.activeDescSuffix': 'Fn',
@@ -6403,8 +6267,6 @@ const messages: TranslationMap = {
'untuk instruksi pengaturan, lalu coba lagi setelah runtime Anda dapat dijangkau.',
'devOptions.menuAi': 'Konfigurasi AI',
'devOptions.menuAiDesc': 'Penyedia cloud, model Ollama lokal, dan routing per beban kerja',
'devOptions.menuScreenAware': 'Kesadaran Layar',
'devOptions.menuScreenAwareDesc': 'Izin tangkapan layar, kebijakan pemantauan, dan kontrol sesi',
'devOptions.menuMessaging': 'Channel Pesan',
'devOptions.menuMessagingDesc':
'Konfigurasikan mode autentikasi Telegram/Discord dan routing channel bawaan',
+1 -142
View File
@@ -9,8 +9,6 @@ const messages: TranslationMap = {
'settings.assistant.desktopCompanionDesc':
'Modalità compagno sempre attiva con scorciatoia nella barra di sistema',
'pages.settings.features.desktopCompanion': 'Companion Desktop',
'pages.settings.features.desktopCompanionDesc':
'Assistente vocale con consapevolezza dello schermo: ascolta, vede, parla, indica',
'settings.companion.title': 'Compagno da scrivania',
'settings.companion.session': 'Sessione',
'settings.companion.activeLabel': 'Attivo',
@@ -26,8 +24,6 @@ const messages: TranslationMap = {
'settings.companion.hotkey': 'Tasto di scelta rapida',
'settings.companion.activationMode': 'Modalità di attivazione',
'settings.companion.sessionTtl': 'Sessione TTL',
'settings.companion.screenCapture': 'Acquisizione schermata',
'settings.companion.appContext': 'Contesto app',
// Share cards (#5006).
'share.button': 'Condividi',
'share.modalTitle': 'Condividi questo momento',
@@ -117,8 +113,6 @@ const messages: TranslationMap = {
'Scegli il modello di embedding usato per la ricerca nella memoria.',
'connections.header.search': 'Configura la ricerca web per il tuo agente.',
'connections.header.wallet': 'Il saldo del tuo wallet locale e la tua frase di recupero.',
'connections.header.screen':
'Lascia che il tuo agente veda il tuo schermo per offrire un aiuto tempestivo e contestuale.',
'connections.header.composioKey':
'Aggiungi una chiave API Composio per sbloccare centinaia di integrazioni di app.',
'connections.llm.apiKeys': 'Chiavi API e instradamento',
@@ -778,8 +772,6 @@ const messages: TranslationMap = {
'settings.assistant.backgroundActivity': 'Subconscio',
'settings.assistant.backgroundActivityDesc':
'Controlla quanto attivamente il tuo assistente lavora in background',
'settings.assistant.screenAwareness': 'Consapevolezza dello schermo',
'settings.assistant.screenAwarenessDesc': "Consenti all'assistente di vedere la finestra attiva",
'settings.assistant.permissions': 'Autorizzazioni',
'settings.assistant.permissionsDesc': "Scegli cosa può fare l'assistente e dove può lavorare",
'settings.privacySecurity.privacy': 'Privacy',
@@ -862,7 +854,6 @@ const messages: TranslationMap = {
'settings.notifications.tabs.preferences': 'Preferenze',
'settings.notifications.tabs.routing': 'Instradamento',
'settings.features': 'Funzionalità',
'settings.featuresDesc': 'Consapevolezza schermo, messaggistica e strumenti',
'settings.aiModels': 'AI e modelli',
'settings.aiModelsDesc': 'Configurazione modello AI locale, download e provider LLM',
'settings.ai': 'Configurazione AI',
@@ -959,8 +950,6 @@ const messages: TranslationMap = {
'settings.notifications.channelControls': 'Controlli per canale',
'settings.notifications.channelControlsDesc':
'Configura le preferenze di notifica per ogni canale',
'settings.features.screenAwareness': 'Consapevolezza schermo',
'settings.features.screenAwarenessDesc': "Consenti all'assistente di vedere la finestra attiva",
'settings.features.messaging': 'Messaggistica',
'settings.features.messagingDesc': 'Impostazioni canale e integrazione messaggistica',
'settings.features.tools': 'Strumenti',
@@ -2250,21 +2239,6 @@ const messages: TranslationMap = {
'settings.tools.saveChanges': 'Salva modifiche',
'settings.tools.preferencesSaved': 'Preferenze salvate',
'settings.tools.saveFailed': 'Salvataggio preferenze fallito. Riprova.',
'settings.screenAwareness.mode': 'Modalità',
'settings.screenAwareness.allExceptBlacklist': 'Tutti tranne blacklist',
'settings.screenAwareness.whitelistOnly': 'Solo whitelist',
'settings.screenAwareness.screenMonitoring': 'Monitoraggio schermo',
'settings.screenAwareness.saveSettings': 'Salva impostazioni',
'settings.screenAwareness.session': 'Sessione',
'settings.screenAwareness.status': 'Stato',
'settings.screenAwareness.active': 'Attivo',
'settings.screenAwareness.stopped': 'Fermato',
'settings.screenAwareness.remaining': 'Rimanente',
'settings.screenAwareness.startSession': 'Avvia sessione',
'settings.screenAwareness.stopSession': 'Ferma sessione',
'settings.screenAwareness.analyzeNow': 'Analizza ora',
'settings.screenAwareness.macosOnly':
'La cattura desktop di Consapevolezza schermo e i controlli dei permessi sono attualmente supportati solo su macOS.',
'connections.comingSoon': 'In arrivo',
'connections.setUp': 'Configura',
'connections.configured': 'Configurato',
@@ -2625,40 +2599,9 @@ const messages: TranslationMap = {
'Scegli quali sottosistemi girano sul modello locale. Quelli disattivati usano il cloud.',
'localModel.enableRuntime': 'Abilita runtime AI locale',
'localModel.enableRuntimeDesc':
"Interruttore principale. Disattivato di default: Ollama resta inattivo. Quando attivo, il tree summarizer, lo screen intelligence e l'autocompletamento usano sempre il modello locale.",
"Interruttore principale. Disattivato di default: Ollama resta inattivo. Quando attivo, il tree summarizer e l'autocompletamento usano sempre il modello locale.",
'localModel.advancedSettings': 'Impostazioni avanzate',
'localModel.debugTitle': 'Debug modello locale',
'screenAwareness.debugTitle': 'Debug consapevolezza schermo',
'screenAwareness.debug.debugAndDiagnostics': 'Debug e diagnostica',
'screenAwareness.debug.collapse': 'Comprimi',
'screenAwareness.debug.expand': 'Espandi',
'screenAwareness.debug.failedToSave': 'Impossibile salvare i dati di Screen Intelligence',
'screenAwareness.debug.policyTitle': 'Policy di Screen Intelligence',
'screenAwareness.debug.baselineFps': 'FPS di base',
'screenAwareness.debug.useVisionModel': 'Utilizza modello di visione',
'screenAwareness.debug.useVisionModelDesc':
'Invia gli screenshot a un LLM con visione per un contesto più ricco. Se disattivato, viene utilizzato solo il testo OCR con un LLM testuale: più veloce e senza modello visivo.',
'screenAwareness.debug.keepScreenshots': 'Conserva screenshot',
'screenAwareness.debug.keepScreenshotsDesc':
"Salva gli screenshot acquisiti nell'area di lavoro invece di eliminarli dopo l'elaborazione",
'screenAwareness.debug.allowlist': 'Lista consentita (una regola per riga)',
'screenAwareness.debug.denylist': 'Lista vietata (una regola per riga)',
'screenAwareness.debug.saveSettings': 'Salva impostazioni di Screen Intelligence',
'screenAwareness.debug.sessionStats': 'Statistiche sessione',
'screenAwareness.debug.framesEphemeral': 'Frame (effimeri)',
'screenAwareness.debug.panicStop': 'Arresto antipanico',
'screenAwareness.debug.defaultPanicHotkey': 'Cmd+Maiusc+.',
'screenAwareness.debug.vision': 'Visione',
'screenAwareness.debug.idle': 'inattiva',
'screenAwareness.debug.visionQueue': 'Coda visioni',
'screenAwareness.debug.lastVision': 'Ultima visione',
'screenAwareness.debug.notAvailable': 'n/a',
'screenAwareness.debug.visionSummaries': 'Riepiloghi visione',
'screenAwareness.debug.refreshing': 'Aggiornamento in corso…',
'screenAwareness.debug.noSummaries': 'Ancora nessun riepilogo.',
'screenAwareness.debug.unknownApp': 'App sconosciuta',
'screenAwareness.debug.macosOnly':
'Screen Intelligence V1 è attualmente supportato solo su macOS.',
'memory.debugTitle': 'Debug memoria',
'memory.documents': 'Documenti',
'memory.filterByNamespace': 'Filtra per spazio dei nomi...',
@@ -3928,33 +3871,6 @@ const messages: TranslationMap = {
'intelligence.diagram.imageAlt': "Ultimo diagramma dell'architettura OpenHuman generato",
'intelligence.diagram.refreshesEvery': 'Si aggiorna ogni {seconds}s',
'intelligence.memoryText.entityTypePrefix': 'Tipo di entità',
'intelligence.screenDebug.active': 'Attivo',
'intelligence.screenDebug.app': 'App',
'intelligence.screenDebug.bounds': 'Limiti',
'intelligence.screenDebug.captureAlt': 'Risultato test di cattura',
'intelligence.screenDebug.captureFailed': 'Fallito',
'intelligence.screenDebug.captureSuccess': 'Successo',
'intelligence.screenDebug.captureTest': 'Test di cattura',
'intelligence.screenDebug.capturing': 'Cattura in corso',
'intelligence.screenDebug.frames': 'Frame',
'intelligence.screenDebug.idle': 'Inattivo',
'intelligence.screenDebug.lastApp': 'Ultima app',
'intelligence.screenDebug.mode': 'Modalità',
'intelligence.screenDebug.permAccessibility': 'Permesso accessibilità',
'intelligence.screenDebug.permInput': 'Permesso input',
'intelligence.screenDebug.permScreen': 'Accessibilità',
'intelligence.screenDebug.permissions': 'Permessi',
'intelligence.screenDebug.platformNotSupported': 'Piattaforma non supportata',
'intelligence.screenDebug.recentVisionSummaries': 'Riassunti vision recenti',
'intelligence.screenDebug.session': 'Sessione',
'intelligence.screenDebug.size': 'Dimensione',
'intelligence.screenDebug.status': 'Stato',
'intelligence.screenDebug.testCapture': 'Test cattura',
'intelligence.screenDebug.time': 'Ora',
'intelligence.screenDebug.title': 'Titolo',
'intelligence.screenDebug.unknown': 'Sconosciuto',
'intelligence.screenDebug.visionQueue': 'Coda vision',
'intelligence.screenDebug.visionState': 'Stato vision',
'intelligence.tasks.activeBoardOne': '1 board attiva tra le conversazioni',
'intelligence.tasks.activeBoardOther': '{count} board attive tra le conversazioni',
'intelligence.tasks.empty': "Nessuna board di attività dell'agente",
@@ -4547,7 +4463,6 @@ const messages: TranslationMap = {
'overlay.ariaVoiceActive': 'Input vocale attivo',
'overlay.companion.error': 'Errore',
'overlay.companion.listening': 'In ascolto…',
'overlay.companion.pointing': 'Sta puntando…',
'overlay.companion.speaking': 'Sta parlando…',
'overlay.companion.thinking': 'Sta pensando…',
'overlay.orbTitle': 'Trascina per spostare · Doppio clic per ripristinare la posizione',
@@ -4579,13 +4494,8 @@ const messages: TranslationMap = {
'pages.settings.features.messagingChannelsDesc': 'Descrizione canali di messaggistica',
'pages.settings.features.notifications': 'Notifiche',
'pages.settings.features.notificationsDesc': 'Descrizione notifiche',
'pages.settings.features.screenAwareness': 'Consapevolezza schermo',
'pages.settings.features.screenAwarenessDesc':
'Lascia che il tuo agente veda il tuo schermo per offrire un aiuto tempestivo e contestuale.',
'pages.settings.features.tools': 'Strumenti',
'pages.settings.features.toolsDesc': 'Descrizione strumenti',
'pages.settings.featuresSection.description':
'Consapevolezza schermo, messaggistica e strumenti.',
'pages.settings.featuresSection.title': 'Funzionalità',
'privacy.dataKind.credentials': 'Credenziali',
'privacy.dataKind.derived': 'Derivati',
@@ -5348,9 +5258,6 @@ const messages: TranslationMap = {
'settings.developerMenu.ai.title': 'Configurazione IA',
'settings.developerMenu.ai.desc':
'Provider cloud, modelli Ollama locali e routing per carico di lavoro',
'settings.developerMenu.screenAwareness.title': 'Consapevolezza schermo',
'settings.developerMenu.screenAwareness.desc':
'Permessi di cattura schermo, criteri di monitoraggio e controlli di sessione',
'settings.developerMenu.messagingChannels.title': 'Canali di messaggistica',
'settings.developerMenu.messagingChannels.desc':
'Configura le modalità di autenticazione Telegram/Discord e il routing predefinito del canale',
@@ -6012,20 +5919,6 @@ const messages: TranslationMap = {
'settings.modelHealth.modal.apply': 'Applica sostituzione',
'settings.modelHealth.tag.cheaper': 'CHEAPER',
'settings.modelHealth.tag.better': 'BETTER',
'settings.screenIntel.permissions.accessibility': 'Accessibilità',
'settings.screenIntel.permissions.grantHint': 'Suggerimento concessione',
'settings.screenIntel.permissions.inputMonitoring': 'Monitoraggio input',
'settings.screenIntel.permissions.macosAppliesPrivacy': 'macOS applica privacy',
'settings.screenIntel.permissions.openInputMonitoring': 'Richiesta…',
'settings.screenIntel.permissions.refreshStatus': 'Aggiornamento…',
'settings.screenIntel.permissions.refreshing': 'Aggiornamento…',
'settings.screenIntel.permissions.requestAccessibility': 'Richiesta…',
'settings.screenIntel.permissions.requestScreenRecording': 'Richiesta…',
'settings.screenIntel.permissions.requesting': 'Richiesta…',
'settings.screenIntel.permissions.restartRefresh': 'Riavvio core…',
'settings.screenIntel.permissions.restartingCore': 'Riavvio core…',
'settings.screenIntel.permissions.screenRecording': 'Registrazione schermo',
'settings.screenIntel.permissions.title': 'Permessi',
'skills.card.moreActions': 'Altre azioni',
'skills.channelIcon.discord': 'Discord',
'skills.channelIcon.imessage': 'iMessage',
@@ -6288,37 +6181,6 @@ const messages: TranslationMap = {
'skills.resource.tree.templates': 'Modelli',
'skills.resource.tree.empty': 'Nessuna risorsa inclusa.',
'skills.search.placeholder': 'Segnaposto',
'skills.setup.screenIntel.activeDesc': 'Descrizione attivo',
'skills.setup.screenIntel.activeTitle': 'Screen Intelligence abilitato',
'skills.setup.screenIntel.advancedSettings': 'Impostazioni avanzate',
'skills.setup.screenIntel.allGranted': 'Tutti i permessi concessi',
'skills.setup.screenIntel.captureMode': 'Modalità di cattura',
'skills.setup.screenIntel.captureModeValue': 'Tutte le finestre (configurabile in seguito)',
'skills.setup.screenIntel.deniedHint':
'Dopo aver concesso i permessi in Impostazioni di sistema, clicca sotto per riavviare e raccogliere le modifiche.',
'skills.setup.screenIntel.enableBtn': 'Abilitazione...',
'skills.setup.screenIntel.enableDesc':
's sul tuo schermo e fornisce contesto utile al tuo agente',
'skills.setup.screenIntel.enableError': 'Abilitazione Screen Intelligence fallita',
'skills.setup.screenIntel.enabling': 'Abilitazione...',
'skills.setup.screenIntel.grant': 'Apertura...',
'skills.setup.screenIntel.granted': 'Concesso',
'skills.setup.screenIntel.macosOnly': 'Solo macOS',
'skills.setup.screenIntel.opening': 'Apertura...',
'skills.setup.screenIntel.panicHotkey': 'Hotkey panico',
'skills.setup.screenIntel.permAccessibility': 'Accessibilità',
'skills.setup.screenIntel.permInputMonitoring': 'Monitoraggio input',
'skills.setup.screenIntel.permScreenRecording': 'Registrazione schermo',
'skills.setup.screenIntel.permissionsDesc': 'Descrizione permessi',
'skills.setup.screenIntel.permissionPathLabel': 'macOS applica la privacy a:',
'skills.setup.screenIntel.refreshStatus': 'Aggiorna stato',
'skills.setup.screenIntel.restartRefresh': 'Riavvio...',
'skills.setup.screenIntel.restarting': 'Riavvio...',
'skills.setup.screenIntel.stepEnable': 'Abilita la skill',
'skills.setup.screenIntel.stepPermissions': 'Concedi permessi',
'skills.setup.screenIntel.stepSuccess': 'Pronto',
'skills.setup.screenIntel.title': 'Intelligenza dello schermo',
'skills.setup.screenIntel.visionModel': 'Modello vision',
'skills.setup.voice.activation': 'Attivazione',
'skills.setup.voice.activeDescPrefix': 'Fn',
'skills.setup.voice.activeDescSuffix': 'Fn',
@@ -6478,9 +6340,6 @@ const messages: TranslationMap = {
'per le istruzioni di configurazione, poi riprova quando il runtime è raggiungibile.',
'devOptions.menuAi': 'Configurazione AI',
'devOptions.menuAiDesc': 'Provider cloud, modelli Ollama locali e routing per carico di lavoro',
'devOptions.menuScreenAware': 'Riconoscimento schermo',
'devOptions.menuScreenAwareDesc':
"Autorizzazioni per l'acquisizione dello schermo, policy di monitoraggio e controlli della sessione",
'devOptions.menuMessaging': 'Canali di messaggistica',
'devOptions.menuMessagingDesc':
'Configura le modalità di autenticazione Telegram/Discord e il routing del canale predefinito',
-137
View File
@@ -8,8 +8,6 @@ const messages: TranslationMap = {
'settings.assistant.desktopCompanionDesc':
'시스템 트레이 단축키가 있는 항상 활성화된 동반자 모드',
'pages.settings.features.desktopCompanion': '데스크탑 동반자',
'pages.settings.features.desktopCompanionDesc':
'화면 인식 기능이 있는 음성 도우미: 듣고, 보고, 말하고, 가리킵니다.',
'settings.companion.title': '데스크톱 컴패니언',
'settings.companion.session': '세션',
'settings.companion.activeLabel': '활성',
@@ -25,8 +23,6 @@ const messages: TranslationMap = {
'settings.companion.hotkey': '단축키',
'settings.companion.activationMode': '활성화 모드',
'settings.companion.sessionTtl': '세션 TTL',
'settings.companion.screenCapture': '화면 캡처',
'settings.companion.appContext': '앱 컨텍스트',
// Share cards (#5006).
'share.button': '공유',
'share.modalTitle': '이 순간을 공유하세요',
@@ -111,8 +107,6 @@ const messages: TranslationMap = {
'connections.header.embeddings': '메모리 검색에 사용할 임베딩 모델을 선택하세요.',
'connections.header.search': '에이전트를 위한 웹 검색을 구성하세요.',
'connections.header.wallet': '로컬 지갑 잔액과 복구 문구입니다.',
'connections.header.screen':
'에이전트가 화면을 볼 수 있게 하여 시의적절하고 맥락에 맞는 도움을 제공하세요.',
'connections.header.composioKey':
'Composio API 키를 추가하여 수백 개의 앱 통합을 잠금 해제하세요.',
'connections.llm.apiKeys': 'API 키 및 라우팅',
@@ -754,8 +748,6 @@ const messages: TranslationMap = {
'settings.assistant.backgroundActivity': '잠재의식',
'settings.assistant.backgroundActivityDesc':
'어시스턴트가 백그라운드에서 얼마나 활발히 작동하는지 제어',
'settings.assistant.screenAwareness': '화면 인식',
'settings.assistant.screenAwarenessDesc': '어시스턴트가 활성 창을 볼 수 있도록 허용',
'settings.assistant.permissions': '권한',
'settings.assistant.permissionsDesc':
'어시스턴트가 무엇을 할 수 있고 어디서 작업할 수 있는지 선택하세요',
@@ -835,7 +827,6 @@ const messages: TranslationMap = {
'settings.notifications.tabs.preferences': '기본 설정',
'settings.notifications.tabs.routing': '라우팅',
'settings.features': '기능',
'settings.featuresDesc': '화면 인식, 메시징 및 도구',
'settings.aiModels': 'AI 및 모델',
'settings.aiModelsDesc': '로컬 AI 모델 설정, 다운로드 및 LLM 제공업체',
'settings.ai': 'AI 구성',
@@ -926,8 +917,6 @@ const messages: TranslationMap = {
'settings.notifications.doNotDisturbDesc': '정해진 시간 동안 모든 알림을 일시 중지합니다',
'settings.notifications.channelControls': '채널별 설정',
'settings.notifications.channelControlsDesc': '각 채널의 알림 기본 설정을 구성합니다',
'settings.features.screenAwareness': '화면 인식',
'settings.features.screenAwarenessDesc': '어시스턴트가 현재 활성 창을 볼 수 있게 합니다',
'settings.features.messaging': '메시징',
'settings.features.messagingDesc': '채널 및 메시징 통합 설정',
'settings.features.tools': '도구',
@@ -2184,21 +2173,6 @@ const messages: TranslationMap = {
'settings.tools.saveChanges': '변경 사항 저장',
'settings.tools.preferencesSaved': '기본 설정이 저장되었습니다',
'settings.tools.saveFailed': '기본 설정 저장에 실패했습니다. 다시 시도하세요.',
'settings.screenAwareness.mode': '모드',
'settings.screenAwareness.allExceptBlacklist': '블랙리스트 제외 모두',
'settings.screenAwareness.whitelistOnly': '화이트리스트만',
'settings.screenAwareness.screenMonitoring': '화면 모니터링',
'settings.screenAwareness.saveSettings': '설정 저장',
'settings.screenAwareness.session': '세션',
'settings.screenAwareness.status': '상태',
'settings.screenAwareness.active': '활성',
'settings.screenAwareness.stopped': '중지됨',
'settings.screenAwareness.remaining': '남은 시간',
'settings.screenAwareness.startSession': '세션 시작',
'settings.screenAwareness.stopSession': '세션 중지',
'settings.screenAwareness.analyzeNow': '지금 분석',
'settings.screenAwareness.macosOnly':
'화면 인식 데스크톱 캡처 및 권한 제어는 현재 macOS에서만 지원됩니다.',
'connections.comingSoon': '곧 제공 예정',
'connections.setUp': '설정',
'connections.configured': '구성됨',
@@ -2551,36 +2525,6 @@ const messages: TranslationMap = {
'마스터 스위치입니다. 기본값은 꺼짐이며 Ollama는 유휴 상태로 유지됩니다. 켜면 트리 요약기, 화면 인텔리전스, 자동 완성이 항상 로컬 모델을 사용합니다.',
'localModel.advancedSettings': '고급 설정',
'localModel.debugTitle': '로컬 모델 디버그',
'screenAwareness.debugTitle': '화면 인식 디버그',
'screenAwareness.debug.debugAndDiagnostics': '디버그 및 진단',
'screenAwareness.debug.collapse': '축소',
'screenAwareness.debug.expand': '확장',
'screenAwareness.debug.failedToSave': '화면 인텔리전스 저장 실패',
'screenAwareness.debug.policyTitle': '화면 인텔리전스 정책',
'screenAwareness.debug.baselineFps': '기준 FPS',
'screenAwareness.debug.useVisionModel': '비전 모델 사용',
'screenAwareness.debug.useVisionModelDesc':
'더 풍부한 컨텍스트를 위해 비전 LLM에 스크린샷을 전송합니다. 꺼져 있으면 텍스트 LLM에서 OCR 텍스트만 사용하므로 더 빠르고 비전 모델이 필요 없습니다.',
'screenAwareness.debug.keepScreenshots': '스크린샷 유지',
'screenAwareness.debug.keepScreenshotsDesc':
'처리 후 삭제하지 않고 캡처한 스크린샷을 워크스페이스에 저장합니다',
'screenAwareness.debug.allowlist': '허용 목록(한 줄에 하나의 규칙)',
'screenAwareness.debug.denylist': '차단 목록(한 줄에 하나의 규칙)',
'screenAwareness.debug.saveSettings': '화면 인텔리전스 설정 저장',
'screenAwareness.debug.sessionStats': '세션 통계',
'screenAwareness.debug.framesEphemeral': '프레임(임시)',
'screenAwareness.debug.panicStop': '패닉 중지',
'screenAwareness.debug.defaultPanicHotkey': 'Cmd+Shift+.',
'screenAwareness.debug.vision': '비전',
'screenAwareness.debug.idle': '유휴',
'screenAwareness.debug.visionQueue': '비전 대기열',
'screenAwareness.debug.lastVision': '마지막 비전',
'screenAwareness.debug.notAvailable': '해당 없음',
'screenAwareness.debug.visionSummaries': '비전 요약',
'screenAwareness.debug.refreshing': '새로 고침…',
'screenAwareness.debug.noSummaries': '아직 요약이 없습니다.',
'screenAwareness.debug.unknownApp': '알 수 없는 앱',
'screenAwareness.debug.macosOnly': 'Screen Intelligence V1은 현재 macOS에서만 지원됩니다.',
'memory.debugTitle': '메모리 디버그',
'memory.documents': '문서',
'memory.filterByNamespace': '네임스페이스로 필터링...',
@@ -3827,33 +3771,6 @@ const messages: TranslationMap = {
'intelligence.diagram.imageAlt': '최신 생성된 OpenHuman 아키텍처 다이어그램',
'intelligence.diagram.refreshesEvery': '{seconds}초마다 새로 고침',
'intelligence.memoryText.entityTypePrefix': '엔터티 유형',
'intelligence.screenDebug.active': '활성',
'intelligence.screenDebug.app': '앱',
'intelligence.screenDebug.bounds': '경계',
'intelligence.screenDebug.captureAlt': '캡처 테스트 결과',
'intelligence.screenDebug.captureFailed': '실패',
'intelligence.screenDebug.captureSuccess': '성공',
'intelligence.screenDebug.captureTest': '캡처 테스트',
'intelligence.screenDebug.capturing': '캡처 중',
'intelligence.screenDebug.frames': '프레임',
'intelligence.screenDebug.idle': '유휴',
'intelligence.screenDebug.lastApp': '마지막 앱',
'intelligence.screenDebug.mode': '모드',
'intelligence.screenDebug.permAccessibility': '접근성 권한',
'intelligence.screenDebug.permInput': '입력 권한',
'intelligence.screenDebug.permScreen': '접근성',
'intelligence.screenDebug.permissions': '권한',
'intelligence.screenDebug.platformNotSupported': '플랫폼이 지원되지 않음',
'intelligence.screenDebug.recentVisionSummaries': '최근 비전 요약',
'intelligence.screenDebug.session': '세션',
'intelligence.screenDebug.size': '크기',
'intelligence.screenDebug.status': '상태',
'intelligence.screenDebug.testCapture': '테스트 캡처',
'intelligence.screenDebug.time': '시간',
'intelligence.screenDebug.title': '제목',
'intelligence.screenDebug.unknown': '알 수 없음',
'intelligence.screenDebug.visionQueue': '비전 대기열',
'intelligence.screenDebug.visionState': '비전 상태',
'intelligence.tasks.activeBoardOne': '대화 전반에 활성 보드 1개',
'intelligence.tasks.activeBoardOther': '대화 전반에 활성 보드 {count}개',
'intelligence.tasks.empty': '아직 에이전트 작업 보드가 없습니다',
@@ -4422,7 +4339,6 @@ const messages: TranslationMap = {
'overlay.ariaVoiceActive': '음성 입력 활성',
'overlay.companion.error': '오류',
'overlay.companion.listening': '듣는 중…',
'overlay.companion.pointing': '가리키는 중…',
'overlay.companion.speaking': '말하는 중…',
'overlay.companion.thinking': '생각 중...',
'overlay.orbTitle': '드래그하여 이동 · 두 번 클릭하여 위치 초기화',
@@ -4452,12 +4368,8 @@ const messages: TranslationMap = {
'pages.settings.features.messagingChannelsDesc': '메시징 채널 설명',
'pages.settings.features.notifications': '알림',
'pages.settings.features.notificationsDesc': '알림 설명',
'pages.settings.features.screenAwareness': '화면 인식',
'pages.settings.features.screenAwarenessDesc':
'에이전트가 화면을 볼 수 있게 하여 시의적절하고 맥락에 맞는 도움을 제공하세요.',
'pages.settings.features.tools': '도구',
'pages.settings.features.toolsDesc': '도구 설명',
'pages.settings.featuresSection.description': '화면 인식, 메시징 및 도구.',
'pages.settings.featuresSection.title': '기능',
'privacy.dataKind.credentials': '자격 증명',
'privacy.dataKind.derived': '파생됨',
@@ -5206,8 +5118,6 @@ const messages: TranslationMap = {
'settings.localModel.status.working': '작업 중...',
'settings.developerMenu.ai.title': 'AI 구성',
'settings.developerMenu.ai.desc': '클라우드 공급자, 로컬 Ollama 모델 및 워크로드별 라우팅',
'settings.developerMenu.screenAwareness.title': '화면 인식',
'settings.developerMenu.screenAwareness.desc': '화면 캡처 권한, 모니터링 정책 및 세션 제어',
'settings.developerMenu.messagingChannels.title': '메시징 채널',
'settings.developerMenu.messagingChannels.desc':
'Telegram/Discord 인증 모드 및 기본 채널 라우팅 구성',
@@ -5840,20 +5750,6 @@ const messages: TranslationMap = {
'settings.modelHealth.modal.apply': '교체 적용',
'settings.modelHealth.tag.cheaper': '더 저렴함',
'settings.modelHealth.tag.better': '더 좋음',
'settings.screenIntel.permissions.accessibility': '접근성',
'settings.screenIntel.permissions.grantHint': '권한 허용 안내',
'settings.screenIntel.permissions.inputMonitoring': '입력 모니터링',
'settings.screenIntel.permissions.macosAppliesPrivacy': 'macOS 개인정보 보호 적용',
'settings.screenIntel.permissions.openInputMonitoring': '요청 중…',
'settings.screenIntel.permissions.refreshStatus': '새로고침 중…',
'settings.screenIntel.permissions.refreshing': '새로고침 중…',
'settings.screenIntel.permissions.requestAccessibility': '요청 중…',
'settings.screenIntel.permissions.requestScreenRecording': '요청 중…',
'settings.screenIntel.permissions.requesting': '요청 중…',
'settings.screenIntel.permissions.restartRefresh': '코어 다시 시작 중…',
'settings.screenIntel.permissions.restartingCore': '코어 다시 시작 중…',
'settings.screenIntel.permissions.screenRecording': '화면 녹화',
'settings.screenIntel.permissions.title': '권한',
'skills.card.moreActions': '추가 작업',
'skills.channelIcon.discord': 'Discord',
'skills.channelIcon.imessage': 'iMessage',
@@ -6112,37 +6008,6 @@ const messages: TranslationMap = {
'skills.resource.tree.templates': '템플릿',
'skills.resource.tree.empty': '번들 리소스가 없습니다.',
'skills.search.placeholder': '플레이스홀더',
'skills.setup.screenIntel.activeDesc': '활성 설명',
'skills.setup.screenIntel.activeTitle': '화면 인텔리전스가 활성화됨',
'skills.setup.screenIntel.advancedSettings': '고급 설정',
'skills.setup.screenIntel.allGranted': '모든 권한 허용됨',
'skills.setup.screenIntel.captureMode': '캡처 모드',
'skills.setup.screenIntel.captureModeValue': '모든 창(나중에 구성 가능)',
'skills.setup.screenIntel.deniedHint':
'시스템 설정에서 권한을 허용한 후 아래를 클릭하여 다시 시작하고 변경 사항을 적용하세요.',
'skills.setup.screenIntel.enableBtn': '활성화 중...',
'skills.setup.screenIntel.enableDesc':
'화면의 내용을 읽고 유용한 컨텍스트를 에이전트에 제공합니다',
'skills.setup.screenIntel.enableError': '화면 인텔리전스를 활성화하지 못했습니다',
'skills.setup.screenIntel.enabling': '활성화 중...',
'skills.setup.screenIntel.grant': '여는 중...',
'skills.setup.screenIntel.granted': '허용됨',
'skills.setup.screenIntel.macosOnly': 'macOS 전용',
'skills.setup.screenIntel.opening': '여는 중...',
'skills.setup.screenIntel.panicHotkey': '패닉 핫키',
'skills.setup.screenIntel.permAccessibility': '접근성',
'skills.setup.screenIntel.permInputMonitoring': '입력 모니터링',
'skills.setup.screenIntel.permScreenRecording': '화면 녹화',
'skills.setup.screenIntel.permissionsDesc': '권한 설명',
'skills.setup.screenIntel.permissionPathLabel': 'macOS는 다음에 개인 정보 보호를 적용합니다.',
'skills.setup.screenIntel.refreshStatus': '상태 새로고침',
'skills.setup.screenIntel.restartRefresh': '다시 시작 중...',
'skills.setup.screenIntel.restarting': '다시 시작 중...',
'skills.setup.screenIntel.stepEnable': '스킬 활성화',
'skills.setup.screenIntel.stepPermissions': '권한 허용',
'skills.setup.screenIntel.stepSuccess': '사용 준비 완료',
'skills.setup.screenIntel.title': '화면 인텔리전스',
'skills.setup.screenIntel.visionModel': '비전 모델',
'skills.setup.voice.activation': '활성화',
'skills.setup.voice.activeDescPrefix': 'Fn',
'skills.setup.voice.activeDescSuffix': 'Fn',
@@ -6301,8 +6166,6 @@ const messages: TranslationMap = {
'설정 지침을 확인한 다음 런타임에 연결할 수 있게 되면 다시 시도하세요.',
'devOptions.menuAi': 'AI 구성',
'devOptions.menuAiDesc': '클라우드 공급자, 로컬 Ollama 모델 및 워크로드별 라우팅',
'devOptions.menuScreenAware': '화면 인식',
'devOptions.menuScreenAwareDesc': '화면 캡처 권한, 모니터링 정책 및 세션 제어',
'devOptions.menuMessaging': '메시징 채널',
'devOptions.menuMessagingDesc': 'Telegram/Discord 인증 모드 및 기본 채널 라우팅 구성',
'devOptions.menuTools': '도구',
-140
View File
@@ -9,8 +9,6 @@ const messages: TranslationMap = {
'settings.assistant.desktopCompanionDesc':
'Tryb towarzyski zawsze aktywny ze skrótem w zasobniku systemowym',
'pages.settings.features.desktopCompanion': 'Towarzysz pulpitu',
'pages.settings.features.desktopCompanionDesc':
'Asystent głosowy ze świadomością ekranu: słucha, widzi, mówi, wskazuje',
'settings.companion.title': 'Towarzysz komputerowy',
'settings.companion.session': 'Sesja',
'settings.companion.activeLabel': 'Aktywna',
@@ -26,8 +24,6 @@ const messages: TranslationMap = {
'settings.companion.hotkey': 'Skrót klawiszowy',
'settings.companion.activationMode': 'Tryb aktywacji',
'settings.companion.sessionTtl': 'TTL sesji',
'settings.companion.screenCapture': 'Przechwytywanie ekranu',
'settings.companion.appContext': 'Kontekst aplikacji',
// Share cards (#5006).
'share.button': 'Udostępnij',
'share.modalTitle': 'Udostępnij tę chwilę',
@@ -115,8 +111,6 @@ const messages: TranslationMap = {
'connections.header.embeddings': 'Wybierz model osadzeń używany do wyszukiwania w pamięci.',
'connections.header.search': 'Skonfiguruj wyszukiwanie internetowe dla swojego agenta.',
'connections.header.wallet': 'Saldo Twojego lokalnego portfela i fraza odzyskiwania.',
'connections.header.screen':
'Pozwól agentowi widzieć Twój ekran, aby oferować pomoc w odpowiednim czasie i kontekście.',
'connections.header.composioKey':
'Dodaj klucz API Composio, aby odblokować setki integracji aplikacji.',
'connections.llm.apiKeys': 'Klucze API i routing',
@@ -773,8 +767,6 @@ const messages: TranslationMap = {
'settings.assistant.faceMascot': 'Twarz / Maskotka',
'settings.assistant.backgroundActivity': 'Podświadomość',
'settings.assistant.backgroundActivityDesc': 'Kontroluj, jak aktywnie asystent pracuje w tle',
'settings.assistant.screenAwareness': 'Świadomość ekranu',
'settings.assistant.screenAwarenessDesc': 'Pozwól asystentowi widzieć aktywne okno',
'settings.assistant.permissions': 'Uprawnienia',
'settings.assistant.permissionsDesc': 'Wybierz, co może robić asystent i gdzie może pracować',
'settings.privacySecurity.privacy': 'Prywatność',
@@ -856,7 +848,6 @@ const messages: TranslationMap = {
'settings.notifications.tabs.preferences': 'Preferencje',
'settings.notifications.tabs.routing': 'Reguły routingu',
'settings.features': 'Funkcje',
'settings.featuresDesc': 'Świadomość ekranu, wiadomości i narzędzia',
'settings.aiModels': 'AI i modele',
'settings.aiModelsDesc': 'Konfiguracja lokalnych modeli AI, pobierania i dostawcy LLM',
'settings.ai': 'Konfiguracja AI',
@@ -952,8 +943,6 @@ const messages: TranslationMap = {
'settings.notifications.channelControls': 'Sterowanie per kanał',
'settings.notifications.channelControlsDesc':
'Skonfiguruj preferencje powiadomień dla każdego kanału',
'settings.features.screenAwareness': 'Świadomość ekranu',
'settings.features.screenAwarenessDesc': 'Pozwól asystentowi widzieć Twoje aktywne okno',
'settings.features.messaging': 'Wiadomości',
'settings.features.messagingDesc': 'Ustawienia kanałów i integracji wiadomości',
'settings.features.tools': 'Narzędzia',
@@ -2239,21 +2228,6 @@ const messages: TranslationMap = {
'settings.tools.saveChanges': 'Zapisz zmiany',
'settings.tools.preferencesSaved': 'Preferencje zapisane',
'settings.tools.saveFailed': 'Nie udało się zapisać preferencji. Spróbuj ponownie.',
'settings.screenAwareness.mode': 'Tryb',
'settings.screenAwareness.allExceptBlacklist': 'Wszystko oprócz czarnej listy',
'settings.screenAwareness.whitelistOnly': 'Tylko biała lista',
'settings.screenAwareness.screenMonitoring': 'Monitorowanie ekranu',
'settings.screenAwareness.saveSettings': 'Zapisz ustawienia',
'settings.screenAwareness.session': 'Sesja',
'settings.screenAwareness.status': 'Stan',
'settings.screenAwareness.active': 'Aktywna',
'settings.screenAwareness.stopped': 'Zatrzymana',
'settings.screenAwareness.remaining': 'Pozostało',
'settings.screenAwareness.startSession': 'Rozpocznij sesję',
'settings.screenAwareness.stopSession': 'Zatrzymaj sesję',
'settings.screenAwareness.analyzeNow': 'Analizuj teraz',
'settings.screenAwareness.macosOnly':
'Przechwytywanie ekranu i zarządzanie uprawnieniami są obecnie obsługiwane tylko na macOS.',
'connections.comingSoon': 'Wkrótce',
'connections.setUp': 'Skonfiguruj',
'connections.configured': 'Skonfigurowano',
@@ -2614,37 +2588,6 @@ const messages: TranslationMap = {
'Główny przełącznik. Domyślnie wyłączony: Ollama pozostaje bezczynna. Gdy włączony, podsumowywanie drzewa, inteligencja ekranu i autouzupełnianie zawsze korzystają z modelu lokalnego.',
'localModel.advancedSettings': 'Ustawienia zaawansowane',
'localModel.debugTitle': 'Debug modelu lokalnego',
'screenAwareness.debugTitle': 'Debug świadomości ekranu',
'screenAwareness.debug.debugAndDiagnostics': 'Debug i diagnostyka',
'screenAwareness.debug.collapse': 'Zwiń',
'screenAwareness.debug.expand': 'Rozwiń',
'screenAwareness.debug.failedToSave': 'Nie udało się zapisać świadomości ekranu',
'screenAwareness.debug.policyTitle': 'Polityka świadomości ekranu',
'screenAwareness.debug.baselineFps': 'Bazowe FPS',
'screenAwareness.debug.useVisionModel': 'Użyj modelu wizji',
'screenAwareness.debug.useVisionModelDesc':
'Wysyłaj zrzuty ekranu do LLM wizyjnego po bogatszy kontekst. Gdy wyłączone, używany jest tylko tekst OCR z LLM tekstowym: szybciej i bez modelu wizji.',
'screenAwareness.debug.keepScreenshots': 'Zachowuj zrzuty ekranu',
'screenAwareness.debug.keepScreenshotsDesc':
'Zapisuj zrzuty ekranu w przestrzeni roboczej zamiast usuwać po przetworzeniu',
'screenAwareness.debug.allowlist': 'Lista dozwolonych (jedna reguła na linię)',
'screenAwareness.debug.denylist': 'Lista blokowanych (jedna reguła na linię)',
'screenAwareness.debug.saveSettings': 'Zapisz ustawienia świadomości ekranu',
'screenAwareness.debug.sessionStats': 'Statystyki sesji',
'screenAwareness.debug.framesEphemeral': 'Klatki (ulotne)',
'screenAwareness.debug.panicStop': 'Awaryjne zatrzymanie',
'screenAwareness.debug.defaultPanicHotkey': 'Cmd+Shift+.',
'screenAwareness.debug.vision': 'Wizja',
'screenAwareness.debug.idle': 'bezczynne',
'screenAwareness.debug.visionQueue': 'Kolejka wizji',
'screenAwareness.debug.lastVision': 'Ostatnia wizja',
'screenAwareness.debug.notAvailable': 'n/d',
'screenAwareness.debug.visionSummaries': 'Podsumowania wizji',
'screenAwareness.debug.refreshing': 'Odświeżanie…',
'screenAwareness.debug.noSummaries': 'Brak podsumowań.',
'screenAwareness.debug.unknownApp': 'Nieznana aplikacja',
'screenAwareness.debug.macosOnly':
'Świadomość ekranu V1 jest obecnie obsługiwana tylko na macOS.',
'memory.debugTitle': 'Debug pamięci',
'memory.documents': 'Dokumenty',
'memory.filterByNamespace': 'Filtruj po przestrzeni nazw...',
@@ -3911,33 +3854,6 @@ const messages: TranslationMap = {
'intelligence.diagram.imageAlt': 'Najnowszy wygenerowany diagram architektury OpenHuman',
'intelligence.diagram.refreshesEvery': 'Odświeża co {seconds}s',
'intelligence.memoryText.entityTypePrefix': 'Typ encji',
'intelligence.screenDebug.active': 'Aktywne',
'intelligence.screenDebug.app': 'Aplikacja',
'intelligence.screenDebug.bounds': 'Granice',
'intelligence.screenDebug.captureAlt': 'Wynik testu przechwytywania',
'intelligence.screenDebug.captureFailed': 'Niepowodzenie',
'intelligence.screenDebug.captureSuccess': 'Powodzenie',
'intelligence.screenDebug.captureTest': 'Test przechwytywania',
'intelligence.screenDebug.capturing': 'Przechwytywanie',
'intelligence.screenDebug.frames': 'Klatki',
'intelligence.screenDebug.idle': 'Bezczynne',
'intelligence.screenDebug.lastApp': 'Ostatnia aplikacja',
'intelligence.screenDebug.mode': 'Tryb',
'intelligence.screenDebug.permAccessibility': 'Uprawnienie: dostępność',
'intelligence.screenDebug.permInput': 'Uprawnienie: wejście',
'intelligence.screenDebug.permScreen': 'Dostępność',
'intelligence.screenDebug.permissions': 'Uprawnienia',
'intelligence.screenDebug.platformNotSupported': 'Platforma nieobsługiwana',
'intelligence.screenDebug.recentVisionSummaries': 'Ostatnie podsumowania wizji',
'intelligence.screenDebug.session': 'Sesja',
'intelligence.screenDebug.size': 'Rozmiar',
'intelligence.screenDebug.status': 'Stan',
'intelligence.screenDebug.testCapture': 'Testuj przechwytywanie',
'intelligence.screenDebug.time': 'Czas',
'intelligence.screenDebug.title': 'Tytuł',
'intelligence.screenDebug.unknown': 'Nieznane',
'intelligence.screenDebug.visionQueue': 'Kolejka wizji',
'intelligence.screenDebug.visionState': 'Stan wizji',
'intelligence.tasks.activeBoardOne': '1 aktywna tablica w rozmowach',
'intelligence.tasks.activeBoardOther': '{count} aktywnych tablic w rozmowach',
'intelligence.tasks.empty': 'Brak tablic zadań agenta',
@@ -4534,7 +4450,6 @@ const messages: TranslationMap = {
'overlay.ariaVoiceActive': 'Wejście głosowe aktywne',
'overlay.companion.error': 'Błąd',
'overlay.companion.listening': 'Słucham…',
'overlay.companion.pointing': 'Wskazuję…',
'overlay.companion.speaking': 'Mówię…',
'overlay.companion.thinking': 'Myślę…',
'overlay.orbTitle': 'Przeciągnij, aby przesunąć · Kliknij dwukrotnie, aby zresetować pozycję',
@@ -4570,12 +4485,8 @@ const messages: TranslationMap = {
'pages.settings.features.notifications': 'Powiadomienia',
'pages.settings.features.notificationsDesc':
'Steruj alertami systemowymi i kategoriami powiadomień',
'pages.settings.features.screenAwareness': 'Świadomość ekranu',
'pages.settings.features.screenAwarenessDesc':
'Pozwól agentowi widzieć Twój ekran, aby oferować pomoc w odpowiednim czasie i kontekście.',
'pages.settings.features.tools': 'Narzędzia',
'pages.settings.features.toolsDesc': 'Wybierz, z jakich możliwości OpenHuman może korzystać',
'pages.settings.featuresSection.description': 'Świadomość ekranu, komunikacja i narzędzia.',
'pages.settings.featuresSection.title': 'Funkcje',
'privacy.dataKind.credentials': 'Poświadczenia',
'privacy.dataKind.derived': 'Wyprowadzone',
@@ -5344,9 +5255,6 @@ const messages: TranslationMap = {
'settings.developerMenu.ai.title': 'Konfiguracja AI',
'settings.developerMenu.ai.desc':
'Dostawcy chmurowi, lokalne modele Ollama i trasowanie per workload',
'settings.developerMenu.screenAwareness.title': 'Świadomość ekranu',
'settings.developerMenu.screenAwareness.desc':
'Uprawnienia do przechwytywania ekranu, polityka monitorowania i kontrola sesji',
'settings.developerMenu.messagingChannels.title': 'Kanały komunikacji',
'settings.developerMenu.messagingChannels.desc':
'Konfiguruj tryby uwierzytelniania Telegram/Discord i domyślne trasowanie',
@@ -5998,20 +5906,6 @@ const messages: TranslationMap = {
'settings.modelHealth.modal.apply': 'Zastosuj zamianę',
'settings.modelHealth.tag.cheaper': 'TAŃSZY',
'settings.modelHealth.tag.better': 'LEPSZY',
'settings.screenIntel.permissions.accessibility': 'Dostępność',
'settings.screenIntel.permissions.grantHint': 'System otworzy okno żądania uprawnień.',
'settings.screenIntel.permissions.inputMonitoring': 'Monitorowanie wejścia',
'settings.screenIntel.permissions.macosAppliesPrivacy': 'macOS stosuje politykę prywatności do:',
'settings.screenIntel.permissions.openInputMonitoring': 'Żądanie…',
'settings.screenIntel.permissions.refreshStatus': 'Odświeżanie…',
'settings.screenIntel.permissions.refreshing': 'Odświeżanie…',
'settings.screenIntel.permissions.requestAccessibility': 'Żądanie…',
'settings.screenIntel.permissions.requestScreenRecording': 'Żądanie…',
'settings.screenIntel.permissions.requesting': 'Żądanie…',
'settings.screenIntel.permissions.restartRefresh': 'Restartowanie rdzenia…',
'settings.screenIntel.permissions.restartingCore': 'Restartowanie rdzenia…',
'settings.screenIntel.permissions.screenRecording': 'Nagrywanie ekranu',
'settings.screenIntel.permissions.title': 'Uprawnienia',
'skills.card.moreActions': 'Więcej akcji',
'skills.channelIcon.discord': 'Discord',
'skills.channelIcon.imessage': 'iMessage',
@@ -6274,37 +6168,6 @@ const messages: TranslationMap = {
'skills.resource.tree.templates': 'Szablony',
'skills.resource.tree.empty': 'Brak dołączonych zasobów.',
'skills.search.placeholder': 'Szukaj umiejętności...',
'skills.setup.screenIntel.activeDesc': 'Inteligencja ekranu działa. Wybrane okna są obserwowane.',
'skills.setup.screenIntel.activeTitle': 'Inteligencja ekranu jest włączona',
'skills.setup.screenIntel.advancedSettings': 'Ustawienia zaawansowane',
'skills.setup.screenIntel.allGranted': 'Wszystkie uprawnienia przyznane',
'skills.setup.screenIntel.captureMode': 'Tryb przechwytywania',
'skills.setup.screenIntel.captureModeValue': 'Wszystkie okna (konfigurowalne później)',
'skills.setup.screenIntel.deniedHint':
'Po przyznaniu uprawnień w Ustawieniach systemowych kliknij poniżej, aby zrestartować i odświeżyć zmiany.',
'skills.setup.screenIntel.enableBtn': 'Włączanie...',
'skills.setup.screenIntel.enableDesc':
's na Twoim ekranie i dostarczają użytecznego kontekstu agentowi',
'skills.setup.screenIntel.enableError': 'Nie udało się włączyć inteligencji ekranu',
'skills.setup.screenIntel.enabling': 'Włączanie...',
'skills.setup.screenIntel.grant': 'Otwieranie...',
'skills.setup.screenIntel.granted': 'Przyznano',
'skills.setup.screenIntel.macosOnly': 'Tylko macOS',
'skills.setup.screenIntel.opening': 'Otwieranie...',
'skills.setup.screenIntel.panicHotkey': 'Skrót awaryjny',
'skills.setup.screenIntel.permAccessibility': 'Dostępność',
'skills.setup.screenIntel.permInputMonitoring': 'Monitorowanie wejścia',
'skills.setup.screenIntel.permScreenRecording': 'Nagrywanie ekranu',
'skills.setup.screenIntel.permissionsDesc': 'Inteligencja ekranu wymaga uprawnień systemowych:',
'skills.setup.screenIntel.permissionPathLabel': 'macOS stosuje politykę prywatności do:',
'skills.setup.screenIntel.refreshStatus': 'Odśwież status',
'skills.setup.screenIntel.restartRefresh': 'Restartowanie...',
'skills.setup.screenIntel.restarting': 'Restartowanie...',
'skills.setup.screenIntel.stepEnable': 'Włącz umiejętność',
'skills.setup.screenIntel.stepPermissions': 'Przyznaj uprawnienia',
'skills.setup.screenIntel.stepSuccess': 'Gotowe',
'skills.setup.screenIntel.title': 'Inteligencja ekranu',
'skills.setup.screenIntel.visionModel': 'Model wizji',
'skills.setup.voice.activation': 'Aktywacja',
'skills.setup.voice.activeDescPrefix': 'Fn',
'skills.setup.voice.activeDescSuffix': 'Fn',
@@ -6465,9 +6328,6 @@ const messages: TranslationMap = {
'po instrukcje konfiguracji, a następnie spróbuj ponownie, gdy środowisko będzie osiągalne.',
'devOptions.menuAi': 'Konfiguracja AI',
'devOptions.menuAiDesc': 'Dostawcy chmurowi, lokalne modele Ollama i trasowanie per workload',
'devOptions.menuScreenAware': 'Świadomość ekranu',
'devOptions.menuScreenAwareDesc':
'Uprawnienia do przechwytywania ekranu, polityka monitorowania i kontrola sesji',
'devOptions.menuMessaging': 'Kanały komunikacji',
'devOptions.menuMessagingDesc':
'Konfiguruj tryby uwierzytelniania Telegram/Discord i domyślne trasowanie',
-139
View File
@@ -9,8 +9,6 @@ const messages: TranslationMap = {
'settings.assistant.desktopCompanionDesc':
'Modo companheiro sempre ativo com atalho na bandeja do sistema',
'pages.settings.features.desktopCompanion': 'Companion Desktop',
'pages.settings.features.desktopCompanionDesc':
'Assistente de voz com consciência da tela: escuta, vê, fala, aponta',
'settings.companion.title': 'Companheiro de Área de Trabalho',
'settings.companion.session': 'Sessão',
'settings.companion.activeLabel': 'Ativo',
@@ -26,8 +24,6 @@ const messages: TranslationMap = {
'settings.companion.hotkey': 'Tecla de atalho',
'settings.companion.activationMode': 'Modo de ativação',
'settings.companion.sessionTtl': 'Sessão TTL',
'settings.companion.screenCapture': 'Captura de tela',
'settings.companion.appContext': 'Contexto do aplicativo',
// Share cards (#5006).
'share.button': 'Partilhar',
'share.modalTitle': 'Partilhe este momento',
@@ -114,8 +110,6 @@ const messages: TranslationMap = {
'connections.header.embeddings': 'Escolha o modelo de embeddings usado na pesquisa da memória.',
'connections.header.search': 'Configure a pesquisa na web para o seu agente.',
'connections.header.wallet': 'O saldo da sua carteira local e a sua frase de recuperação.',
'connections.header.screen':
'Deixe o seu agente ver o seu ecrã para oferecer ajuda oportuna e contextual.',
'connections.header.composioKey':
'Adicione uma chave de API do Composio para desbloquear centenas de integrações de aplicações.',
'connections.llm.apiKeys': 'Chaves de API e encaminhamento',
@@ -769,8 +763,6 @@ const messages: TranslationMap = {
'settings.assistant.backgroundActivity': 'Subconsciente',
'settings.assistant.backgroundActivityDesc':
'Controle o quão ativamente seu assistente trabalha em segundo plano',
'settings.assistant.screenAwareness': 'Consciência de tela',
'settings.assistant.screenAwarenessDesc': 'Permitir que o assistente veja sua janela ativa',
'settings.assistant.permissions': 'Permissões',
'settings.assistant.permissionsDesc':
'Escolha o que o assistente pode fazer e onde pode trabalhar',
@@ -853,7 +845,6 @@ const messages: TranslationMap = {
'settings.notifications.tabs.preferences': 'Preferências',
'settings.notifications.tabs.routing': 'Roteamento',
'settings.features': 'Recursos',
'settings.featuresDesc': 'Reconhecimento de tela, mensagens e ferramentas',
'settings.aiModels': 'IA e Modelos',
'settings.aiModelsDesc': 'Configuração de modelos de IA local, downloads e provedor LLM',
'settings.ai': 'Configuração de IA',
@@ -951,8 +942,6 @@ const messages: TranslationMap = {
'settings.notifications.channelControls': 'Controles por Canal',
'settings.notifications.channelControlsDesc':
'Configurar preferências de notificação para cada canal',
'settings.features.screenAwareness': 'Reconhecimento de Tela',
'settings.features.screenAwarenessDesc': 'Deixar o assistente ver sua janela ativa',
'settings.features.messaging': 'Mensagens',
'settings.features.messagingDesc': 'Configurações de integração de canais e mensagens',
'settings.features.tools': 'Ferramentas',
@@ -2248,21 +2237,6 @@ const messages: TranslationMap = {
'settings.tools.saveChanges': 'Salvar Alterações',
'settings.tools.preferencesSaved': 'Preferências salvas',
'settings.tools.saveFailed': 'Falha ao salvar preferências. Tente novamente.',
'settings.screenAwareness.mode': 'Modo',
'settings.screenAwareness.allExceptBlacklist': 'Todos Exceto Lista Negra',
'settings.screenAwareness.whitelistOnly': 'Somente Lista Branca',
'settings.screenAwareness.screenMonitoring': 'Monitoramento de Tela',
'settings.screenAwareness.saveSettings': 'Salvar Configurações',
'settings.screenAwareness.session': 'Sessão',
'settings.screenAwareness.status': 'Status',
'settings.screenAwareness.active': 'Ativo',
'settings.screenAwareness.stopped': 'Parado',
'settings.screenAwareness.remaining': 'Restante',
'settings.screenAwareness.startSession': 'Iniciar Sessão',
'settings.screenAwareness.stopSession': 'Encerrar Sessão',
'settings.screenAwareness.analyzeNow': 'Analisar Agora',
'settings.screenAwareness.macosOnly':
'A captura de tela e os controles de permissão do Reconhecimento de Tela são suportados atualmente apenas no macOS.',
'connections.comingSoon': 'Em breve',
'connections.setUp': 'Configurar',
'connections.configured': 'Configurado',
@@ -2626,37 +2600,6 @@ const messages: TranslationMap = {
'Chave mestre. Desligado por padrão: Ollama fica inativo. Quando ligado, o sumarizador de árvore, inteligência de tela e autocompletar sempre usam o modelo local.',
'localModel.advancedSettings': 'Configurações avançadas',
'localModel.debugTitle': 'Depuração de Modelo Local',
'screenAwareness.debugTitle': 'Depuração de Reconhecimento de Tela',
'screenAwareness.debug.debugAndDiagnostics': 'Depuração e diagnóstico',
'screenAwareness.debug.collapse': 'Collapse',
'screenAwareness.debug.expand': 'Expandir',
'screenAwareness.debug.failedToSave': 'Falha ao salvar inteligência de tela',
'screenAwareness.debug.policyTitle': 'Política de inteligência de tela',
'screenAwareness.debug.baselineFps': 'FPS de linha de base',
'screenAwareness.debug.useVisionModel': 'Usar modelo de visão',
'screenAwareness.debug.useVisionModelDesc':
'Enviar capturas de tela para um LLM de visão para contexto mais rico. Quando desativado, apenas o texto de OCR é usado com um LLM de texto: mais rápido e sem necessidade de modelo de visão.',
'screenAwareness.debug.keepScreenshots': 'Manter capturas de tela',
'screenAwareness.debug.keepScreenshotsDesc':
'Salvar capturas de tela no workspace em vez de excluí-las após o processamento',
'screenAwareness.debug.allowlist': 'Lista de permissões (uma regra por linha)',
'screenAwareness.debug.denylist': 'Lista de bloqueios (uma regra por linha)',
'screenAwareness.debug.saveSettings': 'Salvar configurações de inteligência de tela',
'screenAwareness.debug.sessionStats': 'Estatísticas de sessão',
'screenAwareness.debug.framesEphemeral': 'Quadros (efêmeros)',
'screenAwareness.debug.panicStop': 'Parada de pânico',
'screenAwareness.debug.defaultPanicHotkey': 'Cmd+Shift+.',
'screenAwareness.debug.vision': 'Visão',
'screenAwareness.debug.idle': 'inativa',
'screenAwareness.debug.visionQueue': 'Fila de visão',
'screenAwareness.debug.lastVision': 'Última visão',
'screenAwareness.debug.notAvailable': 'n/a',
'screenAwareness.debug.visionSummaries': 'Resumos de visão',
'screenAwareness.debug.refreshing': 'Atualizando…',
'screenAwareness.debug.noSummaries': 'Ainda não há resumos.',
'screenAwareness.debug.unknownApp': 'Aplicativo desconhecido',
'screenAwareness.debug.macosOnly':
'O Screen Intelligence V1 é suportado apenas no macOS no momento.',
'memory.debugTitle': 'Depuração de Memória',
'memory.documents': 'Documentos',
'memory.filterByNamespace': 'Filtrar por namespace...',
@@ -3921,33 +3864,6 @@ const messages: TranslationMap = {
'intelligence.diagram.imageAlt': 'Último diagrama de arquitetura OpenHuman gerado',
'intelligence.diagram.refreshesEvery': 'Atualiza a cada {seconds}s',
'intelligence.memoryText.entityTypePrefix': 'Tipo de entidade',
'intelligence.screenDebug.active': 'Ativo',
'intelligence.screenDebug.app': 'Aplicativo',
'intelligence.screenDebug.bounds': 'Limites',
'intelligence.screenDebug.captureAlt': 'Resultado do teste de captura',
'intelligence.screenDebug.captureFailed': 'Falhou',
'intelligence.screenDebug.captureSuccess': 'Sucesso',
'intelligence.screenDebug.captureTest': 'Teste de captura',
'intelligence.screenDebug.capturing': 'Capturando',
'intelligence.screenDebug.frames': 'Quadros',
'intelligence.screenDebug.idle': 'Inativo',
'intelligence.screenDebug.lastApp': 'Último App',
'intelligence.screenDebug.mode': 'Modo',
'intelligence.screenDebug.permAccessibility': 'Permissão de acessibilidade',
'intelligence.screenDebug.permInput': 'Permissão de entrada',
'intelligence.screenDebug.permScreen': 'Acessibilidade',
'intelligence.screenDebug.permissions': 'Permissões',
'intelligence.screenDebug.platformNotSupported': 'Plataforma não suportada',
'intelligence.screenDebug.recentVisionSummaries': 'Resumos de visão recentes',
'intelligence.screenDebug.session': 'Sessão',
'intelligence.screenDebug.size': 'Tamanho',
'intelligence.screenDebug.status': 'Status',
'intelligence.screenDebug.testCapture': 'Testar captura',
'intelligence.screenDebug.time': 'Tempo',
'intelligence.screenDebug.title': 'Título',
'intelligence.screenDebug.unknown': 'Desconhecido',
'intelligence.screenDebug.visionQueue': 'Fila de Visão',
'intelligence.screenDebug.visionState': 'Estado de Visão',
'intelligence.tasks.activeBoardOne': '1 quadro ativo entre conversas',
'intelligence.tasks.activeBoardOther': '{count} quadros ativos entre conversas',
'intelligence.tasks.empty': 'Nenhum quadro de tarefas de agente ainda',
@@ -4537,7 +4453,6 @@ const messages: TranslationMap = {
'overlay.ariaVoiceActive': 'Entrada de voz ativa',
'overlay.companion.error': 'Erro',
'overlay.companion.listening': 'Ouvindo…',
'overlay.companion.pointing': 'Apontando…',
'overlay.companion.speaking': 'Falando…',
'overlay.companion.thinking': 'Pensando…',
'overlay.orbTitle': 'Arraste para mover · Clique duplo para redefinir posição',
@@ -4569,12 +4484,8 @@ const messages: TranslationMap = {
'pages.settings.features.messagingChannelsDesc': 'Descrição dos canais de mensagens',
'pages.settings.features.notifications': 'Notificações',
'pages.settings.features.notificationsDesc': 'Descrição de notificações',
'pages.settings.features.screenAwareness': 'Reconhecimento de tela',
'pages.settings.features.screenAwarenessDesc':
'Deixe o seu agente ver o seu ecrã para oferecer ajuda oportuna e contextual.',
'pages.settings.features.tools': 'Ferramentas',
'pages.settings.features.toolsDesc': 'Descrição de ferramentas',
'pages.settings.featuresSection.description': 'Reconhecimento de tela, mensagens e ferramentas.',
'pages.settings.featuresSection.title': 'Recursos',
'privacy.dataKind.credentials': 'Credenciais',
'privacy.dataKind.derived': 'Derivado',
@@ -5339,9 +5250,6 @@ const messages: TranslationMap = {
'settings.developerMenu.ai.title': 'Configuração de IA',
'settings.developerMenu.ai.desc':
'Provedores em nuvem, modelos Ollama locais e roteamento por carga de trabalho',
'settings.developerMenu.screenAwareness.title': 'Consciência de tela',
'settings.developerMenu.screenAwareness.desc':
'Permissões de captura de tela, política de monitoramento e controles de sessão',
'settings.developerMenu.messagingChannels.title': 'Canais de mensagens',
'settings.developerMenu.messagingChannels.desc':
'Configure modos de autenticação Telegram/Discord e o roteamento de canal padrão',
@@ -6004,20 +5912,6 @@ const messages: TranslationMap = {
'settings.modelHealth.modal.apply': 'Aplicar Substituição',
'settings.modelHealth.tag.cheaper': 'CHEAPER',
'settings.modelHealth.tag.better': 'BETTER',
'settings.screenIntel.permissions.accessibility': 'Acessibilidade',
'settings.screenIntel.permissions.grantHint': 'Dica de concessão',
'settings.screenIntel.permissions.inputMonitoring': 'Monitoramento de Entrada',
'settings.screenIntel.permissions.macosAppliesPrivacy': 'macOS aplica privacidade',
'settings.screenIntel.permissions.openInputMonitoring': 'Solicitando…',
'settings.screenIntel.permissions.refreshStatus': 'Atualizando…',
'settings.screenIntel.permissions.refreshing': 'Atualizando…',
'settings.screenIntel.permissions.requestAccessibility': 'Solicitando…',
'settings.screenIntel.permissions.requestScreenRecording': 'Solicitando…',
'settings.screenIntel.permissions.requesting': 'Solicitando…',
'settings.screenIntel.permissions.restartRefresh': 'Reiniciando o core…',
'settings.screenIntel.permissions.restartingCore': 'Reiniciando o core…',
'settings.screenIntel.permissions.screenRecording': 'Gravação de Tela',
'settings.screenIntel.permissions.title': 'Permissões',
'skills.card.moreActions': 'Mais ações',
'skills.channelIcon.discord': 'Discord',
'skills.channelIcon.imessage': 'iMessage',
@@ -6279,36 +6173,6 @@ const messages: TranslationMap = {
'skills.resource.tree.templates': 'Modelos',
'skills.resource.tree.empty': 'Sem recursos incluídos.',
'skills.search.placeholder': 'Espaço reservado',
'skills.setup.screenIntel.activeDesc': 'Descrição ativo',
'skills.setup.screenIntel.activeTitle': 'Inteligência de Tela está Ativada',
'skills.setup.screenIntel.advancedSettings': 'Configurações avançadas',
'skills.setup.screenIntel.allGranted': 'Todas as permissões concedidas',
'skills.setup.screenIntel.captureMode': 'Modo de captura',
'skills.setup.screenIntel.captureModeValue': 'Todas as janelas (configurável depois)',
'skills.setup.screenIntel.deniedHint':
'Após conceder permissões nas Configurações do Sistema, clique abaixo para reiniciar e aplicar as alterações.',
'skills.setup.screenIntel.enableBtn': 'Ativando...',
'skills.setup.screenIntel.enableDesc': 's na sua tela e alimentar contexto útil no seu agente',
'skills.setup.screenIntel.enableError': 'Falha ao ativar Inteligência de Tela',
'skills.setup.screenIntel.enabling': 'Ativando...',
'skills.setup.screenIntel.grant': 'Abrindo...',
'skills.setup.screenIntel.granted': 'Concedido',
'skills.setup.screenIntel.macosOnly': 'Apenas macOS',
'skills.setup.screenIntel.opening': 'Abrindo...',
'skills.setup.screenIntel.panicHotkey': 'Tecla de pânico',
'skills.setup.screenIntel.permAccessibility': 'Acessibilidade',
'skills.setup.screenIntel.permInputMonitoring': 'Monitoramento de Entrada',
'skills.setup.screenIntel.permScreenRecording': 'Gravação de Tela',
'skills.setup.screenIntel.permissionsDesc': 'Descrição de permissões',
'skills.setup.screenIntel.permissionPathLabel': 'macOS aplica privacidade a:',
'skills.setup.screenIntel.refreshStatus': 'Atualizar status',
'skills.setup.screenIntel.restartRefresh': 'Reiniciando...',
'skills.setup.screenIntel.restarting': 'Reiniciando...',
'skills.setup.screenIntel.stepEnable': 'Ativar a habilidade',
'skills.setup.screenIntel.stepPermissions': 'Conceder permissões',
'skills.setup.screenIntel.stepSuccess': 'Pronto para usar',
'skills.setup.screenIntel.title': 'Inteligência de Tela',
'skills.setup.screenIntel.visionModel': 'Modelo de visão',
'skills.setup.voice.activation': 'Ativação',
'skills.setup.voice.activeDescPrefix': 'Fn',
'skills.setup.voice.activeDescSuffix': 'Fn',
@@ -6469,9 +6333,6 @@ const messages: TranslationMap = {
'devOptions.menuAi': 'Configuração de IA',
'devOptions.menuAiDesc':
'Provedores de nuvem, modelos Ollama locais e roteamento por carga de trabalho',
'devOptions.menuScreenAware': 'Reconhecimento de tela',
'devOptions.menuScreenAwareDesc':
'Permissões de captura de tela, política de monitoramento e controles de sessão',
'devOptions.menuMessaging': 'Canais de mensagens',
'devOptions.menuMessagingDesc':
'Configurar modos de autenticação Telegram/Discord e roteamento de canal padrão',
-140
View File
@@ -9,8 +9,6 @@ const messages: TranslationMap = {
'settings.assistant.desktopCompanionDesc':
'Режим постоянного компаньона с ярлыком в системном лотке',
'pages.settings.features.desktopCompanion': 'Десктоп-спутник',
'pages.settings.features.desktopCompanionDesc':
'Голосовой ассистент с распознаванием экрана: слушает, видит, говорит, указывает',
'settings.companion.title': 'Помощник на рабочем столе',
'settings.companion.session': 'Сеанс',
'settings.companion.activeLabel': 'Активен',
@@ -26,8 +24,6 @@ const messages: TranslationMap = {
'settings.companion.hotkey': 'Горячая клавиша',
'settings.companion.activationMode': 'Режим активации',
'settings.companion.sessionTtl': 'TTL сеанса',
'settings.companion.screenCapture': 'Снимок экрана',
'settings.companion.appContext': 'Контекст приложения',
// Share cards (#5006).
'share.button': 'Поделиться',
'share.modalTitle': 'Поделитесь этим моментом',
@@ -112,8 +108,6 @@ const messages: TranslationMap = {
'connections.header.embeddings': 'Выберите модель эмбеддингов для поиска в памяти.',
'connections.header.search': 'Настройте веб-поиск для вашего агента.',
'connections.header.wallet': 'Баланс вашего локального кошелька и фраза восстановления.',
'connections.header.screen':
'Позвольте агенту видеть ваш экран, чтобы предлагать своевременную контекстную помощь.',
'connections.header.composioKey':
'Добавьте ключ API Composio, чтобы разблокировать сотни интеграций приложений.',
'connections.llm.apiKeys': 'Ключи API и маршрутизация',
@@ -767,8 +761,6 @@ const messages: TranslationMap = {
'settings.assistant.backgroundActivity': 'Подсознание',
'settings.assistant.backgroundActivityDesc':
'Управление тем, насколько активно ассистент работает в фоне',
'settings.assistant.screenAwareness': 'Осведомлённость об экране',
'settings.assistant.screenAwarenessDesc': 'Разрешить ассистенту видеть активное окно',
'settings.assistant.permissions': 'Разрешения',
'settings.assistant.permissionsDesc':
'Выберите, что может делать помощник и где он может работать',
@@ -851,7 +843,6 @@ const messages: TranslationMap = {
'settings.notifications.tabs.preferences': 'Настройки',
'settings.notifications.tabs.routing': 'Маршрутизация',
'settings.features': 'Функции',
'settings.featuresDesc': 'Слежение за экраном, мессенджеры и инструменты',
'settings.aiModels': 'AI и модели',
'settings.aiModelsDesc': 'Настройка локальных AI-моделей, загрузки и LLM-провайдер',
'settings.ai': 'Настройки AI',
@@ -946,8 +937,6 @@ const messages: TranslationMap = {
'settings.notifications.doNotDisturbDesc': 'Отключить все уведомления на заданный период',
'settings.notifications.channelControls': 'По каналам',
'settings.notifications.channelControlsDesc': 'Настройка уведомлений для каждого канала',
'settings.features.screenAwareness': 'Слежение за экраном',
'settings.features.screenAwarenessDesc': 'Разреши ассистенту видеть активное окно',
'settings.features.messaging': 'Мессенджеры',
'settings.features.messagingDesc': 'Настройки каналов и интеграции мессенджеров',
'settings.features.tools': 'Инструменты',
@@ -2226,21 +2215,6 @@ const messages: TranslationMap = {
'settings.tools.saveChanges': 'Сохранить изменения',
'settings.tools.preferencesSaved': 'Настройки сохранены',
'settings.tools.saveFailed': 'Не удалось сохранить настройки. Попробуй ещё раз.',
'settings.screenAwareness.mode': 'Режим',
'settings.screenAwareness.allExceptBlacklist': 'Всё, кроме исключений',
'settings.screenAwareness.whitelistOnly': 'Только разрешённые',
'settings.screenAwareness.screenMonitoring': 'Мониторинг экрана',
'settings.screenAwareness.saveSettings': 'Сохранить настройки',
'settings.screenAwareness.session': 'Сессия',
'settings.screenAwareness.status': 'Статус',
'settings.screenAwareness.active': 'Активно',
'settings.screenAwareness.stopped': 'Остановлено',
'settings.screenAwareness.remaining': 'Осталось',
'settings.screenAwareness.startSession': 'Начать сессию',
'settings.screenAwareness.stopSession': 'Завершить сессию',
'settings.screenAwareness.analyzeNow': 'Анализировать сейчас',
'settings.screenAwareness.macosOnly':
'Захват экрана и управление разрешениями поддерживаются только на macOS.',
'connections.comingSoon': 'Скоро',
'connections.setUp': 'Настроить',
'connections.configured': 'Настроено',
@@ -2601,37 +2575,6 @@ const messages: TranslationMap = {
'Главный переключатель. По умолчанию выключен: Ollama простаивает. При включении суммаризатор деревьев, интеллект экрана и автодополнение всегда используют локальную модель.',
'localModel.advancedSettings': 'Дополнительные настройки',
'localModel.debugTitle': 'Отладка локальной модели',
'screenAwareness.debugTitle': 'Отладка слежения за экраном',
'screenAwareness.debug.debugAndDiagnostics': 'Отладка и диагностика',
'screenAwareness.debug.collapse': 'Свернуть',
'screenAwareness.debug.expand': 'Развернуть',
'screenAwareness.debug.failedToSave': 'Не удалось сохранить интеллект экрана.',
'screenAwareness.debug.policyTitle': 'Политика интеллекта экрана',
'screenAwareness.debug.baselineFps': 'Базовый показатель FPS',
'screenAwareness.debug.useVisionModel': 'Использовать модель концепции.',
'screenAwareness.debug.useVisionModelDesc':
'Отправлять снимки экрана в визуальную языковую модель для более богатого контекста. Если отключено, используется только OCR-текст с текстовой моделью: быстрее и без требований к визуальной модели.',
'screenAwareness.debug.keepScreenshots': 'Сохранять снимки экрана.',
'screenAwareness.debug.keepScreenshotsDesc':
'Сохранять снимки экрана в рабочей области вместо удаления после обработки',
'screenAwareness.debug.allowlist': 'Список разрешенных (одно правило в строке)',
'screenAwareness.debug.denylist': 'Список запрещенных (одно правило в строке)',
'screenAwareness.debug.saveSettings': 'Сохранить настройки интеллекта экрана',
'screenAwareness.debug.sessionStats': 'Статистика сеанса',
'screenAwareness.debug.framesEphemeral': 'Кадры (эфемерные)',
'screenAwareness.debug.panicStop': 'Паника-стоп',
'screenAwareness.debug.defaultPanicHotkey': 'Cmd+Shift+.',
'screenAwareness.debug.vision': 'Видение',
'screenAwareness.debug.idle': 'бездействующее',
'screenAwareness.debug.visionQueue': 'Очередь видений',
'screenAwareness.debug.lastVision': 'Последнее видение',
'screenAwareness.debug.notAvailable': 'н/д',
'screenAwareness.debug.visionSummaries': 'Сводки видений',
'screenAwareness.debug.refreshing': 'Обновление…',
'screenAwareness.debug.noSummaries': 'Сводок пока нет.',
'screenAwareness.debug.unknownApp': 'Неизвестное приложение.',
'screenAwareness.debug.macosOnly':
'Screen Intelligence V1 в настоящее время поддерживается только на macOS.',
'memory.debugTitle': 'Отладка памяти',
'memory.documents': 'Документы',
'memory.filterByNamespace': 'Фильтровать по пространству имен...',
@@ -3893,33 +3836,6 @@ const messages: TranslationMap = {
'intelligence.diagram.imageAlt': 'Последняя сгенерированная архитектурная диаграмма OpenHuman',
'intelligence.diagram.refreshesEvery': 'Обновляется каждые {seconds} с',
'intelligence.memoryText.entityTypePrefix': 'Тип сущности',
'intelligence.screenDebug.active': 'Активно',
'intelligence.screenDebug.app': 'Приложение',
'intelligence.screenDebug.bounds': 'Границы',
'intelligence.screenDebug.captureAlt': 'Результат тестового захвата',
'intelligence.screenDebug.captureFailed': 'Ошибка',
'intelligence.screenDebug.captureSuccess': 'Успех',
'intelligence.screenDebug.captureTest': 'Тест захвата',
'intelligence.screenDebug.capturing': 'Захват',
'intelligence.screenDebug.frames': 'Кадры',
'intelligence.screenDebug.idle': 'Ожидание',
'intelligence.screenDebug.lastApp': 'Последнее приложение',
'intelligence.screenDebug.mode': 'Режим',
'intelligence.screenDebug.permAccessibility': 'Доступность',
'intelligence.screenDebug.permInput': 'Ввод',
'intelligence.screenDebug.permScreen': 'Доступность',
'intelligence.screenDebug.permissions': 'Разрешения',
'intelligence.screenDebug.platformNotSupported': 'Платформа не поддерживается',
'intelligence.screenDebug.recentVisionSummaries': 'Недавние визуальные резюме',
'intelligence.screenDebug.session': 'Сессия',
'intelligence.screenDebug.size': 'Размер',
'intelligence.screenDebug.status': 'Статус',
'intelligence.screenDebug.testCapture': 'Тестовый захват',
'intelligence.screenDebug.time': 'Время',
'intelligence.screenDebug.title': 'Заголовок',
'intelligence.screenDebug.unknown': 'Неизвестно',
'intelligence.screenDebug.visionQueue': 'Очередь обработки изображений',
'intelligence.screenDebug.visionState': 'Состояние обработки изображений',
'intelligence.tasks.activeBoardOne': '1 активная доска в разговорах',
'intelligence.tasks.activeBoardOther': '{count} активных досок в разговорах',
'intelligence.tasks.empty': 'Досок задач агента пока нет',
@@ -4516,7 +4432,6 @@ const messages: TranslationMap = {
'overlay.ariaVoiceActive': 'Голосовой ввод активен',
'overlay.companion.error': 'Ошибка',
'overlay.companion.listening': 'Слушает…',
'overlay.companion.pointing': 'Указывает…',
'overlay.companion.speaking': 'Говорит…',
'overlay.companion.thinking': 'Думает…',
'overlay.orbTitle': 'Перетащи для перемещения · Двойной клик для сброса позиции',
@@ -4547,12 +4462,8 @@ const messages: TranslationMap = {
'pages.settings.features.messagingChannelsDesc': 'Описание каналов сообщений',
'pages.settings.features.notifications': 'Уведомления',
'pages.settings.features.notificationsDesc': 'Описание уведомлений',
'pages.settings.features.screenAwareness': 'Слежение за экраном',
'pages.settings.features.screenAwarenessDesc':
'Позвольте агенту видеть ваш экран, чтобы предлагать своевременную контекстную помощь.',
'pages.settings.features.tools': 'Инструменты',
'pages.settings.features.toolsDesc': 'Описание инструментов',
'pages.settings.featuresSection.description': 'Слежение за экраном, мессенджеры и инструменты.',
'pages.settings.featuresSection.title': 'Функции',
'privacy.dataKind.credentials': 'Учётные данные',
'privacy.dataKind.derived': 'Производные данные',
@@ -5314,9 +5225,6 @@ const messages: TranslationMap = {
'settings.developerMenu.ai.title': 'Конфигурация ИИ',
'settings.developerMenu.ai.desc':
'Облачные провайдеры, локальные модели Ollama и маршрутизация по типам нагрузки',
'settings.developerMenu.screenAwareness.title': 'Осведомленность об экране',
'settings.developerMenu.screenAwareness.desc':
'Разрешения на захват экрана, политика мониторинга и управление сессиями',
'settings.developerMenu.messagingChannels.title': 'Каналы сообщений',
'settings.developerMenu.messagingChannels.desc':
'Настройка режимов аутентификации Telegram/Discord и маршрутизации канала по умолчанию',
@@ -5971,20 +5879,6 @@ const messages: TranslationMap = {
'settings.modelHealth.modal.apply': 'Применить замену',
'settings.modelHealth.tag.cheaper': 'CHEAPER',
'settings.modelHealth.tag.better': 'BETTER',
'settings.screenIntel.permissions.accessibility': 'Доступность',
'settings.screenIntel.permissions.grantHint': 'Подсказка по предоставлению',
'settings.screenIntel.permissions.inputMonitoring': 'Мониторинг ввода',
'settings.screenIntel.permissions.macosAppliesPrivacy': 'macOS применяет конфиденциальность',
'settings.screenIntel.permissions.openInputMonitoring': 'Запрос…',
'settings.screenIntel.permissions.refreshStatus': 'Обновление…',
'settings.screenIntel.permissions.refreshing': 'Обновление…',
'settings.screenIntel.permissions.requestAccessibility': 'Запрос…',
'settings.screenIntel.permissions.requestScreenRecording': 'Запрос…',
'settings.screenIntel.permissions.requesting': 'Запрос…',
'settings.screenIntel.permissions.restartRefresh': 'Перезапуск ядра…',
'settings.screenIntel.permissions.restartingCore': 'Перезапуск ядра…',
'settings.screenIntel.permissions.screenRecording': 'Запись экрана',
'settings.screenIntel.permissions.title': 'Разрешения',
'skills.card.moreActions': 'Ещё действия',
'skills.channelIcon.discord': 'Discord',
'skills.channelIcon.imessage': 'iMessage',
@@ -6243,37 +6137,6 @@ const messages: TranslationMap = {
'skills.resource.tree.templates': 'Шаблоны',
'skills.resource.tree.empty': 'Встроенных ресурсов нет.',
'skills.search.placeholder': 'Поиск',
'skills.setup.screenIntel.activeDesc': 'Активно',
'skills.setup.screenIntel.activeTitle': 'Интеллект экрана включён',
'skills.setup.screenIntel.advancedSettings': 'Дополнительные настройки',
'skills.setup.screenIntel.allGranted': 'Все разрешения предоставлены',
'skills.setup.screenIntel.captureMode': 'Режим захвата',
'skills.setup.screenIntel.captureModeValue': 'Все окна (можно изменить позже)',
'skills.setup.screenIntel.deniedHint':
'После предоставления разрешений в Системных настройках нажми ниже для перезапуска и применения изменений.',
'skills.setup.screenIntel.enableBtn': 'Включение...',
'skills.setup.screenIntel.enableDesc':
'Видеть, что происходит на вашем экране, и передавать полезный контекст вашему агенту',
'skills.setup.screenIntel.enableError': 'Не удалось включить интеллект экрана',
'skills.setup.screenIntel.enabling': 'Включение...',
'skills.setup.screenIntel.grant': 'Открытие...',
'skills.setup.screenIntel.granted': 'Предоставлено',
'skills.setup.screenIntel.macosOnly': 'Только macOS',
'skills.setup.screenIntel.opening': 'Открытие...',
'skills.setup.screenIntel.panicHotkey': 'Экстренная горячая клавиша',
'skills.setup.screenIntel.permAccessibility': 'Доступность',
'skills.setup.screenIntel.permInputMonitoring': 'Мониторинг ввода',
'skills.setup.screenIntel.permScreenRecording': 'Запись экрана',
'skills.setup.screenIntel.permissionsDesc': 'Описание разрешений',
'skills.setup.screenIntel.permissionPathLabel': 'macOS применяет конфиденциальность к:',
'skills.setup.screenIntel.refreshStatus': 'Обновить статус',
'skills.setup.screenIntel.restartRefresh': 'Перезапуск...',
'skills.setup.screenIntel.restarting': 'Перезапуск...',
'skills.setup.screenIntel.stepEnable': 'Включить навык',
'skills.setup.screenIntel.stepPermissions': 'Предоставить разрешения',
'skills.setup.screenIntel.stepSuccess': 'Готово к работе',
'skills.setup.screenIntel.title': 'Интеллект экрана',
'skills.setup.screenIntel.visionModel': 'Визуальная модель',
'skills.setup.voice.activation': 'Активация',
'skills.setup.voice.activeDescPrefix': 'Fn',
'skills.setup.voice.activeDescSuffix': 'Fn',
@@ -6435,9 +6298,6 @@ const messages: TranslationMap = {
'devOptions.menuAi': 'Конфигурация AI',
'devOptions.menuAiDesc':
'Облачные поставщики, локальные модели Ollama и маршрутизация для каждой рабочей нагрузки',
'devOptions.menuScreenAware': 'Функция Screen Aware',
'devOptions.menuScreenAwareDesc':
'Разрешения на захват экрана, мониторинг политика и элементы управления сеансом',
'devOptions.menuMessaging': 'Каналы обмена сообщениями',
'devOptions.menuMessagingDesc':
'Настройка режимов аутентификации Telegram/Discord и маршрутизации каналов по умолчанию',
-132
View File
@@ -7,8 +7,6 @@ const messages: TranslationMap = {
'settings.assistant.desktopCompanion': '桌面伴侣',
'settings.assistant.desktopCompanionDesc': '带有系统托盘快捷方式的常驻伴侣模式',
'pages.settings.features.desktopCompanion': '桌面伴侣',
'pages.settings.features.desktopCompanionDesc':
'具有屏幕感知能力的语音助手:倾听、观看、说话、指向',
'settings.companion.title': '桌面伴侣',
'settings.companion.session': '会话',
'settings.companion.activeLabel': '活跃',
@@ -24,8 +22,6 @@ const messages: TranslationMap = {
'settings.companion.hotkey': '热键',
'settings.companion.activationMode': '激活模式',
'settings.companion.sessionTtl': '会话有效期',
'settings.companion.screenCapture': '屏幕截图',
'settings.companion.appContext': '应用程序上下文',
// Share cards (#5006).
'share.button': '分享',
'share.modalTitle': '分享这一刻',
@@ -108,7 +104,6 @@ const messages: TranslationMap = {
'connections.header.embeddings': '选择用于记忆搜索的嵌入模型。',
'connections.header.search': '为你的智能体配置网络搜索。',
'connections.header.wallet': '你的本地钱包余额和恢复短语。',
'connections.header.screen': '让你的智能体查看你的屏幕,以便提供及时、贴合上下文的帮助。',
'connections.header.composioKey': '添加 Composio API 密钥,解锁数百个应用集成。',
'connections.llm.apiKeys': 'API 密钥与路由',
'connections.welcome.nav': '欢迎',
@@ -723,8 +718,6 @@ const messages: TranslationMap = {
'settings.assistant.faceMascot': '面孔 / 吉祥物',
'settings.assistant.backgroundActivity': '潜意识',
'settings.assistant.backgroundActivityDesc': '控制助手在后台工作的活跃程度',
'settings.assistant.screenAwareness': '屏幕感知',
'settings.assistant.screenAwarenessDesc': '允许助手查看您的活动窗口',
'settings.assistant.permissions': '权限',
'settings.assistant.permissionsDesc': '选择助手可以做什么以及可以在哪里工作',
'settings.privacySecurity.privacy': '隐私',
@@ -798,7 +791,6 @@ const messages: TranslationMap = {
'settings.notifications.tabs.preferences': '偏好设置',
'settings.notifications.tabs.routing': '路由',
'settings.features': '功能',
'settings.featuresDesc': '屏幕感知、消息与工具',
'settings.aiModels': 'AI 与模型',
'settings.aiModelsDesc': '本地 AI 模型设置、下载与 LLM 提供商',
'settings.ai': 'AI',
@@ -886,8 +878,6 @@ const messages: TranslationMap = {
'settings.notifications.doNotDisturbDesc': '在一定时间内暂停所有通知',
'settings.notifications.channelControls': '各渠道控制',
'settings.notifications.channelControlsDesc': '为每个渠道配置通知偏好',
'settings.features.screenAwareness': '屏幕感知',
'settings.features.screenAwarenessDesc': '让助手看到你的活动窗口',
'settings.features.messaging': '消息',
'settings.features.messagingDesc': '渠道与消息集成设置',
'settings.features.tools': '工具',
@@ -2085,20 +2075,6 @@ const messages: TranslationMap = {
'settings.tools.saveChanges': '保存更改',
'settings.tools.preferencesSaved': '偏好设置已保存',
'settings.tools.saveFailed': '保存偏好失败,请重试。',
'settings.screenAwareness.mode': '模式',
'settings.screenAwareness.allExceptBlacklist': '除黑名单外全部',
'settings.screenAwareness.whitelistOnly': '仅白名单',
'settings.screenAwareness.screenMonitoring': '屏幕监控',
'settings.screenAwareness.saveSettings': '保存设置',
'settings.screenAwareness.session': '会话',
'settings.screenAwareness.status': '状态',
'settings.screenAwareness.active': '活跃',
'settings.screenAwareness.stopped': '已停止',
'settings.screenAwareness.remaining': '剩余',
'settings.screenAwareness.startSession': '开始会话',
'settings.screenAwareness.stopSession': '停止会话',
'settings.screenAwareness.analyzeNow': '立即分析',
'settings.screenAwareness.macosOnly': '屏幕感知桌面捕获和权限控制目前仅在 macOS 上受支持。',
'connections.comingSoon': '即将推出',
'connections.setUp': '设置',
'connections.configured': '已配置',
@@ -2434,35 +2410,6 @@ const messages: TranslationMap = {
'总开关。默认关闭:Ollama 保持空闲。启用后,树摘要器、屏幕智能和自动补全始终使用本地模型。',
'localModel.advancedSettings': '高级设置',
'localModel.debugTitle': '本地模型调试',
'screenAwareness.debugTitle': '屏幕感知调试',
'screenAwareness.debug.debugAndDiagnostics': '调试与诊断',
'screenAwareness.debug.collapse': '崩溃',
'screenAwareness.debug.expand': '展开',
'screenAwareness.debug.failedToSave': '保存屏幕情报失败',
'screenAwareness.debug.policyTitle': '屏幕情报政策',
'screenAwareness.debug.baselineFps': '基线 FPS',
'screenAwareness.debug.useVisionModel': '使用视觉模型',
'screenAwareness.debug.useVisionModelDesc':
'将截图发送给视觉 LLM 以获取更丰富的上下文。关闭时,仅使用 OCR 文本配合纯文本 LLM:速度更快且无需视觉模型。',
'screenAwareness.debug.keepScreenshots': '保留截图',
'screenAwareness.debug.keepScreenshotsDesc': '将捕获的截图保存到工作区,而非处理后删除',
'screenAwareness.debug.allowlist': '允许列表(每行一条规则)',
'screenAwareness.debug.denylist': '拒绝名单(每行一条规则)',
'screenAwareness.debug.saveSettings': '保存屏幕智能设置',
'screenAwareness.debug.sessionStats': '会话统计',
'screenAwareness.debug.framesEphemeral': '框架(临时)',
'screenAwareness.debug.panicStop': '紧急停止',
'screenAwareness.debug.defaultPanicHotkey': 'Cmd+Shift+。',
'screenAwareness.debug.vision': '愿景',
'screenAwareness.debug.idle': '空闲',
'screenAwareness.debug.visionQueue': '视觉队列',
'screenAwareness.debug.lastVision': '最后的愿景',
'screenAwareness.debug.notAvailable': '不适用',
'screenAwareness.debug.visionSummaries': '愿景摘要',
'screenAwareness.debug.refreshing': '清爽…',
'screenAwareness.debug.noSummaries': '还没有总结。',
'screenAwareness.debug.unknownApp': '未知应用程序',
'screenAwareness.debug.macosOnly': 'Screen Intelligence V1 目前仅在 macOS 上受支持。',
'memory.debugTitle': '记忆调试',
'memory.documents': '文件',
'memory.filterByNamespace': '按名称空间过滤...',
@@ -3662,33 +3609,6 @@ const messages: TranslationMap = {
'intelligence.diagram.imageAlt': '最新生成的 OpenHuman 架构图',
'intelligence.diagram.refreshesEvery': '每 {seconds} 秒刷新',
'intelligence.memoryText.entityTypePrefix': '实体类型',
'intelligence.screenDebug.active': '活跃',
'intelligence.screenDebug.app': '应用',
'intelligence.screenDebug.bounds': '边界',
'intelligence.screenDebug.captureAlt': '捕获测试结果',
'intelligence.screenDebug.captureFailed': '失败',
'intelligence.screenDebug.captureSuccess': '成功',
'intelligence.screenDebug.captureTest': '捕获测试',
'intelligence.screenDebug.capturing': '捕获中',
'intelligence.screenDebug.frames': '帧数',
'intelligence.screenDebug.idle': '空闲',
'intelligence.screenDebug.lastApp': '上一个应用',
'intelligence.screenDebug.mode': '模式',
'intelligence.screenDebug.permAccessibility': '辅助功能权限',
'intelligence.screenDebug.permInput': '输入监控权限',
'intelligence.screenDebug.permScreen': '辅助功能',
'intelligence.screenDebug.permissions': '权限',
'intelligence.screenDebug.platformNotSupported': '平台不支持',
'intelligence.screenDebug.recentVisionSummaries': '最近视觉摘要',
'intelligence.screenDebug.session': '会话',
'intelligence.screenDebug.size': '大小',
'intelligence.screenDebug.status': '状态',
'intelligence.screenDebug.testCapture': '测试捕获',
'intelligence.screenDebug.time': '时间',
'intelligence.screenDebug.title': '标题',
'intelligence.screenDebug.unknown': '未知',
'intelligence.screenDebug.visionQueue': '视觉队列',
'intelligence.screenDebug.visionState': '视觉状态',
'intelligence.tasks.activeBoardOne': '1 个跨对话的活跃看板',
'intelligence.tasks.activeBoardOther': '{count} 个跨对话的活跃看板',
'intelligence.tasks.empty': '暂无智能体任务看板',
@@ -4231,7 +4151,6 @@ const messages: TranslationMap = {
'overlay.ariaVoiceActive': '语音输入已激活',
'overlay.companion.error': '错误',
'overlay.companion.listening': '正在聆听…',
'overlay.companion.pointing': '正在指向…',
'overlay.companion.speaking': '正在说话…',
'overlay.companion.thinking': '正在思考…',
'overlay.orbTitle': '拖动以移动 · 双击重置位置',
@@ -4260,12 +4179,8 @@ const messages: TranslationMap = {
'pages.settings.features.messagingChannelsDesc': '配置消息渠道和集成',
'pages.settings.features.notifications': '通知',
'pages.settings.features.notificationsDesc': '管理通知偏好',
'pages.settings.features.screenAwareness': '屏幕感知',
'pages.settings.features.screenAwarenessDesc':
'让你的智能体查看你的屏幕,以便提供及时、贴合上下文的帮助。',
'pages.settings.features.tools': '工具',
'pages.settings.features.toolsDesc': '管理已连接的工具和集成',
'pages.settings.featuresSection.description': '屏幕感知、消息和工具。',
'pages.settings.featuresSection.title': '功能',
'privacy.dataKind.credentials': '凭据',
'privacy.dataKind.derived': '派生数据',
@@ -4988,8 +4903,6 @@ const messages: TranslationMap = {
'settings.localModel.status.working': '处理中...',
'settings.developerMenu.ai.title': 'AI 配置',
'settings.developerMenu.ai.desc': '云端提供商、本地 Ollama 模型和按工作负载路由',
'settings.developerMenu.screenAwareness.title': '屏幕感知',
'settings.developerMenu.screenAwareness.desc': '屏幕捕获权限、监控策略和会话控制',
'settings.developerMenu.messagingChannels.title': '消息渠道',
'settings.developerMenu.messagingChannels.desc': '配置 Telegram/Discord 认证模式和默认渠道路由',
'settings.developerMenu.tools.title': '工具',
@@ -5591,20 +5504,6 @@ const messages: TranslationMap = {
'settings.modelHealth.modal.apply': '应用替换',
'settings.modelHealth.tag.cheaper': '更便宜',
'settings.modelHealth.tag.better': '更好',
'settings.screenIntel.permissions.accessibility': '辅助功能',
'settings.screenIntel.permissions.grantHint': '在系统设置中授权后,点击下方刷新',
'settings.screenIntel.permissions.inputMonitoring': '输入监控',
'settings.screenIntel.permissions.macosAppliesPrivacy': 'macOS 会在所有窗口中应用隐私保护',
'settings.screenIntel.permissions.openInputMonitoring': '请求中…',
'settings.screenIntel.permissions.refreshStatus': '刷新中…',
'settings.screenIntel.permissions.refreshing': '刷新中…',
'settings.screenIntel.permissions.requestAccessibility': '请求中…',
'settings.screenIntel.permissions.requestScreenRecording': '请求中…',
'settings.screenIntel.permissions.requesting': '请求中…',
'settings.screenIntel.permissions.restartRefresh': '正在重启核心…',
'settings.screenIntel.permissions.restartingCore': '正在重启核心…',
'settings.screenIntel.permissions.screenRecording': '屏幕录制',
'settings.screenIntel.permissions.title': '权限',
'skills.card.moreActions': '更多操作',
'skills.channelIcon.discord': 'Discord',
'skills.channelIcon.imessage': 'iMessage',
@@ -5853,35 +5752,6 @@ const messages: TranslationMap = {
'skills.resource.tree.templates': '模板',
'skills.resource.tree.empty': '无捆绑资源。',
'skills.search.placeholder': '搜索技能...',
'skills.setup.screenIntel.activeDesc': '屏幕智能已激活,正在感知你的屏幕',
'skills.setup.screenIntel.activeTitle': '屏幕智能已启用',
'skills.setup.screenIntel.advancedSettings': '高级设置',
'skills.setup.screenIntel.allGranted': '所有权限已授予',
'skills.setup.screenIntel.captureMode': '捕获模式',
'skills.setup.screenIntel.captureModeValue': '所有窗口(稍后可配置)',
'skills.setup.screenIntel.deniedHint': '在系统设置中授权后,点击下方重启以应用更改。',
'skills.setup.screenIntel.enableBtn': '启用中...',
'skills.setup.screenIntel.enableDesc': '检测屏幕上的内容并将有用的上下文提供给助手',
'skills.setup.screenIntel.enableError': '启用屏幕智能失败',
'skills.setup.screenIntel.enabling': '启用中...',
'skills.setup.screenIntel.grant': '打开中...',
'skills.setup.screenIntel.granted': '已授予',
'skills.setup.screenIntel.macosOnly': '仅支持 macOS',
'skills.setup.screenIntel.opening': '打开中...',
'skills.setup.screenIntel.panicHotkey': '紧急热键',
'skills.setup.screenIntel.permAccessibility': '辅助功能',
'skills.setup.screenIntel.permInputMonitoring': '输入监控',
'skills.setup.screenIntel.permScreenRecording': '屏幕录制',
'skills.setup.screenIntel.permissionsDesc': '屏幕智能需要以下权限才能正常工作',
'skills.setup.screenIntel.permissionPathLabel': 'macOS 将隐私应用于:',
'skills.setup.screenIntel.refreshStatus': '刷新状态',
'skills.setup.screenIntel.restartRefresh': '重启中...',
'skills.setup.screenIntel.restarting': '重启中...',
'skills.setup.screenIntel.stepEnable': '启用技能',
'skills.setup.screenIntel.stepPermissions': '授予权限',
'skills.setup.screenIntel.stepSuccess': '准备就绪',
'skills.setup.screenIntel.title': '屏幕智能',
'skills.setup.screenIntel.visionModel': '视觉模型',
'skills.setup.voice.activation': '激活方式',
'skills.setup.voice.activeDescPrefix': 'Fn',
'skills.setup.voice.activeDescSuffix': 'Fn',
@@ -6031,8 +5901,6 @@ const messages: TranslationMap = {
'settings.localModel.status.thenRetry': '获取设置说明,待你的运行时可达后再重试。',
'devOptions.menuAi': 'AI 配置',
'devOptions.menuAiDesc': '云端提供商、本地 Ollama 模型与按工作负载路由',
'devOptions.menuScreenAware': '屏幕感知',
'devOptions.menuScreenAwareDesc': '屏幕捕获权限、监控策略与会话控制',
'devOptions.menuMessaging': '消息通道',
'devOptions.menuMessagingDesc': '配置 Telegram/Discord 认证模式与默认通道路由',
'devOptions.menuTools': '工具',
-45
View File
@@ -1,45 +0,0 @@
/**
* CompanionPointer renders point-target labels inside the overlay bubble area.
*/
import { useEffect, useState } from 'react';
export interface PointTarget {
absolute_x: number;
absolute_y: number;
label: string;
}
interface CompanionPointerProps {
targets: PointTarget[];
/** Auto-dismiss each target after this many ms. Default: 2000 */
dismissMs?: number;
}
export default function CompanionPointer({ targets, dismissMs = 2000 }: CompanionPointerProps) {
const [visible, setVisible] = useState(true);
useEffect(() => {
if (targets.length === 0) return;
const showFrame = window.requestAnimationFrame(() => setVisible(true));
const dismissTimer = window.setTimeout(() => setVisible(false), dismissMs);
return () => {
window.cancelAnimationFrame(showFrame);
window.clearTimeout(dismissTimer);
};
}, [targets, dismissMs]);
if (!visible || targets.length === 0) return null;
return (
<div className="flex flex-col items-end gap-1">
{targets.map((t, i) => (
<div
key={`${t.label}-${i}`}
className="animate-[overlay-bubble-in_220ms_ease-out] rounded-lg bg-blue-600 px-2 py-1 text-xs text-white shadow-md">
<span className="mr-1 inline-block h-2 w-2 rounded-full bg-blue-300 animate-pulse" />
{t.label}
</div>
))}
</div>
);
}
-2
View File
@@ -119,8 +119,6 @@ export function companionStateLabel(state: string, t: (key: string) => string):
return t('overlay.companion.thinking');
case 'speaking':
return t('overlay.companion.speaking');
case 'pointing':
return t('overlay.companion.pointing');
default:
return state;
}
@@ -1,80 +0,0 @@
import { act, render, screen } from '@testing-library/react';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import CompanionPointer, { type PointTarget } from '../CompanionPointer';
describe('CompanionPointer', () => {
beforeEach(() => {
vi.useFakeTimers();
// Make rAF fire synchronously so we can observe the visible→true transition
// without juggling async/await boundaries in every case.
vi.spyOn(window, 'requestAnimationFrame').mockImplementation(cb => {
cb(0);
return 1;
});
vi.spyOn(window, 'cancelAnimationFrame').mockImplementation(() => undefined);
});
afterEach(() => {
vi.useRealTimers();
vi.restoreAllMocks();
});
it('renders nothing when targets is empty', () => {
const { container } = render(<CompanionPointer targets={[]} />);
expect(container.firstChild).toBeNull();
});
it('renders one label per target with the label text', () => {
const targets: PointTarget[] = [
{ absolute_x: 10, absolute_y: 20, label: 'Click here' },
{ absolute_x: 30, absolute_y: 40, label: 'Then this' },
];
render(<CompanionPointer targets={targets} />);
expect(screen.getByText('Click here')).toBeInTheDocument();
expect(screen.getByText('Then this')).toBeInTheDocument();
});
it('auto-dismisses after dismissMs elapses', () => {
const { container } = render(
<CompanionPointer
targets={[{ absolute_x: 0, absolute_y: 0, label: 'Vanish' }]}
dismissMs={500}
/>
);
expect(screen.getByText('Vanish')).toBeInTheDocument();
act(() => {
vi.advanceTimersByTime(500);
});
expect(container.firstChild).toBeNull();
});
it('uses default dismissMs of 2000 when not provided', () => {
const { container } = render(
<CompanionPointer targets={[{ absolute_x: 0, absolute_y: 0, label: 'Default' }]} />
);
act(() => {
vi.advanceTimersByTime(1999);
});
expect(screen.getByText('Default')).toBeInTheDocument();
act(() => {
vi.advanceTimersByTime(1);
});
expect(container.firstChild).toBeNull();
});
it('cancels rAF and timer on unmount', () => {
const cancelTimerSpy = vi.spyOn(window, 'clearTimeout');
const cancelRafSpy = vi.spyOn(window, 'cancelAnimationFrame');
const { unmount } = render(
<CompanionPointer targets={[{ absolute_x: 0, absolute_y: 0, label: 'A' }]} />
);
unmount();
expect(cancelRafSpy).toHaveBeenCalled();
expect(cancelTimerSpy).toHaveBeenCalled();
});
});
@@ -11,7 +11,6 @@ describe('companionStateLabel', () => {
['listening', '“[overlay.companion.listening]”'],
['thinking', '“[overlay.companion.thinking]”'],
['speaking', '“[overlay.companion.speaking]”'],
['pointing', '“[overlay.companion.pointing]”'],
])('routes %s through the matching i18n key', (state, expected) => {
expect(companionStateLabel(state, t)).toBe(expected);
});
+3 -70
View File
@@ -23,12 +23,10 @@ import ComposioPanel from '../components/settings/panels/ComposioPanel';
import EmbeddingsPanel from '../components/settings/panels/EmbeddingsPanel';
import LlmConnectionsPanel from '../components/settings/panels/LlmConnectionsPanel';
import MeetingSettingsPanel from '../components/settings/panels/MeetingSettingsPanel';
import ScreenIntelligencePanel from '../components/settings/panels/ScreenIntelligencePanel';
import SearchPanel from '../components/settings/panels/SearchPanel';
import UsagePanel from '../components/settings/panels/UsagePanel';
import VoicePanel from '../components/settings/panels/VoicePanel';
import WalletPanel from '../components/settings/panels/WalletPanel';
import ScreenIntelligenceSetupModal from '../components/skills/ScreenIntelligenceSetupModal';
import UnifiedSkillCard from '../components/skills/SkillCard';
import { SKILL_CATEGORY_ORDER, type SkillCategory } from '../components/skills/skillCategories';
import SkillCategoryFilter from '../components/skills/SkillCategoryFilter';
@@ -41,7 +39,6 @@ import SkillSearchBar from '../components/skills/SkillSearchBar';
import SkillsExplorerTab from '../components/skills/SkillsExplorerTab';
import VoiceSetupModal from '../components/skills/VoiceSetupModal';
import BetaBanner from '../components/ui/BetaBanner';
import { useScreenIntelligenceSkillStatus } from '../features/screen-intelligence/useScreenIntelligenceSkillStatus';
import { useVoiceSkillStatus } from '../features/voice/useVoiceSkillStatus';
import { useChannelDefinitions } from '../hooks/useChannelDefinitions';
import { useAgentReadyComposioToolkits, useComposioIntegrations } from '../lib/composio/hooks';
@@ -398,18 +395,7 @@ const BUILT_IN_SKILLS: Array<{
description: string;
route: string;
icon: React.ReactNode;
}> = [
// Hidden — not active yet. Uncomment to re-enable.
// {
// id: 'screen-intelligence',
// title: 'Screen Intelligence',
// description:
// 'Capture windows, summarize what is on screen, and feed useful context into memory.',
// route: '/settings/screen-intelligence',
// icon: BUILT_IN_SKILL_ICONS.screenIntelligence,
// },
// text-autocomplete + voice-stt hidden per #717 (modals/status hooks retained for re-enable).
];
}> = [];
// ─── Item type for unified list ────────────────────────────────────────────────
@@ -455,7 +441,6 @@ type ConnectionsTab =
| 'usage'
| 'composio-key'
| 'wallet'
| 'screen-intelligence'
| 'companion';
/**
@@ -487,10 +472,6 @@ const INTELLIGENCE_HEADERS: Partial<Record<ConnectionsTab, { titleKey: string; d
},
search: { titleKey: 'settings.search.title', descKey: 'connections.header.search' },
usage: { titleKey: 'settings.usage.title', descKey: 'settings.usage.menuDesc' },
'screen-intelligence': {
titleKey: 'pages.settings.features.screenAwareness',
descKey: 'connections.header.screen',
},
companion: {
titleKey: 'pages.settings.features.desktopCompanion',
descKey: 'connections.header.companion',
@@ -509,7 +490,6 @@ const INTELLIGENCE_TABS: ReadonlySet<ConnectionsTab> = new Set<ConnectionsTab>([
'usage',
'composio-key',
'wallet',
'screen-intelligence',
'companion',
]);
@@ -540,7 +520,6 @@ export default function Skills() {
raw === 'usage' ||
raw === 'composio-key' ||
raw === 'wallet' ||
raw === 'screen-intelligence' ||
raw === 'companion'
)
return raw;
@@ -609,9 +588,7 @@ export default function Skills() {
const [composioModalToolkit, setComposioModalToolkit] = useState<ComposioToolkitMeta | null>(
null
);
const [screenIntelligenceModalOpen, setScreenIntelligenceModalOpen] = useState(false);
const [voiceModalOpen, setVoiceModalOpen] = useState(false);
const screenIntelligenceStatus = useScreenIntelligenceSkillStatus();
const voiceStatus = useVoiceSkillStatus();
const [toasts, setToasts] = useState<ToastNotification[]>([]);
@@ -872,39 +849,8 @@ export default function Skills() {
{items.map(item => {
if (item.kind === 'builtin') {
/* v8 ignore start -- BUILT_IN_SKILLS list is empty today; the per-id
branches below are kept for re-enabling screen-intelligence /
text-autocomplete / voice-stt and shouldn't drag the diff-coverage
gate down while they're unreachable. */
if (item.id === 'screen-intelligence') {
return (
<UnifiedSkillCard
key={item.id}
icon={item.icon}
title={item.name}
description={item.description}
statusLabel={screenIntelligenceStatus.statusLabel}
statusColor={screenIntelligenceStatus.statusColor}
ctaLabel={screenIntelligenceStatus.ctaLabel}
ctaVariant={screenIntelligenceStatus.ctaVariant}
testId={`skill-row-${item.id}`}
ctaTestId={`skill-install-${item.id}`}
onCtaClick={() => {
if (screenIntelligenceStatus.platformUnsupported) {
navigate(item.route!);
return;
}
if (
screenIntelligenceStatus.connectionStatus === 'connected' ||
screenIntelligenceStatus.connectionStatus === 'disconnected'
) {
navigate(item.route!);
return;
}
setScreenIntelligenceModalOpen(true);
}}
/>
);
}
branches below are retained for the hidden voice skill without
dragging the diff-coverage gate down while unreachable. */
if (item.id === 'voice-stt') {
return (
<UnifiedSkillCard
@@ -1020,11 +966,6 @@ export default function Skills() {
// Desktop capabilities relocated from Settings → Connections.
label: t('connections.groups.desktop'),
items: [
{
value: 'screen-intelligence',
label: t('pages.settings.features.screenAwareness'),
icon: navIcon('M3 5h18v12H3zM8 21h8m-4-4v4'),
},
{
value: 'companion',
label: t('pages.settings.features.desktopCompanion'),
@@ -1166,7 +1107,6 @@ export default function Skills() {
{activeTab === 'search' && <SearchPanel />}
{activeTab === 'usage' && <UsagePanel />}
{activeTab === 'composio-key' && <ComposioPanel />}
{activeTab === 'screen-intelligence' && <ScreenIntelligencePanel />}
{activeTab === 'companion' && <CompanionPanel />}
</SettingsLayoutProvider>
</div>
@@ -1433,13 +1373,6 @@ export default function Skills() {
<ChannelSetupModal definition={channelModalDef} onClose={() => setChannelModalDef(null)} />
)}
{screenIntelligenceModalOpen && (
<ScreenIntelligenceSetupModal
onClose={() => setScreenIntelligenceModalOpen(false)}
initialStep={screenIntelligenceStatus.allPermissionsGranted ? 'enable' : 'permissions'}
/>
)}
{voiceModalOpen && (
<VoiceSetupModal onClose={() => setVoiceModalOpen(false)} skillStatus={voiceStatus} />
)}
@@ -121,7 +121,7 @@ async function setupLayout(onboardingTasks: unknown = null) {
activeMode: 'os_keyring',
backendName: 'os',
},
runtime: { screenIntelligence: null, localAi: null, service: null },
runtime: { localAi: null, service: null },
},
isBootstrapping: false,
isReady: true,
+1 -5
View File
@@ -240,11 +240,7 @@ function normalizeSnapshot(
activeMode: 'os_keyring',
backendName: 'os',
},
runtime: {
screenIntelligence: result.runtime?.screenIntelligence ?? null,
localAi: result.runtime?.localAi ?? null,
service: result.runtime?.service ?? null,
},
runtime: { localAi: result.runtime?.localAi ?? null, service: result.runtime?.service ?? null },
};
}
@@ -44,7 +44,7 @@ function makeSnapshot(overrides: {
chatOnboardingCompleted: false,
analyticsEnabled: false,
localState: {},
runtime: { screenIntelligence: null as never, localAi: null as never, service: null as never },
runtime: { localAi: null as never, service: null as never },
};
}
@@ -77,7 +77,7 @@ function resetCoreStateStore() {
activeMode: 'os_keyring',
backendName: 'os',
},
runtime: { screenIntelligence: null, localAi: null, service: null },
runtime: { localAi: null, service: null },
},
teams: [],
teamMembersById: {},
@@ -37,7 +37,7 @@ function makeSnapshot(overrides: {
chatOnboardingCompleted: false,
analyticsEnabled: false,
localState: {},
runtime: { screenIntelligence: null as never, localAi: null as never, service: null as never },
runtime: { localAi: null as never, service: null as never },
};
}
@@ -104,7 +104,7 @@ function resetCoreStateStore() {
activeMode: 'os_keyring',
backendName: 'os',
},
runtime: { screenIntelligence: null, localAi: null, service: null },
runtime: { localAi: null, service: null },
},
teams: [],
teamMembersById: {},
@@ -55,7 +55,7 @@ describe('parseCompanionStateChangedEvent', () => {
});
it('accepts every valid state value', () => {
for (const state of ['idle', 'listening', 'thinking', 'speaking', 'pointing', 'error']) {
for (const state of ['idle', 'listening', 'thinking', 'speaking', 'error']) {
const event = parseCompanionStateChangedEvent({ sessionId: 's', state });
expect(event?.state).toBe(state);
}
@@ -3,7 +3,6 @@ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
import { dispatchLocalAiMethod } from '../../lib/ai/localCoreAiMemory';
import { CORE_RPC_TIMEOUT_MS } from '../../utils/config';
import type { AccessibilityStatus, CommandResponse } from '../../utils/tauriCommands';
import {
callCoreRpc,
classifyAuthExpiredReason,
@@ -12,56 +11,6 @@ import {
isThreadNotFoundCoreRpcError,
} from '../coreRpcClient';
function sampleAccessibilityStatus(
overrides: Partial<AccessibilityStatus> = {}
): AccessibilityStatus {
return {
platform_supported: true,
core_process: { pid: 4242, started_at_ms: 1712700000000 },
permissions: {
screen_recording: 'denied',
accessibility: 'granted',
input_monitoring: 'unknown',
},
features: { screen_monitoring: true },
session: {
active: false,
started_at_ms: null,
expires_at_ms: null,
remaining_ms: null,
ttl_secs: 300,
panic_hotkey: 'Cmd+Shift+.',
stop_reason: null,
frames_in_memory: 0,
last_capture_at_ms: null,
last_context: null,
vision_enabled: true,
vision_state: 'idle',
vision_queue_depth: 0,
last_vision_at_ms: null,
last_vision_summary: null,
},
config: {
enabled: true,
capture_policy: 'hybrid',
policy_mode: 'all_except_blacklist',
baseline_fps: 1,
vision_enabled: true,
session_ttl_secs: 300,
panic_stop_hotkey: 'Cmd+Shift+.',
autocomplete_enabled: true,
use_vision_model: true,
keep_screenshots: false,
allowlist: [],
denylist: [],
},
denylist: [],
is_context_blocked: false,
permission_check_process_path: '/tmp/openhuman-core-aarch64-apple-darwin',
...overrides,
};
}
vi.mock('@tauri-apps/api/core', () => ({ invoke: vi.fn(), isTauri: vi.fn(() => false) }));
vi.mock('../../lib/ai/localCoreAiMemory', () => ({
dispatchLocalAiMethod: vi.fn(async (_method: string) => ({ source: 'local-ai' })),
@@ -88,50 +37,6 @@ describe('coreRpcClient', () => {
expect(body.method).toBe('openhuman.auth_get_state');
});
test('maps accessibility prefix to screen intelligence prefix', async () => {
const fetchMock = vi.mocked(fetch);
fetchMock.mockResolvedValueOnce({
ok: true,
json: async () => ({ jsonrpc: '2.0', id: 2, result: { accepted: true } }),
} as Response);
await callCoreRpc({ method: 'openhuman.accessibility_status' });
const requestInit = fetchMock.mock.calls[0][1] as RequestInit;
const body = JSON.parse(String(requestInit.body));
expect(body.method).toBe('openhuman.screen_intelligence_status');
});
test('fetches accessibility_status CommandResponse with permissions and process path', async () => {
const fetchMock = vi.mocked(fetch);
const status = sampleAccessibilityStatus({
permission_check_process_path:
'/Users/dev/openhuman/app/src-tauri/binaries/openhuman-core-aarch64-apple-darwin',
});
const envelope: CommandResponse<AccessibilityStatus> = {
result: status,
logs: ['screen intelligence status fetched'],
};
fetchMock.mockResolvedValueOnce({
ok: true,
json: async () => ({ jsonrpc: '2.0', id: 99, result: envelope }),
} as Response);
const out = await callCoreRpc<CommandResponse<AccessibilityStatus>>({
method: 'openhuman.accessibility_status',
});
expect(out.logs).toContain('screen intelligence status fetched');
expect(out.result.permissions.screen_recording).toBe('denied');
expect(out.result.permissions.accessibility).toBe('granted');
expect(out.result.permissions.input_monitoring).toBe('unknown');
expect(out.result.core_process?.pid).toBe(4242);
expect(out.result.permission_check_process_path).toBe(
'/Users/dev/openhuman/app/src-tauri/binaries/openhuman-core-aarch64-apple-darwin'
);
});
test('throws clean error when JSON-RPC error payload is returned', async () => {
const fetchMock = vi.mocked(fetch);
fetchMock.mockResolvedValueOnce({
@@ -241,10 +146,6 @@ describe('coreRpcClient', () => {
['openhuman.update_memory_settings', 'openhuman.config_update_memory_settings'],
['openhuman.update_model_settings', 'openhuman.inference_update_model_settings'],
['openhuman.update_runtime_settings', 'openhuman.config_update_runtime_settings'],
[
'openhuman.update_screen_intelligence_settings',
'openhuman.config_update_screen_intelligence_settings',
],
[
'openhuman.workspace_onboarding_flag_exists',
'openhuman.config_workspace_onboarding_flag_exists',
+17 -32
View File
@@ -18,15 +18,6 @@ describe('rpcMethods catalog', () => {
expect(normalizeRpcMethod('openhuman.auth.a.b.c')).toBe('openhuman.auth_a_b_c');
});
test('transforms accessibility prefix to screen_intelligence prefix', () => {
expect(normalizeRpcMethod('openhuman.accessibility_status')).toBe(
'openhuman.screen_intelligence_status'
);
expect(normalizeRpcMethod('openhuman.accessibility_enable')).toBe(
'openhuman.screen_intelligence_enable'
);
});
test('returns unmapped or unrecognized methods unchanged', () => {
expect(normalizeRpcMethod('openhuman.threads_list')).toBe('openhuman.threads_list');
expect(normalizeRpcMethod('openhuman.unknown_method')).toBe('openhuman.unknown_method');
@@ -37,8 +28,8 @@ describe('rpcMethods catalog', () => {
test('trims whitespace and converts to lower case', () => {
expect(normalizeRpcMethod(' OpenHuman.Auth.Login ')).toBe('openhuman.auth_login');
expect(normalizeRpcMethod(' OPENHUMAN.GET_CONFIG ')).toBe(CORE_RPC_METHODS.configGet);
expect(normalizeRpcMethod('OpenHuman.Accessibility_Status ')).toBe(
'openhuman.screen_intelligence_status'
expect(normalizeRpcMethod('OpenHuman.Unrecognized_Status ')).toBe(
'openhuman.unrecognized_status'
);
expect(normalizeRpcMethod(' some_RANDOM_method ')).toBe('some_random_method');
});
@@ -137,10 +128,6 @@ describe('rpcMethods catalog', () => {
path.resolve(__dirname, '../../../../src/openhuman/config/schemas/schema_defs.rs'),
'utf8'
),
fs.readFileSync(
path.resolve(__dirname, '../../../../src/openhuman/screen_intelligence/schemas.rs'),
'utf8'
),
fs.readFileSync(
path.resolve(__dirname, '../../../../src/openhuman/inference/provider/schemas.rs'),
'utf8'
@@ -188,23 +175,21 @@ describe('rpcMethods catalog', () => {
// core.* methods (e.g. core.ping) are special dispatch methods, not in the schema catalog.
if (!method.startsWith('openhuman.')) continue;
const methodRoot = method.slice('openhuman.'.length);
const namespace = methodRoot.startsWith('screen_intelligence_')
? 'screen_intelligence'
: methodRoot.startsWith('inference_')
? 'inference'
: methodRoot.startsWith('embeddings_')
? 'embeddings'
: methodRoot.startsWith('providers_')
? 'providers'
: methodRoot.startsWith('mcp_clients_')
? 'mcp_clients'
: methodRoot.startsWith('health_')
? 'health'
: methodRoot.startsWith('channels_')
? 'channels'
: methodRoot.startsWith('tool_registry_')
? 'tool_registry'
: 'config';
const namespace = methodRoot.startsWith('inference_')
? 'inference'
: methodRoot.startsWith('embeddings_')
? 'embeddings'
: methodRoot.startsWith('providers_')
? 'providers'
: methodRoot.startsWith('mcp_clients_')
? 'mcp_clients'
: methodRoot.startsWith('health_')
? 'health'
: methodRoot.startsWith('channels_')
? 'channels'
: methodRoot.startsWith('tool_registry_')
? 'tool_registry'
: 'config';
const fnName = methodRoot.slice(`${namespace}_`.length);
expect(schemaSources).toContain(`namespace: "${namespace}"`);
expect(schemaSources).toContain(`function: "${fnName}"`);
-1
View File
@@ -23,7 +23,6 @@ const COMPANION_STATES: ReadonlySet<string> = new Set([
'listening',
'thinking',
'speaking',
'pointing',
'error',
]);
+1 -1
View File
@@ -22,7 +22,7 @@ function makeSnapshotResult(overrides: Record<string, unknown> = {}) {
activeMode: 'os_keyring',
backendName: 'os',
},
runtime: { screenIntelligence: {}, localAi: {}, service: {} },
runtime: { localAi: {}, service: {} },
...overrides,
};
}
+1 -6
View File
@@ -1,6 +1,5 @@
import type { User } from '../types/api';
import type { TeamInvite, TeamMember, TeamWithRole } from '../types/team';
import type { AccessibilityStatus } from '../utils/tauriCommands/accessibility';
import type { LocalAiStatus } from '../utils/tauriCommands/localAi';
import type { ServiceStatus } from '../utils/tauriCommands/service';
import { callCoreRpc } from './coreRpcClient';
@@ -57,11 +56,7 @@ interface AppStateSnapshotResult {
keyringConsent?: KeyringConsentPreference | null;
};
keyringStatus?: KeyringStatus;
runtime: {
screenIntelligence: AccessibilityStatus;
localAi: LocalAiStatus;
service: ServiceStatus;
};
runtime: { localAi: LocalAiStatus; service: ServiceStatus };
/**
* Process + component health, folded into this snapshot (#daemon-poll-fold)
* so the daemon-health store hydrates from the same poll instead of a second
-8
View File
@@ -28,7 +28,6 @@ export const CORE_RPC_METHODS = {
configUpdateModelSettings: 'openhuman.config_update_model_settings',
configUpdateRuntimeSettings: 'openhuman.config_update_runtime_settings',
configUpdateSandboxSettings: 'openhuman.config_update_sandbox_settings',
configUpdateScreenIntelligenceSettings: 'openhuman.config_update_screen_intelligence_settings',
configWorkspaceOnboardingFlagExists: 'openhuman.config_workspace_onboarding_flag_exists',
configWorkspaceOnboardingFlagSet: 'openhuman.config_workspace_onboarding_flag_set',
corePing: 'core.ping',
@@ -54,7 +53,6 @@ export const CORE_RPC_METHODS = {
inferenceUpdateModelSettings: 'openhuman.inference_update_model_settings',
inferenceWhisperInstallStatus: 'openhuman.inference_whisper_install_status',
providersListModels: 'openhuman.inference_list_models',
screenIntelligenceStatus: 'openhuman.screen_intelligence_status',
embeddingsGetSettings: 'openhuman.embeddings_get_settings',
embeddingsUpdateSettings: 'openhuman.embeddings_update_settings',
embeddingsSetApiKey: 'openhuman.embeddings_set_api_key',
@@ -104,8 +102,6 @@ export const LEGACY_METHOD_ALIASES: Record<string, CoreRpcMethod> = {
'openhuman.update_memory_settings': CORE_RPC_METHODS.configUpdateMemorySettings,
'openhuman.update_model_settings': CORE_RPC_METHODS.inferenceUpdateModelSettings,
'openhuman.update_runtime_settings': CORE_RPC_METHODS.configUpdateRuntimeSettings,
'openhuman.update_screen_intelligence_settings':
CORE_RPC_METHODS.configUpdateScreenIntelligenceSettings,
'openhuman.workspace_onboarding_flag_exists':
CORE_RPC_METHODS.configWorkspaceOnboardingFlagExists,
'openhuman.workspace_onboarding_flag_set': CORE_RPC_METHODS.configWorkspaceOnboardingFlagSet,
@@ -154,9 +150,5 @@ export function normalizeRpcMethod(method: string): string {
return `openhuman.auth_${normalized.slice('openhuman.auth.'.length).split('.').join('_')}`;
}
if (normalized.startsWith('openhuman.accessibility_')) {
return normalized.replace('openhuman.accessibility_', 'openhuman.screen_intelligence_');
}
return normalized;
}
@@ -29,7 +29,7 @@ function makeCoreState(token: string | null, userId: string | null = null): Core
activeMode: 'os_keyring',
backendName: 'os',
},
runtime: { screenIntelligence: null, localAi: null, service: null },
runtime: { localAi: null, service: null },
},
teams: [],
teamMembersById: {},
+3 -21
View File
@@ -94,13 +94,7 @@ describe('companionSlice', () => {
});
it('setConfig stores config object', () => {
const config = {
hotkey: 'ctrl+space',
activation_mode: 'push',
ttl_secs: 3600,
capture_screen: true,
include_app_context: true,
};
const config = { hotkey: 'ctrl+space', activation_mode: 'push', ttl_secs: 3600 };
const state = companionReducer(initialState, setConfig(config));
expect(state.config).toEqual(config);
});
@@ -121,13 +115,7 @@ describe('companionSlice', () => {
sessionActive: true,
state: 'speaking',
sessionId: 'sess-1',
config: {
hotkey: 'ctrl+space',
activation_mode: 'push',
ttl_secs: 3600,
capture_screen: true,
include_app_context: true,
},
config: { hotkey: 'ctrl+space', activation_mode: 'push', ttl_secs: 3600 },
lastError: 'some error',
};
const state = companionReducer(active, clearSession());
@@ -151,13 +139,7 @@ describe('companion selectors', () => {
sessionActive: true,
state: 'speaking' as const,
sessionId: 'sess-1',
config: {
hotkey: 'ctrl+space',
activation_mode: 'push',
ttl_secs: 3600,
capture_screen: true,
include_app_context: true,
},
config: { hotkey: 'ctrl+space', activation_mode: 'push', ttl_secs: 3600 },
lastError: 'err',
},
};
+1 -3
View File
@@ -5,15 +5,13 @@ import { resetUserScopedState } from './resetActions';
// ── Types matching the Tauri-side companion module ───────────────────
/** Matches `CompanionState` enum (serde rename_all = "snake_case"). */
export type CompanionState = 'idle' | 'listening' | 'thinking' | 'speaking' | 'pointing' | 'error';
export type CompanionState = 'idle' | 'listening' | 'thinking' | 'speaking' | 'error';
/** Matches `CompanionConfig` struct. */
export interface CompanionConfig {
hotkey: string;
activation_mode: string;
ttl_secs: number;
capture_screen: boolean;
include_app_context: boolean;
}
/**
+1 -9
View File
@@ -1,5 +1,5 @@
/**
* Shared Vitest mocks for screen-intelligence / voice status hooks.
* Shared Vitest mocks for voice status hooks.
* Import this module first in Skills page tests so `Skills` does not require `CoreStateProvider`.
*/
import { vi } from 'vitest';
@@ -14,14 +14,6 @@ const offlineStatusBase = {
ctaVariant: 'sage' as const,
};
vi.mock('../features/screen-intelligence/useScreenIntelligenceSkillStatus', () => ({
useScreenIntelligenceSkillStatus: () => ({
...offlineStatusBase,
allPermissionsGranted: false,
platformUnsupported: false,
}),
}));
vi.mock('../features/voice/useVoiceSkillStatus', () => ({
useVoiceSkillStatus: () => ({
...offlineStatusBase,
@@ -205,12 +205,6 @@ describe('formatTimelineEntry', () => {
).toEqual({ title: 'Git diff', detail: 'diff --stat' });
});
it('formats screenshot as a simple label', () => {
expect(formatTimelineEntry(entry({ name: 'screenshot' }))).toEqual({
title: 'Taking screenshot',
});
});
it('formats glob with pattern detail', () => {
expect(
formatTimelineEntry(
@@ -247,7 +241,6 @@ describe('formatToolName', () => {
expect(formatToolName('edit')).toBe('Editing file');
expect(formatToolName('grep')).toBe('Searching code');
expect(formatToolName('git_operations')).toBe('Git operation');
expect(formatToolName('screenshot')).toBe('Taking screenshot');
expect(formatToolName('lsp')).toBe('Code intelligence');
});
-1
View File
@@ -16,7 +16,6 @@ export type CapabilityCategory =
| 'team'
| 'settings'
| 'auth'
| 'screen_intelligence'
| 'channels'
| 'automation';
@@ -1,331 +0,0 @@
/**
* Accessibility and Screen Intelligence commands.
*/
import { callCoreRpc } from '../../services/coreRpcClient';
import { CommandResponse, isTauri } from './common';
export type AccessibilityPermissionState = 'granted' | 'denied' | 'unknown' | 'unsupported';
export type AccessibilityPermissionKind =
| 'screen_recording'
| 'accessibility'
| 'input_monitoring'
| 'microphone';
export interface AccessibilityPermissionStatus {
screen_recording: AccessibilityPermissionState;
accessibility: AccessibilityPermissionState;
input_monitoring: AccessibilityPermissionState;
/**
* Microphone permission. The Rust `PermissionStatus` always serializes this,
* but it's optional here so existing object literals (e.g. screen-intelligence
* status fixtures) that predate the desktop-agent panel still typecheck.
*/
microphone?: AccessibilityPermissionState;
}
export interface AccessibilityFeatures {
screen_monitoring: boolean;
}
export interface AccessibilitySessionStatus {
active: boolean;
started_at_ms: number | null;
expires_at_ms: number | null;
remaining_ms: number | null;
ttl_secs: number;
panic_hotkey: string;
stop_reason: string | null;
frames_in_memory: number;
last_capture_at_ms: number | null;
last_context: string | null;
vision_enabled: boolean;
vision_state: string;
vision_queue_depth: number;
last_vision_at_ms: number | null;
last_vision_summary: string | null;
}
export interface AccessibilityConfig {
enabled: boolean;
capture_policy: string;
policy_mode: 'all_except_blacklist' | 'whitelist_only' | string;
baseline_fps: number;
vision_enabled: boolean;
session_ttl_secs: number;
panic_stop_hotkey: string;
autocomplete_enabled: boolean;
use_vision_model: boolean;
keep_screenshots: boolean;
allowlist: string[];
denylist: string[];
}
export interface AccessibilityCoreProcessStatus {
pid: number;
started_at_ms: number;
}
export interface AccessibilityStatus {
platform_supported: boolean;
permissions: AccessibilityPermissionStatus;
features: AccessibilityFeatures;
session: AccessibilitySessionStatus;
config: AccessibilityConfig;
denylist: string[];
is_context_blocked: boolean;
/** Absolute path of the core binary; macOS TCC applies to this executable. */
permission_check_process_path?: string | null;
/** Identity of the core process currently serving RPC requests. */
core_process?: AccessibilityCoreProcessStatus | null;
}
export interface AccessibilityStartSessionParams {
consent: boolean;
ttl_secs?: number;
screen_monitoring?: boolean;
}
export interface AccessibilityStopSessionParams {
reason?: string;
}
export interface AccessibilityCaptureFrame {
captured_at_ms: number;
reason: string;
app_name: string | null;
window_title: string | null;
image_ref?: string | null;
}
export interface AccessibilityCaptureNowResult {
accepted: boolean;
frame: AccessibilityCaptureFrame | null;
}
export interface AccessibilityInputActionParams {
action: string;
x?: number;
y?: number;
button?: string;
text?: string;
key?: string;
modifiers?: string[];
}
export interface AccessibilityInputActionResult {
accepted: boolean;
blocked: boolean;
reason: string | null;
}
export interface AccessibilityAutocompleteSuggestion {
value: string;
confidence: number;
}
export interface AccessibilityAutocompleteSuggestParams {
context?: string;
max_results?: number;
}
export interface AccessibilityAutocompleteSuggestResult {
suggestions: AccessibilityAutocompleteSuggestion[];
}
export interface AccessibilityAutocompleteCommitParams {
suggestion: string;
}
export interface AccessibilityAutocompleteCommitResult {
committed: boolean;
}
export interface AccessibilityVisionSummary {
id: string;
captured_at_ms: number;
app_name: string | null;
window_title: string | null;
ui_state: string;
key_text: string;
actionable_notes: string;
confidence: number;
}
export interface AccessibilityVisionRecentResult {
summaries: AccessibilityVisionSummary[];
}
export interface AccessibilityVisionFlushResult {
accepted: boolean;
summary: AccessibilityVisionSummary | null;
}
export interface CaptureTestContextInfo {
app_name: string | null;
window_title: string | null;
bounds_x: number | null;
bounds_y: number | null;
bounds_width: number | null;
bounds_height: number | null;
}
export interface CaptureTestResult {
ok: boolean;
capture_mode: string;
context: CaptureTestContextInfo | null;
image_ref: string | null;
bytes_estimate: number | null;
error: string | null;
timing_ms: number;
}
export async function openhumanAccessibilityStatus(): Promise<
CommandResponse<AccessibilityStatus>
> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<AccessibilityStatus>>({
method: 'openhuman.accessibility_status',
serviceManaged: true,
});
}
export async function openhumanAccessibilityRequestPermissions(): Promise<
CommandResponse<AccessibilityPermissionStatus>
> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<AccessibilityPermissionStatus>>({
method: 'openhuman.accessibility_request_permissions',
serviceManaged: true,
});
}
export async function openhumanAccessibilityRequestPermission(
permission: AccessibilityPermissionKind
): Promise<CommandResponse<AccessibilityPermissionStatus>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<AccessibilityPermissionStatus>>({
method: 'openhuman.accessibility_request_permission',
params: { permission },
serviceManaged: true,
});
}
export async function openhumanAccessibilityStartSession(
params: AccessibilityStartSessionParams
): Promise<CommandResponse<AccessibilitySessionStatus>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<AccessibilitySessionStatus>>({
method: 'openhuman.accessibility_start_session',
params,
serviceManaged: true,
});
}
export async function openhumanAccessibilityStopSession(
params?: AccessibilityStopSessionParams
): Promise<CommandResponse<AccessibilitySessionStatus>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<AccessibilitySessionStatus>>({
method: 'openhuman.accessibility_stop_session',
params: params ?? {},
serviceManaged: true,
});
}
export async function openhumanAccessibilityCaptureNow(): Promise<
CommandResponse<AccessibilityCaptureNowResult>
> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<AccessibilityCaptureNowResult>>({
method: 'openhuman.accessibility_capture_now',
serviceManaged: true,
});
}
export async function openhumanAccessibilityInputAction(
params: AccessibilityInputActionParams
): Promise<CommandResponse<AccessibilityInputActionResult>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<AccessibilityInputActionResult>>({
method: 'openhuman.accessibility_input_action',
params,
serviceManaged: true,
});
}
export async function openhumanAccessibilityAutocompleteSuggest(
params?: AccessibilityAutocompleteSuggestParams
): Promise<CommandResponse<AccessibilityAutocompleteSuggestResult>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<AccessibilityAutocompleteSuggestResult>>({
method: 'openhuman.accessibility_autocomplete_suggest',
params: params ?? {},
serviceManaged: true,
});
}
export async function openhumanAccessibilityAutocompleteCommit(
params: AccessibilityAutocompleteCommitParams
): Promise<CommandResponse<AccessibilityAutocompleteCommitResult>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<AccessibilityAutocompleteCommitResult>>({
method: 'openhuman.accessibility_autocomplete_commit',
params,
serviceManaged: true,
});
}
export async function openhumanAccessibilityVisionRecent(
limit?: number
): Promise<CommandResponse<AccessibilityVisionRecentResult>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<AccessibilityVisionRecentResult>>({
method: 'openhuman.accessibility_vision_recent',
params: { limit },
serviceManaged: true,
});
}
export async function openhumanAccessibilityVisionFlush(): Promise<
CommandResponse<AccessibilityVisionFlushResult>
> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<AccessibilityVisionFlushResult>>({
method: 'openhuman.accessibility_vision_flush',
serviceManaged: true,
});
}
export async function openhumanScreenIntelligenceCaptureTest(): Promise<
CommandResponse<CaptureTestResult>
> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<CaptureTestResult>>({
method: 'openhuman.screen_intelligence_capture_test',
serviceManaged: true,
});
}
-25
View File
@@ -153,19 +153,6 @@ export interface BrowserSettingsUpdate {
backend?: 'agent_browser' | 'playwright' | 'rust_native' | 'computer_use' | 'auto' | null;
}
export interface ScreenIntelligenceSettingsUpdate {
enabled?: boolean | null;
capture_policy?: string | null;
policy_mode?: 'all_except_blacklist' | 'whitelist_only' | null;
baseline_fps?: number | null;
vision_enabled?: boolean | null;
autocomplete_enabled?: boolean | null;
use_vision_model?: boolean | null;
keep_screenshots?: boolean | null;
allowlist?: string[] | null;
denylist?: string[] | null;
}
export interface LocalAiSettingsUpdate {
runtime_enabled?: boolean | null;
/**
@@ -454,18 +441,6 @@ export async function openhumanUpdateBrowserSettings(
});
}
export async function openhumanUpdateScreenIntelligenceSettings(
update: ScreenIntelligenceSettingsUpdate
): Promise<CommandResponse<ConfigSnapshot>> {
if (!isTauri()) {
throw new Error('Not running in Tauri');
}
return await callCoreRpc<CommandResponse<ConfigSnapshot>>({
method: CORE_RPC_METHODS.configUpdateScreenIntelligenceSettings,
params: update,
});
}
// ── Agent access mode (autonomy / filesystem permissions) ───────────────────
export type AutonomyLevel = 'readonly' | 'supervised' | 'full';
-1
View File
@@ -16,7 +16,6 @@ export * from './cron';
export * from './taskSources';
export * from './heartbeat';
export * from './service';
export * from './accessibility';
export * from './voice';
export * from './aboutApp';
export * from './workspacePaths';
+1 -9
View File
@@ -56,14 +56,6 @@ export const TOOL_CATALOG: ToolDefinition[] = [
},
// Vision
{
id: 'screenshot',
displayName: 'Screenshot',
description: 'Capture screenshots of your screen.',
category: 'Vision',
defaultEnabled: true,
rustToolNames: ['screenshot'],
},
{
id: 'image_info',
displayName: 'Image Analysis',
@@ -155,7 +147,7 @@ export const TOOL_CATALOG: ToolDefinition[] = [
export const CATEGORY_DESCRIPTIONS: Record<ToolCategory, string> = {
System: 'Shell access and version control',
Files: 'Read and write files on disk',
Vision: 'Screen capture and image analysis',
Vision: 'Image analysis',
Web: 'Browser, HTTP, and web search',
Memory: 'Persistent recall for the AI',
Automation: 'Cron jobs and scheduled tasks',
-6
View File
@@ -35,7 +35,6 @@ const TOOL_DISPLAY_NAMES: Record<string, string> = {
git_operations: 'Git operation',
browser: 'Browsing',
browser_open: 'Opening browser',
screenshot: 'Taking screenshot',
image_info: 'Analyzing image',
install_tool: 'Installing tool',
lsp: 'Code intelligence',
@@ -115,7 +114,6 @@ const CLIENT_KNOWN_TOOLS = new Set<string>([
'git_operations',
'browser',
'browser_open',
'screenshot',
'image_info',
'install_tool',
'lsp',
@@ -189,7 +187,6 @@ const TOOL_CATEGORIES: Record<string, ToolCategory> = {
curl: 'fetch',
browser: 'browse',
browser_open: 'browse',
screenshot: 'browse',
};
/** Categorize a (possibly `subagent:`-prefixed) tool name for grouping/icons. */
@@ -545,9 +542,6 @@ function formatToolDetail(
return { title: host ? `Browsing ${host}` : 'Browsing' };
}
case 'screenshot':
return { title: 'Taking screenshot' };
case 'image_info':
return { title: 'Analyzing image' };
-8
View File
@@ -467,14 +467,6 @@ describe('Mega flow — login + Gmail OAuth + Composio in one session', () => {
// can reliably emit nested tool-call chains — neither is present.
// -------------------------------------------------------------------------
// -------------------------------------------------------------------------
// Scenario 9 — Accessibility permission flow.
// SKIPPED: No `openhuman.accessibility_*` RPC surface exists in the Rust
// controller registry. The `accessibility` domain name appears only in
// directory listings; it has no `schemas.rs` with registered controllers.
// If a future PR adds accessibility controllers, add scenarios here.
// -------------------------------------------------------------------------
// -------------------------------------------------------------------------
// Scenario 10 — Account switch + restore.
// Login as user A, create a thread, reset, login as user B, then re-login
@@ -1,136 +0,0 @@
import { browser, expect } from '@wdio/globals';
import { waitForApp } from '../helpers/app-helpers';
import {
clickButton,
dumpAccessibilityTree,
textExists,
waitForText,
} from '../helpers/element-helpers';
import { isTauriDriver } from '../helpers/platform';
import { resetApp } from '../helpers/reset-app';
import { navigateViaHash } from '../helpers/shared-flows';
import { clearRequestLog, startMockServer, stopMockServer } from '../mock-server';
function stepLog(message: string, context?: unknown): void {
const stamp = new Date().toISOString();
if (context === undefined) {
console.log(`[ScreenIntelligenceE2E][${stamp}] ${message}`);
return;
}
console.log(`[ScreenIntelligenceE2E][${stamp}] ${message}`, JSON.stringify(context, null, 2));
}
async function waitForCaptureOutcome(timeoutMs = 20_000): Promise<'success' | 'failure'> {
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
if (
(await textExists('Success')) &&
((await textExists('windowed')) || (await textExists('fullscreen')))
) {
return 'success';
}
if (
(await textExists('Failed')) ||
(await textExists('screen recording permission is not granted')) ||
(await textExists('screen capture is unsupported on this platform')) ||
(await textExists('screen capture failed'))
) {
return 'failure';
}
await browser.pause(500);
}
throw new Error('Timed out waiting for screen capture outcome');
}
describe('Screen Intelligence', () => {
before(async function () {
stepLog('Starting Screen Intelligence E2E');
await startMockServer();
await waitForApp();
await resetApp('e2e-screen-intelligence-user');
clearRequestLog();
});
after(async () => {
await stopMockServer();
});
it('opens the Screen Intelligence settings route', async function () {
if (!isTauriDriver()) {
this.skip();
return;
}
// Load the settings shell first so nested routes are available.
await browser.execute(() => {
window.location.hash = '/settings';
});
await browser.pause(2_000);
// Now navigate to the nested screen-intelligence route.
// Retry if the hash bounces (lazy component load may cause redirect).
for (let attempt = 0; attempt < 3; attempt++) {
await browser.execute(() => {
window.location.hash = '/settings/screen-intelligence';
});
await browser.pause(3_000);
const h = String(await browser.execute(() => window.location.hash));
if (h.includes('/settings/screen-intelligence')) break;
stepLog(`hash bounce attempt ${attempt}`, { hash: h });
}
const currentHash = await browser.execute(() => window.location.hash);
stepLog('Navigated to screen intelligence route', { currentHash });
// The panel renders the "Screen Awareness" title on every platform.
// The "Permissions" sub-section is only rendered when
// status.platform_supported is true (macOS) — on Linux/Windows the
// section is gated out by ScreenIntelligencePanel, so don't assert
// on it here.
await waitForText('Screen Awareness', 15_000);
});
it('triggers capture test and reaches a stable UI outcome', async function () {
if (!isTauriDriver()) {
this.skip();
return;
}
// The capture test UI lives in the debug panel, not the main panel.
await navigateViaHash('/settings/screen-awareness-debug');
await waitForText('Screen Awareness', 10_000);
// The Expand button opens the Debug & Diagnostics section.
// If not present, the debug panel may already be expanded.
if (await textExists('Expand')) {
await clickButton('Expand', 10_000);
}
await waitForText('Capture test', 10_000);
await clickButton('Test capture', 10_000);
const outcome = await waitForCaptureOutcome();
stepLog('Capture test outcome', { outcome });
if (outcome === 'success') {
const hasPreviewImage = await browser.execute(() => {
const img = document.querySelector('img[alt="Capture test result"]');
return !!img && !!img.getAttribute('src');
});
expect(hasPreviewImage).toBe(true);
expect((await textExists('windowed')) || (await textExists('fullscreen'))).toBe(true);
return;
}
const hasFailureGuidance =
(await textExists('Failed')) ||
(await textExists('screen recording permission is not granted')) ||
(await textExists('screen capture is unsupported on this platform')) ||
(await textExists('screen capture failed'));
if (!hasFailureGuidance) {
const tree = await dumpAccessibilityTree();
stepLog('Capture failure outcome missing expected guidance', { tree: tree.slice(0, 4000) });
}
expect(hasFailureGuidance).toBe(true);
});
});
@@ -73,16 +73,25 @@ describe('Settings - Feature Preferences', function () {
await stopMockServer();
});
it('renders the screen-awareness settings route', async () => {
// The combined "Features" hub was retired: screen-awareness, notifications,
// and tools are now independent sidebar entries. The legacy /settings/features
// slug redirects to /settings/screen-intelligence (see Settings.tsx), which
// renders the Screen Awareness panel.
await navigateViaHash('/settings/features');
it('falls through removed feature routes to settings home', async () => {
for (const route of [
'/settings/features',
'/settings/screen-intelligence',
'/settings/screen-awareness-debug',
]) {
await navigateViaHash(route);
await browser.waitUntil(
async () => {
const hash = await browser.execute(() => window.location.hash);
return hash === '#/settings' || hash === '#/settings/account';
},
{ timeout: 15_000, timeoutMsg: `${route} did not normalize to the settings index` }
);
// ScreenIntelligencePanel renders SettingsSection title
// t('settings.features.screenAwareness') = 'Screen awareness'.
await waitForText('Screen awareness', 15_000);
const bodyText = await browser.$('body').getText();
expect(bodyText).not.toContain('Screen Intelligence');
expect(bodyText).not.toContain('Screen Awareness Debug');
}
});
it('persists the default messaging channel through redux state', async () => {
+36 -26
View File
@@ -29,11 +29,10 @@ const USER_ID = 'e2e-tool-browser';
* over JSON-RPC. Plus: the mock backend correctly records arbitrary HTTP
* requests (proving the side-channel browser-automation flows would emit
* against the mocked services is intact).
* - 7.1.2 `BrowserTool::parameters_schema` enumerates the automation
* surface (open / snapshot / click / fill / type / get_text / etc.).
* Asserting that `tools_agent`'s tool scope is wildcard (which would
* surface `browser` to the LLM) ensures the schema-driven tool surface is
* intact for the agent path.
* - 7.1.2 the live built-in tool catalog contains `browser` and advertises
* its DOM/accessibility `snapshot` action without the removed screenshot
* action. The exact action-enum schema is pinned by the BrowserTool Rust
* unit test.
*
* Future: when the harness gains a deterministic mock-LLM that emits
* structured `tool_calls`, the `it.skip` block below can flip into a real
@@ -63,6 +62,15 @@ interface ListDefinitionsResult {
definitions?: AgentDef[];
}
interface AgentToolInfo {
name?: string;
description?: string;
}
interface AvailableToolsResult {
tools?: AgentToolInfo[];
}
describe('System tools — Browser (open URL + automation registry)', () => {
before(async function () {
// resetApp bring-up can run ~25-30s and race the default 30s Mocha hook
@@ -71,6 +79,14 @@ describe('System tools — Browser (open URL + automation registry)', () => {
await startMockServer();
await waitForApp();
await resetApp(USER_ID);
const browserSettings = await callOpenhumanRpc('openhuman.config_update_browser_settings', {
enabled: true,
});
expect(browserSettings.ok).toBe(true);
const toolPreferences = await callOpenhumanRpc('openhuman.app_state_update_local_state', {
onboardingTasks: { enabledTools: ['browser'] },
});
expect(toolPreferences.ok).toBe(true);
clearRequestLog();
});
@@ -137,30 +153,24 @@ describe('System tools — Browser (open URL + automation registry)', () => {
expect(Array.isArray(log)).toBe(true);
});
it('7.1.2 browser-automation registry surface is reachable via the agent registry', async () => {
// BrowserTool's parameters_schema enumerates 22 actions (open, snapshot,
// click, fill, type, get_text, screenshot, …). Asserting tools_agent's
// wildcard scope is present means the LLM-facing tool surface that
// would expose this schema to a model is intact. The schema content
// itself is unit-tested in `browser_tests.rs::browser_tool_schema_*`.
const list = await callOpenhumanRpc<ListDefinitionsResult>(
'openhuman.agent_list_definitions',
it('7.1.2 live BrowserTool catalog advertises DOM snapshots without screenshots', async () => {
// `agent_registry_available_tools` builds its response from the wildcard
// tools-catalog agent's live `tool_specs()`, so this checks the actual
// BrowserTool metadata exposed to agents rather than merely finding an
// agent definition that might be able to resolve it. The exact
// parameters-schema action enum is pinned by
// `browser_tests.rs::browser_tool_schema_has_required_action`.
const list = await callOpenhumanRpc<AvailableToolsResult>(
'openhuman.agent_registry_available_tools',
{}
);
expect(list.ok).toBe(true);
const defs = list.result?.definitions ?? [];
// The integrations_agent and tools_agent both bring browser surfaces
// (the former via SaaS-specific scrapers, the latter via the generic
// `browser` automation tool). Confirm at least one is present.
const browserBearing = defs.filter(
d =>
d?.id === 'tools_agent' ||
d?.id === 'integrations_agent' ||
d?.id === 'researcher' ||
d?.id === 'planner'
);
stepLog('browser-bearing agent definitions', { ids: browserBearing.map(d => d.id) });
expect(browserBearing.length).toBeGreaterThan(0);
const tools = list.result?.tools ?? [];
const browserTool = tools.find(tool => tool?.name === 'browser');
stepLog('live browser tool metadata', browserTool);
expect(browserTool).toBeDefined();
expect(browserTool?.description).toContain("'snapshot'");
expect(browserTool?.description?.toLowerCase()).not.toContain('screenshot');
});
it.skip('(future #68) chat tool_calls drive `browser_open https://example.com` via mock LLM', () => {
@@ -161,7 +161,6 @@ async function setupMockRpc(page: Page, state: MockState) {
localState: {},
keyringStatus: { isUnlocked: true, hasPassphrase: false },
runtime: {
screenIntelligence: { enabled: false },
localAi: { enabled: false },
autocomplete: { enabled: false },
service: { running: false },
@@ -1,33 +0,0 @@
import { expect, test } from '@playwright/test';
import { bootAuthenticatedPage, waitForAppReady } from '../helpers/core-rpc';
test.describe('Screen Intelligence', () => {
test('opens the Screen Intelligence settings route', async ({ page }) => {
await bootAuthenticatedPage(page, 'pw-screen-intelligence', '/settings/screen-intelligence');
await waitForAppReady(page);
const text = await page.locator('#root').innerText();
expect(text.includes('Screen Awareness')).toBe(true);
});
test('debug route reaches a stable success or unsupported/failure state', async ({ page }) => {
await bootAuthenticatedPage(
page,
'pw-screen-intelligence-debug',
'/settings/screen-awareness-debug'
);
await waitForAppReady(page);
const text = await page.locator('#root').innerText();
expect(
[
'Screen Awareness',
'screen capture is unsupported on this platform',
'screen recording permission is not granted',
'Capture test',
'Test capture',
].some(marker => text.includes(marker))
).toBe(true);
});
});

Some files were not shown because too many files have changed in this diff Show More