Compare commits

..

8 Commits

Author SHA1 Message Date
Shantur Rathore
1f59e66065 ci: skip draft PR builds until ready 2026-03-22 19:30:13 +00:00
Pascal André
51ac7f152d perf(ui): defer Monaco secondary viewers 2026-03-21 22:56:02 +00:00
Pascal André
df74c06ba2 fix(ui): localize git changes overlay label 2026-03-21 22:56:02 +00:00
Pascal André
5f144ca24d fix(ui): retry deferred markdown renderer setup 2026-03-21 22:56:02 +00:00
Pascal André
de66b1349a fix(ui): tolerate markdown parts without ids 2026-03-21 22:56:02 +00:00
Pascal André
3d888fee64 fix(ui): tighten diff viewer review follow-ups 2026-03-21 22:56:02 +00:00
Pascal André
1abcc8ee3c perf(ui): slim git diff syntax highlighting 2026-03-21 22:56:02 +00:00
Pascal André
d0d5c309e6 perf(ui): lazy-load markdown and diff rendering 2026-03-21 22:56:02 +00:00
70 changed files with 539 additions and 2109 deletions

View File

@@ -473,7 +473,6 @@ dependencies = [
"tauri",
"tauri-build",
"tauri-plugin-dialog",
"tauri-plugin-global-shortcut",
"tauri-plugin-notification",
"tauri-plugin-opener",
"thiserror 1.0.69",
@@ -1351,16 +1350,6 @@ dependencies = [
"version_check",
]
[[package]]
name = "gethostname"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8"
dependencies = [
"rustix 1.1.4",
"windows-link 0.2.1",
]
[[package]]
name = "getrandom"
version = "0.1.16"
@@ -1493,24 +1482,6 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]]
name = "global-hotkey"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9247516746aa8e53411a0db9b62b0e24efbcf6a76e0ba73e5a91b512ddabed7"
dependencies = [
"crossbeam-channel",
"keyboard-types",
"objc2",
"objc2-app-kit",
"once_cell",
"serde",
"thiserror 2.0.18",
"windows-sys 0.59.0",
"x11rb",
"xkeysym",
]
[[package]]
name = "gobject-sys"
version = "0.18.0"
@@ -4084,21 +4055,6 @@ dependencies = [
"url",
]
[[package]]
name = "tauri-plugin-global-shortcut"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "424af23c7e88d05e4a1a6fc2c7be077912f8c76bd7900fd50aa2b7cbf5a2c405"
dependencies = [
"global-hotkey",
"log",
"serde",
"serde_json",
"tauri",
"tauri-plugin",
"thiserror 2.0.18",
]
[[package]]
name = "tauri-plugin-notification"
version = "2.3.3"
@@ -5779,29 +5735,6 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "x11rb"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414"
dependencies = [
"gethostname",
"rustix 1.1.4",
"x11rb-protocol",
]
[[package]]
name = "x11rb-protocol"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd"
[[package]]
name = "xkeysym"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56"
[[package]]
name = "yoke"
version = "0.8.1"

View File

@@ -23,7 +23,6 @@ keepawake = "0.6"
tauri-plugin-dialog = "2"
dirs = "5"
tauri-plugin-opener = "2"
tauri-plugin-global-shortcut = "2"
url = "2"
tauri-plugin-notification = "2"

File diff suppressed because one or more lines are too long

View File

@@ -2378,72 +2378,6 @@
"const": "dialog:deny-save",
"markdownDescription": "Denies the save command without any pre-configured scope."
},
{
"description": "No features are enabled by default, as we believe\nthe shortcuts can be inherently dangerous and it is\napplication specific if specific shortcuts should be\nregistered or unregistered.\n",
"type": "string",
"const": "global-shortcut:default",
"markdownDescription": "No features are enabled by default, as we believe\nthe shortcuts can be inherently dangerous and it is\napplication specific if specific shortcuts should be\nregistered or unregistered.\n"
},
{
"description": "Enables the is_registered command without any pre-configured scope.",
"type": "string",
"const": "global-shortcut:allow-is-registered",
"markdownDescription": "Enables the is_registered command without any pre-configured scope."
},
{
"description": "Enables the register command without any pre-configured scope.",
"type": "string",
"const": "global-shortcut:allow-register",
"markdownDescription": "Enables the register command without any pre-configured scope."
},
{
"description": "Enables the register_all command without any pre-configured scope.",
"type": "string",
"const": "global-shortcut:allow-register-all",
"markdownDescription": "Enables the register_all command without any pre-configured scope."
},
{
"description": "Enables the unregister command without any pre-configured scope.",
"type": "string",
"const": "global-shortcut:allow-unregister",
"markdownDescription": "Enables the unregister command without any pre-configured scope."
},
{
"description": "Enables the unregister_all command without any pre-configured scope.",
"type": "string",
"const": "global-shortcut:allow-unregister-all",
"markdownDescription": "Enables the unregister_all command without any pre-configured scope."
},
{
"description": "Denies the is_registered command without any pre-configured scope.",
"type": "string",
"const": "global-shortcut:deny-is-registered",
"markdownDescription": "Denies the is_registered command without any pre-configured scope."
},
{
"description": "Denies the register command without any pre-configured scope.",
"type": "string",
"const": "global-shortcut:deny-register",
"markdownDescription": "Denies the register command without any pre-configured scope."
},
{
"description": "Denies the register_all command without any pre-configured scope.",
"type": "string",
"const": "global-shortcut:deny-register-all",
"markdownDescription": "Denies the register_all command without any pre-configured scope."
},
{
"description": "Denies the unregister command without any pre-configured scope.",
"type": "string",
"const": "global-shortcut:deny-unregister",
"markdownDescription": "Denies the unregister command without any pre-configured scope."
},
{
"description": "Denies the unregister_all command without any pre-configured scope.",
"type": "string",
"const": "global-shortcut:deny-unregister-all",
"markdownDescription": "Denies the unregister_all command without any pre-configured scope."
},
{
"description": "This permission set configures which\nnotification features are by default exposed.\n\n#### Granted Permissions\n\nIt allows all notification related features.\n\n\n#### This default permission set includes:\n\n- `allow-is-permission-granted`\n- `allow-request-permission`\n- `allow-notify`\n- `allow-register-action-types`\n- `allow-register-listener`\n- `allow-cancel`\n- `allow-get-pending`\n- `allow-remove-active`\n- `allow-get-active`\n- `allow-check-permissions`\n- `allow-show`\n- `allow-batch`\n- `allow-list-channels`\n- `allow-delete-channel`\n- `allow-create-channel`\n- `allow-permission-state`",
"type": "string",

View File

@@ -51,8 +51,6 @@ fn workspace_root() -> Option<PathBuf> {
const SESSION_COOKIE_NAME: &str = "codenomad_session";
const CLI_STOP_GRACE_SECS: u64 = 30;
#[cfg(windows)]
const CLI_WINDOWS_FORCE_GRACE_MS: u64 = 2_000;
#[cfg(unix)]
fn configure_posix_process_group(command: &mut Command) {
@@ -404,8 +402,6 @@ impl CliProcessManager {
let mut child_opt = self.child.lock();
if let Some(mut child) = child_opt.take() {
log_line(&format!("stopping CLI pid={}", child.id()));
#[cfg(windows)]
let mut forced_tree_shutdown = false;
#[cfg(unix)]
unsafe {
let pid = child.id() as i32;
@@ -418,7 +414,9 @@ impl CliProcessManager {
}
#[cfg(windows)]
{
let _ = kill_process_tree_windows(child.id(), false);
if !kill_process_tree_windows(child.id(), false) {
let _ = child.kill();
}
}
let start = Instant::now();
@@ -426,21 +424,6 @@ impl CliProcessManager {
match child.try_wait() {
Ok(Some(_)) => break,
Ok(None) => {
#[cfg(windows)]
if !forced_tree_shutdown
&& start.elapsed() > Duration::from_millis(CLI_WINDOWS_FORCE_GRACE_MS)
{
log_line(&format!(
"regular Windows shutdown still running after {}ms; escalating pid={}",
CLI_WINDOWS_FORCE_GRACE_MS,
child.id()
));
forced_tree_shutdown = true;
if !kill_process_tree_windows(child.id(), true) {
let _ = child.kill();
}
}
if start.elapsed() > Duration::from_secs(CLI_STOP_GRACE_SECS) {
log_line(&format!(
"stop timed out after {}s; sending SIGKILL pid={}",
@@ -457,11 +440,7 @@ impl CliProcessManager {
}
#[cfg(windows)]
{
if !forced_tree_shutdown
&& !kill_process_tree_windows(child.id(), true)
{
let _ = child.kill();
} else if forced_tree_shutdown {
if !kill_process_tree_windows(child.id(), true) {
let _ = child.kill();
}
}

View File

@@ -8,14 +8,10 @@ use serde::Deserialize;
use serde_json::json;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Mutex;
use std::time::{SystemTime, UNIX_EPOCH};
use tauri::menu::{MenuBuilder, MenuItem, SubmenuBuilder};
use tauri::plugin::{Builder as PluginBuilder, TauriPlugin};
use tauri::webview::Webview;
use tauri::{AppHandle, Emitter, Manager, Runtime, WindowEvent, Wry};
use tauri_plugin_global_shortcut::{
Code as ShortcutCode, GlobalShortcutExt, Shortcut, ShortcutState,
};
use tauri::{AppHandle, Emitter, Manager, Runtime, Wry};
use tauri_plugin_opener::OpenerExt;
use url::Url;
@@ -29,10 +25,6 @@ use std::os::windows::ffi::OsStrExt;
use windows_sys::Win32::UI::Shell::SetCurrentProcessExplicitAppUserModelID;
static QUIT_REQUESTED: AtomicBool = AtomicBool::new(false);
const DEFAULT_ZOOM_LEVEL: f64 = 1.0;
const ZOOM_STEP: f64 = 0.2;
const MIN_ZOOM_LEVEL: f64 = 0.2;
const MAX_ZOOM_LEVEL: f64 = 5.0;
#[cfg(windows)]
const WINDOWS_APP_USER_MODEL_ID: &str = "ai.neuralnomads.codenomad.client";
@@ -40,7 +32,6 @@ const WINDOWS_APP_USER_MODEL_ID: &str = "ai.neuralnomads.codenomad.client";
pub struct AppState {
pub manager: CliProcessManager,
pub wake_lock: Mutex<Option<KeepAwake>>,
pub zoom_level: Mutex<f64>,
}
#[derive(Debug, Default, Deserialize)]
@@ -166,83 +157,6 @@ fn emit_folder_drop_event(
}
}
fn clamp_zoom_level(value: f64) -> f64 {
value.clamp(MIN_ZOOM_LEVEL, MAX_ZOOM_LEVEL)
}
fn set_main_window_zoom(app_handle: &AppHandle, next_zoom: f64) {
if let Some(window) = app_handle.get_webview_window("main") {
let normalized = clamp_zoom_level(next_zoom);
if window.set_zoom(normalized).is_ok() {
if let Ok(mut zoom_level) = app_handle.state::<AppState>().zoom_level.lock() {
*zoom_level = normalized;
}
}
}
}
fn reload_main_window(app_handle: &AppHandle) {
if let Some(window) = app_handle.get_webview_window("main") {
let _ = window.reload();
}
}
fn force_reload_main_window(app_handle: &AppHandle) {
if let Some(window) = app_handle.get_webview_window("main") {
if let Ok(mut url) = window.url() {
if should_allow_internal(&url) {
let reload_token = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_millis()
.to_string();
let existing_pairs: Vec<(String, String)> = url
.query_pairs()
.into_owned()
.filter(|(key, _)| key != "__codenomad_force_reload")
.collect();
{
let mut pairs = url.query_pairs_mut();
pairs.clear();
for (key, value) in existing_pairs {
pairs.append_pair(&key, &value);
}
pairs.append_pair("__codenomad_force_reload", &reload_token);
}
let _ = window.navigate(url);
return;
}
}
let _ = window.reload();
}
}
fn toggle_fullscreen_window(app_handle: &AppHandle) {
if let Some(window) = app_handle.get_webview_window("main") {
let next_fullscreen = !window.is_fullscreen().unwrap_or(false);
let _ = window.set_fullscreen(next_fullscreen);
if cfg!(not(target_os = "macos")) {
if next_fullscreen {
let _ = window.hide_menu();
} else {
let _ = window.show_menu();
}
}
}
}
fn fullscreen_shortcut() -> Option<Shortcut> {
if cfg!(target_os = "macos") {
None
} else {
Some(Shortcut::new(None, ShortcutCode::F11))
}
}
#[cfg(windows)]
fn set_windows_app_user_model_id() {
let app_id: Vec<u16> = OsStr::new(WINDOWS_APP_USER_MODEL_ID)
@@ -267,48 +181,15 @@ fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_opener::init())
.plugin(
tauri_plugin_global_shortcut::Builder::new()
.with_handler(|app, shortcut, event| {
if event.state() != ShortcutState::Pressed {
return;
}
if fullscreen_shortcut().as_ref() == Some(shortcut) {
toggle_fullscreen_window(app);
}
})
.build(),
)
.plugin(tauri_plugin_notification::init())
.plugin(navigation_guard)
.manage(AppState {
manager: CliProcessManager::new(),
wake_lock: Mutex::new(None),
zoom_level: Mutex::new(DEFAULT_ZOOM_LEVEL),
})
.setup(|app| {
set_windows_app_user_model_id();
build_menu(&app.handle())?;
if let Some(shortcut) = fullscreen_shortcut() {
let shortcut_manager = app.handle().global_shortcut();
let _ = shortcut_manager.register(shortcut.clone());
if let Some(window) = app.get_webview_window("main") {
let app_handle = app.handle().clone();
window.on_window_event(move |event| {
if let WindowEvent::Focused(focused) = event {
let shortcut_manager = app_handle.global_shortcut();
if *focused {
let _ = shortcut_manager.register(shortcut.clone());
} else {
let _ = shortcut_manager.unregister(shortcut.clone());
}
}
});
}
}
let dev_mode = is_dev_mode();
let app_handle = app.handle().clone();
let manager = app.state::<AppState>().manager.clone();
@@ -333,42 +214,36 @@ fn main() {
let _ = window.emit("menu:newInstance", ());
}
}
"close" => {
if let Some(window) = app_handle.get_webview_window("main") {
let _ = window.close();
}
}
"quit" => {
app_handle.exit(0);
}
// View menu
"reload" => {
reload_main_window(app_handle);
if let Some(window) = app_handle.get_webview_window("main") {
let _ = window.eval("window.location.reload()");
}
}
"force_reload" => {
force_reload_main_window(app_handle);
if let Some(window) = app_handle.get_webview_window("main") {
let _ = window.eval("window.location.reload(true)");
}
}
"toggle_devtools" => {
if let Some(window) = app_handle.get_webview_window("main") {
if window.is_devtools_open() {
window.close_devtools();
} else {
window.open_devtools();
}
}
}
"reset_zoom" => {
set_main_window_zoom(app_handle, DEFAULT_ZOOM_LEVEL);
}
"zoom_in" => {
if let Ok(zoom_level) = app_handle.state::<AppState>().zoom_level.lock() {
set_main_window_zoom(app_handle, *zoom_level + ZOOM_STEP);
}
}
"zoom_out" => {
if let Ok(zoom_level) = app_handle.state::<AppState>().zoom_level.lock() {
set_main_window_zoom(app_handle, *zoom_level - ZOOM_STEP);
window.open_devtools();
}
}
"toggle_fullscreen" => {
toggle_fullscreen_window(app_handle);
if let Some(window) = app_handle.get_webview_window("main") {
let _ = window.set_fullscreen(!window.is_fullscreen().unwrap_or(false));
}
}
// Window menu
@@ -382,11 +257,6 @@ fn main() {
let _ = window.maximize();
}
}
"close_window" => {
if let Some(window) = app_handle.get_webview_window("main") {
let _ = window.close();
}
}
// App menu (macOS)
"about" => {
@@ -474,7 +344,6 @@ fn main() {
fn build_menu(app: &AppHandle) -> tauri::Result<()> {
let is_mac = cfg!(target_os = "macos");
let is_linux = cfg!(target_os = "linux");
// Create submenus
let mut submenus = Vec::new();
@@ -502,74 +371,16 @@ fn build_menu(app: &AppHandle) -> tauri::Result<()> {
Some("CmdOrCtrl+N"),
)?;
let file_menu = if is_mac {
SubmenuBuilder::new(app, "File")
.item(&new_instance_item)
.separator()
.close_window()
.build()?
} else {
SubmenuBuilder::new(app, "File")
.item(&new_instance_item)
.separator()
.text("quit", "Quit")
.build()?
};
let file_menu = SubmenuBuilder::new(app, "File")
.item(&new_instance_item)
.separator()
.text(
if is_mac { "close" } else { "quit" },
if is_mac { "Close" } else { "Quit" },
)
.build()?;
submenus.push(file_menu);
let reload_item = MenuItem::with_id(app, "reload", "Reload", true, Some("CmdOrCtrl+R"))?;
let force_reload_item = MenuItem::with_id(
app,
"force_reload",
"Force Reload",
true,
Some("CmdOrCtrl+Shift+R"),
)?;
let toggle_devtools_item = MenuItem::with_id(
app,
"toggle_devtools",
"Toggle Developer Tools",
true,
Some("Alt+CmdOrCtrl+I"),
)?;
let reset_zoom_item =
MenuItem::with_id(app, "reset_zoom", "Actual Size", true, Some("CmdOrCtrl+0"))?;
let zoom_in_item = MenuItem::with_id(
app,
"zoom_in",
if is_mac { "Zoom In" } else { "Zoom In\tCtrl++" },
true,
None::<&str>,
)?;
let zoom_out_item = MenuItem::with_id(
app,
"zoom_out",
if is_mac {
"Zoom Out"
} else {
"Zoom Out\tCtrl+-"
},
true,
None::<&str>,
)?;
let toggle_fullscreen_item = MenuItem::with_id(
app,
"toggle_fullscreen",
if is_mac {
"Toggle Full Screen"
} else {
"Toggle Full Screen\tF11"
},
true,
if is_mac {
Some("Ctrl+Cmd+F")
} else {
None::<&str>
},
)?;
let close_window_item =
MenuItem::with_id(app, "close_window", "Close", true, Some("CmdOrCtrl+W"))?;
// Edit menu with predefined items for standard functionality
let edit_menu = SubmenuBuilder::new(app, "Edit")
.undo()
@@ -585,39 +396,20 @@ fn build_menu(app: &AppHandle) -> tauri::Result<()> {
// View menu
let view_menu = SubmenuBuilder::new(app, "View")
.item(&reload_item)
.item(&force_reload_item)
.item(&toggle_devtools_item)
.text("reload", "Reload")
.text("force_reload", "Force Reload")
.text("toggle_devtools", "Toggle Developer Tools")
.separator()
.item(&reset_zoom_item)
.item(&zoom_in_item)
.item(&zoom_out_item)
.separator()
.item(&toggle_fullscreen_item)
.text("toggle_fullscreen", "Toggle Full Screen")
.build()?;
submenus.push(view_menu);
// Window menu
let window_menu = if is_linux {
SubmenuBuilder::new(app, "Window")
.text("minimize", "Minimize")
.text("zoom", "Zoom")
.separator()
.item(&close_window_item)
.build()?
} else if is_mac {
SubmenuBuilder::new(app, "Window")
.minimize()
.maximize()
.build()?
} else {
SubmenuBuilder::new(app, "Window")
.minimize()
.maximize()
.separator()
.close_window()
.build()?
};
let window_menu = SubmenuBuilder::new(app, "Window")
.text("minimize", "Minimize")
.text("zoom", "Zoom")
.build()?;
submenus.push(window_menu);
// Build the main menu with all submenus

View File

@@ -45,7 +45,6 @@ const FolderSelectionView: Component<FolderSelectionViewProps> = (props) => {
{ value: "ru", label: "Русский" },
{ value: "ja", label: "日本語" },
{ value: "zh-Hans", label: "简体中文" },
{ value: "he", label: "עברית" },
]
const selectedLanguageOption = () => languageOptions.find((opt) => opt.value === locale()) ?? languageOptions[0]
@@ -342,7 +341,7 @@ const FolderSelectionView: Component<FolderSelectionViewProps> = (props) => {
class="w-full max-w-5xl h-full px-4 sm:px-8 pb-2 flex flex-col overflow-hidden"
aria-busy={isLoading() ? "true" : "false"}
>
<div class="absolute top-4" style="inset-inline-start: 1.5rem;">
<div class="absolute top-4 left-6">
<Select<LanguageOption>
value={selectedLanguageOption()}
onChange={(value) => {
@@ -386,7 +385,7 @@ const FolderSelectionView: Component<FolderSelectionViewProps> = (props) => {
</Select.Portal>
</Select>
</div>
<div class="absolute top-4 flex items-center gap-2" style="inset-inline-end: 1.5rem;">
<div class="absolute top-4 right-6 flex items-center gap-2">
<button
type="button"
class="selector-button selector-button-secondary w-auto p-2 inline-flex items-center justify-center"

View File

@@ -82,7 +82,7 @@ const InstanceInfo: Component<InstanceInfoProps> = (props) => {
<div class="panel-body space-y-3">
<div>
<div class="text-xs font-medium text-muted uppercase tracking-wide mb-1">{t("instanceInfo.labels.folder")}</div>
<div dir="ltr" class="text-xs text-primary font-mono break-all px-2 py-1.5 rounded border bg-surface-secondary border-base">
<div class="text-xs text-primary font-mono break-all px-2 py-1.5 rounded border bg-surface-secondary border-base">
{currentInstance().folder}
</div>
</div>
@@ -94,7 +94,7 @@ const InstanceInfo: Component<InstanceInfoProps> = (props) => {
<div class="text-xs font-medium text-muted uppercase tracking-wide mb-1">
{t("instanceInfo.labels.project")}
</div>
<div dir="ltr" class="text-xs font-mono px-2 py-1.5 rounded border truncate bg-surface-secondary border-base text-primary">
<div class="text-xs font-mono px-2 py-1.5 rounded border truncate bg-surface-secondary border-base text-primary">
{project().id}
</div>
</div>
@@ -137,7 +137,7 @@ const InstanceInfo: Component<InstanceInfoProps> = (props) => {
<div class="text-xs font-medium text-muted uppercase tracking-wide mb-1">
{t("instanceInfo.labels.binaryPath")}
</div>
<div dir="ltr" class="text-xs font-mono break-all px-2 py-1.5 rounded border bg-surface-secondary border-base text-primary">
<div class="text-xs font-mono break-all px-2 py-1.5 rounded border bg-surface-secondary border-base text-primary">
{currentInstance().binaryPath}
</div>
</div>
@@ -151,7 +151,7 @@ const InstanceInfo: Component<InstanceInfoProps> = (props) => {
<div class="space-y-1">
<For each={environmentEntries()}>
{([key, value]) => (
<div dir="ltr" class="flex items-center gap-2 px-2 py-1.5 rounded border bg-surface-secondary border-base">
<div class="flex items-center gap-2 px-2 py-1.5 rounded border bg-surface-secondary border-base">
<span class="text-xs font-mono font-medium flex-1 text-primary" title={key}>
{key}
</span>

View File

@@ -404,7 +404,6 @@ const InstanceWelcomeView: Component<InstanceWelcomeViewProps> = (props) => {
<div class="flex items-center gap-2">
<span
class="text-sm font-medium text-primary whitespace-normal break-words transition-colors"
dir="auto"
classList={{
"text-accent": isFocused(),
}}

View File

@@ -81,8 +81,7 @@ interface InstanceShellProps {
}
const InstanceShell2: Component<InstanceShellProps> = (props) => {
const { t, locale } = useI18n()
const isRTL = () => locale() === "he"
const { t } = useI18n()
const [sessionSidebarWidth, setSessionSidebarWidth] = createSignal(DEFAULT_SESSION_SIDEBAR_WIDTH)
const [rightDrawerWidth, setRightDrawerWidth] = createSignal(
@@ -372,7 +371,7 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
sx={{
width: `${sessionSidebarWidth()}px`,
flexShrink: 0,
borderInlineEnd: "1px solid var(--border-base)",
borderRight: "1px solid var(--border-base)",
backgroundColor: "var(--surface-secondary)",
height: "100%",
minHeight: 0,
@@ -414,7 +413,7 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
const modalProps = container ? { container: container as Element } : undefined
return (
<Drawer
anchor={isRTL() ? "right" : "left"}
anchor="left"
variant="temporary"
open={leftOpen()}
onClose={closeLeftDrawer}
@@ -423,7 +422,7 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
"& .MuiDrawer-paper": {
width: isPhoneLayout() ? "100vw" : `${sessionSidebarWidth()}px`,
boxSizing: "border-box",
borderInlineEnd: isPhoneLayout() ? "none" : "1px solid var(--border-base)",
borderRight: isPhoneLayout() ? "none" : "1px solid var(--border-base)",
backgroundColor: "var(--surface-secondary)",
backgroundImage: "none",
color: "var(--text-primary)",
@@ -481,7 +480,7 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
sx={{
width: `${rightDrawerWidth()}px`,
flexShrink: 0,
borderInlineStart: "1px solid var(--border-base)",
borderLeft: "1px solid var(--border-base)",
backgroundColor: "var(--surface-secondary)",
height: "100%",
minHeight: 0,
@@ -524,7 +523,7 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
const modalProps = container ? { container: container as Element } : undefined
return (
<Drawer
anchor={isRTL() ? "left" : "right"}
anchor="right"
variant="temporary"
open={rightOpen()}
onClose={closeRightDrawer}
@@ -533,7 +532,7 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
"& .MuiDrawer-paper": {
width: isPhoneLayout() ? "100vw" : `${rightDrawerWidth()}px`,
boxSizing: "border-box",
borderInlineStart: isPhoneLayout() ? "none" : "1px solid var(--border-base)",
borderLeft: isPhoneLayout() ? "none" : "1px solid var(--border-base)",
backgroundColor: "var(--surface-secondary)",
backgroundImage: "none",
color: "var(--text-primary)",
@@ -743,7 +742,7 @@ const InstanceShell2: Component<InstanceShellProps> = (props) => {
<Kbd shortcut="cmd+shift+p" />
</span>
<div class="ms-auto flex items-center gap-3">
<div class="ml-auto flex items-center gap-3">
<div class="connection-status-meta flex items-center gap-3">
<Show when={connectionStatus() === "connected"}>
<span class="status-indicator connected">

View File

@@ -1,10 +1,8 @@
import {
Show,
Suspense,
createEffect,
createMemo,
createSignal,
lazy,
onCleanup,
type Accessor,
type Component,
@@ -22,6 +20,11 @@ import type { Session } from "../../../../types/session"
import type { DrawerViewState } from "../types"
import type { DiffContextMode, DiffViewMode, DiffWordWrapMode, RightPanelTab } from "./types"
import ChangesTab from "./tabs/ChangesTab"
import FilesTab from "./tabs/FilesTab"
import GitChangesTab from "./tabs/GitChangesTab"
import StatusTab from "./tabs/StatusTab"
import { getDefaultWorktreeSlug, getOrCreateWorktreeClient, getWorktreeSlugForSession } from "../../../../stores/worktrees"
import { requestData } from "../../../../lib/opencode-api"
import { buildUnifiedDiffFromSdkPatch, tryReverseApplyUnifiedDiff } from "../../../../lib/unified-diff-reverse"
@@ -46,15 +49,6 @@ import {
readStoredRightPanelTab,
} from "../storage"
const LazyChangesTab = lazy(() => import("./tabs/ChangesTab"))
const LazyGitChangesTab = lazy(() => import("./tabs/GitChangesTab"))
const LazyFilesTab = lazy(() => import("./tabs/FilesTab"))
const LazyStatusTab = lazy(() => import("./tabs/StatusTab"))
function RightPanelTabFallback() {
return <div class="flex-1 min-h-0" />
}
interface RightPanelProps {
t: (key: string, vars?: Record<string, any>) => string
@@ -249,8 +243,7 @@ const RightPanel: Component<RightPanelProps> = (props) => {
const mode = activeSplitResize()
if (!mode) return
event.preventDefault()
const isRtl = typeof document !== "undefined" && document.documentElement.dir === "rtl"
const delta = (event.clientX - splitResizeStartX()) * (isRtl ? -1 : 1)
const delta = event.clientX - splitResizeStartX()
const next = clampSplitWidth(splitResizeStartWidth() + delta)
if (mode === "changes") setChangesSplitWidth(next)
else if (mode === "git-changes") setGitChangesSplitWidth(next)
@@ -273,8 +266,7 @@ const RightPanel: Component<RightPanelProps> = (props) => {
const touch = event.touches[0]
if (!touch) return
event.preventDefault()
const isRtl = typeof document !== "undefined" && document.documentElement.dir === "rtl"
const delta = (touch.clientX - splitResizeStartX()) * (isRtl ? -1 : 1)
const delta = touch.clientX - splitResizeStartX()
const next = clampSplitWidth(splitResizeStartWidth() + delta)
if (mode === "changes") setChangesSplitWidth(next)
else if (mode === "git-changes") setGitChangesSplitWidth(next)
@@ -573,13 +565,6 @@ const RightPanel: Component<RightPanelProps> = (props) => {
void loadBrowserEntries(browserPath())
})
createEffect(() => {
if (rightPanelTab() === "files") return
setBrowserSelectedContent(null)
setBrowserSelectedLoading(false)
setBrowserSelectedError(null)
})
createEffect(() => {
if (rightPanelTab() !== "git-changes") return
if (gitStatusLoading()) return
@@ -587,14 +572,6 @@ const RightPanel: Component<RightPanelProps> = (props) => {
void loadGitStatus()
})
createEffect(() => {
if (rightPanelTab() === "git-changes") return
setGitSelectedBefore(null)
setGitSelectedAfter(null)
setGitSelectedLoading(false)
setGitSelectedError(null)
})
const handleSelectChangesFile = (file: string, closeList: boolean) => {
setSelectedFile(file)
if (closeList) {
@@ -761,109 +738,101 @@ const RightPanel: Component<RightPanelProps> = (props) => {
<div class="flex-1 overflow-y-auto">
<Show when={rightPanelTab() === "changes"}>
<Suspense fallback={<RightPanelTabFallback />}>
<LazyChangesTab
t={props.t}
instanceId={props.instanceId}
activeSessionId={props.activeSessionId}
activeSessionDiffs={props.activeSessionDiffs}
selectedFile={selectedFile}
onSelectFile={handleSelectChangesFile}
diffViewMode={diffViewMode}
diffContextMode={diffContextMode}
diffWordWrapMode={diffWordWrapMode}
onViewModeChange={setDiffViewMode}
onContextModeChange={setDiffContextMode}
onWordWrapModeChange={setDiffWordWrapMode}
listOpen={changesListOpen}
onToggleList={toggleChangesList}
splitWidth={changesSplitWidth}
onResizeMouseDown={handleSplitResizeMouseDown("changes")}
onResizeTouchStart={handleSplitResizeTouchStart("changes")}
isPhoneLayout={props.isPhoneLayout}
/>
</Suspense>
<ChangesTab
t={props.t}
instanceId={props.instanceId}
activeSessionId={props.activeSessionId}
activeSessionDiffs={props.activeSessionDiffs}
selectedFile={selectedFile}
onSelectFile={handleSelectChangesFile}
diffViewMode={diffViewMode}
diffContextMode={diffContextMode}
diffWordWrapMode={diffWordWrapMode}
onViewModeChange={setDiffViewMode}
onContextModeChange={setDiffContextMode}
onWordWrapModeChange={setDiffWordWrapMode}
listOpen={changesListOpen}
onToggleList={toggleChangesList}
splitWidth={changesSplitWidth}
onResizeMouseDown={handleSplitResizeMouseDown("changes")}
onResizeTouchStart={handleSplitResizeTouchStart("changes")}
isPhoneLayout={props.isPhoneLayout}
/>
</Show>
<Show when={rightPanelTab() === "git-changes"}>
<Suspense fallback={<RightPanelTabFallback />}>
<LazyGitChangesTab
t={props.t}
activeSessionId={props.activeSessionId}
entries={gitStatusEntries}
statusLoading={gitStatusLoading}
statusError={gitStatusError}
selectedPath={gitSelectedPath}
selectedLoading={gitSelectedLoading}
selectedError={gitSelectedError}
selectedBefore={gitSelectedBefore}
selectedAfter={gitSelectedAfter}
mostChangedPath={gitMostChangedPath}
scopeKey={gitScopeKey}
diffViewMode={diffViewMode}
diffContextMode={diffContextMode}
diffWordWrapMode={diffWordWrapMode}
onViewModeChange={setDiffViewMode}
onContextModeChange={setDiffContextMode}
onWordWrapModeChange={setDiffWordWrapMode}
onOpenFile={(path: string) => void openGitFile(path)}
onRefresh={() => void refreshGitStatus()}
listOpen={gitChangesListOpen}
onToggleList={toggleGitList}
splitWidth={gitChangesSplitWidth}
onResizeMouseDown={handleSplitResizeMouseDown("git-changes")}
onResizeTouchStart={handleSplitResizeTouchStart("git-changes")}
isPhoneLayout={props.isPhoneLayout}
/>
</Suspense>
<GitChangesTab
t={props.t}
activeSessionId={props.activeSessionId}
entries={gitStatusEntries}
statusLoading={gitStatusLoading}
statusError={gitStatusError}
selectedPath={gitSelectedPath}
selectedLoading={gitSelectedLoading}
selectedError={gitSelectedError}
selectedBefore={gitSelectedBefore}
selectedAfter={gitSelectedAfter}
mostChangedPath={gitMostChangedPath}
scopeKey={gitScopeKey}
diffViewMode={diffViewMode}
diffContextMode={diffContextMode}
diffWordWrapMode={diffWordWrapMode}
onViewModeChange={setDiffViewMode}
onContextModeChange={setDiffContextMode}
onWordWrapModeChange={setDiffWordWrapMode}
onOpenFile={(path) => void openGitFile(path)}
onRefresh={() => void refreshGitStatus()}
listOpen={gitChangesListOpen}
onToggleList={toggleGitList}
splitWidth={gitChangesSplitWidth}
onResizeMouseDown={handleSplitResizeMouseDown("git-changes")}
onResizeTouchStart={handleSplitResizeTouchStart("git-changes")}
isPhoneLayout={props.isPhoneLayout}
/>
</Show>
<Show when={rightPanelTab() === "files"}>
<Suspense fallback={<RightPanelTabFallback />}>
<LazyFilesTab
t={props.t}
browserPath={browserPath}
browserEntries={browserEntries}
browserLoading={browserLoading}
browserError={browserError}
browserSelectedPath={browserSelectedPath}
browserSelectedContent={browserSelectedContent}
browserSelectedLoading={browserSelectedLoading}
browserSelectedError={browserSelectedError}
parentPath={browserParentPath}
scopeKey={browserScopeKey}
onLoadEntries={(path: string) => void loadBrowserEntries(path)}
onOpenFile={(path: string) => void openBrowserFile(path)}
onRefresh={() => void refreshFilesTab()}
listOpen={filesListOpen}
onToggleList={toggleFilesList}
splitWidth={filesSplitWidth}
onResizeMouseDown={handleSplitResizeMouseDown("files")}
onResizeTouchStart={handleSplitResizeTouchStart("files")}
isPhoneLayout={props.isPhoneLayout}
/>
</Suspense>
<FilesTab
t={props.t}
browserPath={browserPath}
browserEntries={browserEntries}
browserLoading={browserLoading}
browserError={browserError}
browserSelectedPath={browserSelectedPath}
browserSelectedContent={browserSelectedContent}
browserSelectedLoading={browserSelectedLoading}
browserSelectedError={browserSelectedError}
parentPath={browserParentPath}
scopeKey={browserScopeKey}
onLoadEntries={(path) => void loadBrowserEntries(path)}
onOpenFile={(path) => void openBrowserFile(path)}
onRefresh={() => void refreshFilesTab()}
listOpen={filesListOpen}
onToggleList={toggleFilesList}
splitWidth={filesSplitWidth}
onResizeMouseDown={handleSplitResizeMouseDown("files")}
onResizeTouchStart={handleSplitResizeTouchStart("files")}
isPhoneLayout={props.isPhoneLayout}
/>
</Show>
<Show when={rightPanelTab() === "status"}>
<Suspense fallback={<RightPanelTabFallback />}>
<LazyStatusTab
t={props.t}
instanceId={props.instanceId}
instance={props.instance}
activeSessionId={props.activeSessionId}
activeSession={props.activeSession}
activeSessionDiffs={props.activeSessionDiffs}
latestTodoState={props.latestTodoState}
backgroundProcessList={props.backgroundProcessList}
onOpenBackgroundOutput={props.onOpenBackgroundOutput}
onStopBackgroundProcess={props.onStopBackgroundProcess}
onTerminateBackgroundProcess={props.onTerminateBackgroundProcess}
expandedItems={rightPanelExpandedItems}
onExpandedItemsChange={handleAccordionChange}
onOpenChangesTab={openChangesTabFromStatus}
/>
</Suspense>
<StatusTab
t={props.t}
instanceId={props.instanceId}
instance={props.instance}
activeSessionId={props.activeSessionId}
activeSession={props.activeSession}
activeSessionDiffs={props.activeSessionDiffs}
latestTodoState={props.latestTodoState}
backgroundProcessList={props.backgroundProcessList}
onOpenBackgroundOutput={props.onOpenBackgroundOutput}
onStopBackgroundProcess={props.onStopBackgroundProcess}
onTerminateBackgroundProcess={props.onTerminateBackgroundProcess}
expandedItems={rightPanelExpandedItems}
onExpandedItemsChange={handleAccordionChange}
onOpenChangesTab={openChangesTabFromStatus}
/>
</Show>
</div>
</div>

View File

@@ -2,7 +2,6 @@ import type { Component } from "solid-js"
import { AlignJustify, FoldVertical, Split, UnfoldVertical, WrapText } from "lucide-solid"
import { useI18n } from "../../../../../lib/i18n"
import type { DiffContextMode, DiffViewMode, DiffWordWrapMode } from "../types"
interface DiffToolbarProps {
@@ -15,15 +14,14 @@ interface DiffToolbarProps {
}
const DiffToolbar: Component<DiffToolbarProps> = (props) => {
const { t } = useI18n()
const nextViewMode = (): DiffViewMode => (props.viewMode === "split" ? "unified" : "split")
const nextContextMode = (): DiffContextMode => (props.contextMode === "collapsed" ? "expanded" : "collapsed")
const nextWordWrapMode = (): DiffWordWrapMode => (props.wordWrapMode === "on" ? "off" : "on")
const viewModeTitle = () => (nextViewMode() === "split" ? t("instanceShell.diff.switchToSplit") : t("instanceShell.diff.switchToUnified"))
const viewModeTitle = () => (nextViewMode() === "split" ? "Switch to split view" : "Switch to unified view")
const contextModeTitle = () =>
nextContextMode() === "collapsed" ? t("instanceShell.diff.hideUnchanged") : t("instanceShell.diff.showFull")
const wordWrapTitle = () => (nextWordWrapMode() === "on" ? t("instanceShell.diff.enableWordWrap") : t("instanceShell.diff.disableWordWrap"))
nextContextMode() === "collapsed" ? "Hide unchanged regions" : "Show full file"
const wordWrapTitle = () => (nextWordWrapMode() === "on" ? "Enable word wrap" : "Disable word wrap")
return (
<div class="file-viewer-toolbar">

View File

@@ -1,6 +1,5 @@
import { Show, type Component, type JSX } from "solid-js"
import { useI18n } from "../../../../../lib/i18n"
import OverlayList from "./OverlayList"
type SplitFilePanelList = {
@@ -25,13 +24,12 @@ interface SplitFilePanelProps {
}
const SplitFilePanel: Component<SplitFilePanelProps> = (props) => {
const { t } = useI18n()
return (
<div class="files-tab-container">
<div class="files-tab-header">
<div class="files-tab-header-row">
<button type="button" class="files-toggle-button" onClick={props.onToggleList}>
{props.listOpen ? t("instanceShell.filesShell.hideFiles") : t("instanceShell.filesShell.showFiles")}
{props.listOpen ? "Hide files" : "Show files"}
</button>
{props.header}

View File

@@ -175,7 +175,7 @@ const FilesTab: Component<FilesTabProps> = (props) => {
title={props.t("instanceShell.rightPanel.actions.refresh")}
aria-label={props.t("instanceShell.rightPanel.actions.refresh")}
disabled={props.browserLoading()}
style={{ "margin-inline-start": "auto" }}
style={{ "margin-left": "auto" }}
onClick={() => props.onRefresh()}
>
<RefreshCw class={`h-4 w-4${props.browserLoading() ? " animate-spin" : ""}`} />

View File

@@ -82,7 +82,7 @@ const GitChangesTab: Component<GitChangesTabProps> = (props) => {
})
const emptyViewerMessage = createMemo(() => {
if (!hasSession()) return props.t("instanceShell.gitChanges.noSessionSelected")
if (!hasSession()) return props.t("instanceShell.sessionChanges.noSessionSelected")
const currentEntries = entries()
if (currentEntries === null) return props.t("instanceShell.gitChanges.loading")
if (nonDeleted().length === 0) return props.t("instanceShell.gitChanges.empty")

View File

@@ -46,9 +46,7 @@ export function useDrawerResize(options: DrawerResizeOptions): DrawerResizeApi {
if (!side) return
const startWidth = resizeStartWidth()
const clamp = side === "left" ? options.clampLeft : options.clampRight
const isRtl = typeof document !== "undefined" && document.documentElement.dir === "rtl"
const rawDelta = side === "left" ? clientX - resizeStartX() : resizeStartX() - clientX
const delta = isRtl ? -rawDelta : rawDelta
const delta = side === "left" ? clientX - resizeStartX() : resizeStartX() - clientX
const nextWidth = clamp(startWidth + delta)
applyDrawerWidth(side, nextWidth)
}

View File

@@ -244,7 +244,6 @@ export function Markdown(props: MarkdownProps) {
<div
ref={containerRef}
class="markdown-body"
dir="auto"
data-view="markdown"
data-part-id={resolved().partId}
data-markdown-theme={resolved().themeKey}

View File

@@ -1,6 +1,7 @@
import { For, Match, Show, Suspense, Switch, createEffect, createMemo, createSignal, lazy, onCleanup, untrack } from "solid-js"
import { For, Match, Show, Switch, createEffect, createMemo, createSignal, onCleanup, untrack } from "solid-js"
import { ChevronsDownUp, ChevronsUpDown, ExternalLink, FoldVertical, ListStart, Trash } from "lucide-solid"
import MessageItem from "./message-item"
import ToolCall from "./tool-call"
import type { InstanceMessageStore } from "../stores/message-v2/instance-store"
import type { ClientPart, MessageInfo } from "../types/message"
import { partHasRenderableText } from "../types/message"
@@ -28,12 +29,6 @@ const USER_BORDER_COLOR = "var(--message-user-border)"
const ASSISTANT_BORDER_COLOR = "var(--message-assistant-border)"
const TOOL_BORDER_COLOR = "var(--message-tool-border)"
const LazyToolCall = lazy(() => import("./tool-call"))
function ToolCallFallback() {
return <div class="tool-call tool-call-loading" />
}
type ToolCallPart = Extract<ClientPart, { type: "tool" }>
@@ -505,18 +500,16 @@ function ToolCallItem(props: ToolCallItemProps) {
</div>
</div>
<Suspense fallback={<ToolCallFallback />}>
<LazyToolCall
toolCall={resolvedToolPart()}
toolCallId={props.partId}
messageId={props.messageId}
messageVersion={messageVersion()}
partVersion={partVersion()}
instanceId={props.instanceId}
sessionId={props.sessionId}
onContentRendered={props.onContentRendered}
/>
</Suspense>
<ToolCall
toolCall={resolvedToolPart()}
toolCallId={props.partId}
messageId={props.messageId}
messageVersion={messageVersion()}
partVersion={partVersion()}
instanceId={props.instanceId}
sessionId={props.sessionId}
onContentRendered={props.onContentRendered}
/>
</div>
)}
</Show>
@@ -909,7 +902,6 @@ export default function MessageBlock(props: MessageBlockProps) {
onDeleteMessagesUpTo={props.onDeleteMessagesUpTo}
selectedMessageIds={props.selectedMessageIds}
onToggleSelectedMessage={props.onToggleSelectedMessage}
onContentRendered={props.onContentRendered}
/>
</Match>
</Switch>
@@ -1288,7 +1280,6 @@ interface ReasoningCardProps {
onDeleteMessagesUpTo?: (messageId: string) => void | Promise<void>
selectedMessageIds?: () => Set<string>
onToggleSelectedMessage?: (messageId: string, selected: boolean) => void
onContentRendered?: () => void
}
function ReasoningCard(props: ReasoningCardProps) {
@@ -1297,25 +1288,6 @@ function ReasoningCard(props: ReasoningCardProps) {
const [deletingMessage, setDeletingMessage] = createSignal(false)
const [deletingUpTo, setDeletingUpTo] = createSignal(false)
const isSelectedForDeletion = () => Boolean(props.selectedMessageIds?.().has(props.messageId))
let pendingRenderNotificationFrame: number | null = null
const notifyContentRendered = () => {
if (!props.onContentRendered || typeof requestAnimationFrame !== "function") return
if (pendingRenderNotificationFrame !== null) {
cancelAnimationFrame(pendingRenderNotificationFrame)
}
pendingRenderNotificationFrame = requestAnimationFrame(() => {
pendingRenderNotificationFrame = null
props.onContentRendered?.()
})
}
onCleanup(() => {
if (pendingRenderNotificationFrame !== null) {
cancelAnimationFrame(pendingRenderNotificationFrame)
pendingRenderNotificationFrame = null
}
})
createEffect(() => {
setExpanded(Boolean(props.defaultExpanded))
@@ -1384,12 +1356,6 @@ function ReasoningCard(props: ReasoningCardProps) {
const viewHideLabel = () =>
expanded() ? t("messageBlock.reasoning.indicator.hide") : t("messageBlock.reasoning.indicator.view")
createEffect(() => {
if (!expanded()) return
reasoningText()
notifyContentRendered()
})
const canDeleteMessage = () => Boolean(props.showDeleteMessage) && !deletingMessage()
const handleDeleteMessage = async (event: MouseEvent) => {
@@ -1531,7 +1497,7 @@ function ReasoningCard(props: ReasoningCardProps) {
<div class="message-reasoning-expanded">
<div class="message-reasoning-body">
<div class="message-reasoning-output" role="region" aria-label={t("messageBlock.reasoning.detailsAriaLabel")}>
<pre class="message-reasoning-text" dir="auto">{reasoningText() || ""}</pre>
<pre class="message-reasoning-text">{reasoningText() || ""}</pre>
</div>
</div>
</div>

View File

@@ -542,7 +542,7 @@ export default function MessageItem(props: MessageItemProps) {
</header>
<div class="pt-1 whitespace-pre-wrap break-words leading-[1.1]" dir="auto">
<div class="pt-1 whitespace-pre-wrap break-words leading-[1.1]">
<Show when={props.isQueued && isUser()}>
@@ -550,7 +550,7 @@ export default function MessageItem(props: MessageItemProps) {
</Show>
<Show when={errorMessage()}>
<div class="message-error-block" dir="auto"> {errorMessage()}</div>
<div class="message-error-block"> {errorMessage()}</div>
</Show>
<Show when={isGenerating()}>

View File

@@ -1,4 +1,5 @@
import { Match, Show, Suspense, Switch, lazy } from "solid-js"
import { Show, Match, Switch } from "solid-js"
import ToolCall from "./tool-call"
import { isItemExpanded, toggleItemExpanded } from "../stores/tool-call-state"
import { Markdown } from "./markdown"
import { useTheme } from "../lib/theme"
@@ -6,8 +7,6 @@ import { partHasRenderableText, SDKPart, TextPart, ClientPart } from "../types/m
type ToolCallPart = Extract<ClientPart, { type: "tool" }>
const LazyToolCall = lazy(() => import("./tool-call"))
interface MessagePartProps {
part: ClientPart
messageType?: "user" | "assistant"
@@ -134,12 +133,11 @@ export default function MessagePart(props: MessagePartProps) {
<Show when={!shouldHideTextPart() && partHasRenderableText(props.part)}>
<div
class={canRenderMarkdown() ? markdownContainerClass() : textContainerClass()}
dir="auto"
data-role={textContainerRole()}
data-part-type="text"
data-part-id={typeof (props.part as any)?.id === "string" ? (props.part as any).id : undefined}
>
<Show when={canRenderMarkdown()} fallback={<span class="text-primary" dir="auto">{plainTextContent()}</span>}>
<Show when={canRenderMarkdown()} fallback={<span class="text-primary">{plainTextContent()}</span>}>
<Markdown
part={createTextPartForMarkdown()}
instanceId={props.instanceId}
@@ -154,14 +152,12 @@ export default function MessagePart(props: MessagePartProps) {
</Match>
<Match when={partType() === "tool"}>
<Suspense fallback={<div class="tool-call tool-call-loading" />}>
<LazyToolCall
toolCall={props.part as ToolCallPart}
toolCallId={props.part?.id}
instanceId={props.instanceId}
sessionId={props.sessionId}
/>
</Suspense>
<ToolCall
toolCall={props.part as ToolCallPart}
toolCallId={props.part?.id}
instanceId={props.instanceId}
sessionId={props.sessionId}
/>
</Match>

View File

@@ -19,7 +19,7 @@ import type { DeleteHoverState } from "../types/delete-hover"
import { buildRecordDisplayData } from "../stores/message-v2/record-display-cache"
import { getPartCharCount } from "../lib/token-utils"
const SCROLL_SENTINEL_MARGIN_PX = 8
const SCROLL_SENTINEL_MARGIN_PX = 48
const MESSAGE_SCROLL_CACHE_SCOPE = "message-stream"
const QUOTE_SELECTION_MAX_LENGTH = 2000
const codeNomadLogo = new URL("../images/CodeNomad-Icon.png", import.meta.url).href

View File

@@ -295,7 +295,7 @@ export default function ModelSelector(props: ModelSelectorProps) {
{t("modelSelector.trigger.primary", { model: currentModelValue()?.name ?? t("modelSelector.none") })}
</span>
{currentModelValue() && (
<span class="selector-trigger-secondary" dir="ltr">
<span class="selector-trigger-secondary">
{currentModelValue()!.providerId}/{currentModelValue()!.id}
</span>
)}

View File

@@ -1,4 +1,4 @@
import { For, Show, Suspense, createMemo, createSignal, createEffect, lazy, onCleanup, type Component } from "solid-js"
import { For, Show, createMemo, createSignal, createEffect, onCleanup, type Component } from "solid-js"
import type { PermissionRequestLike } from "../types/permission"
import { getPermissionCallId, getPermissionDisplayTitle, getPermissionKind, getPermissionMessageId, getPermissionSessionId } from "../types/permission"
import { getQuestionCallId, getQuestionMessageId, getQuestionSessionId, type QuestionRequest } from "../types/question"
@@ -12,8 +12,7 @@ import {
} from "../stores/instances"
import { ensureSessionParentExpanded, loadMessages, sessions as sessionStateSessions, setActiveSessionFromList } from "../stores/sessions"
import { messageStoreBus } from "../stores/message-v2/bus"
const LazyToolCall = lazy(() => import("./tool-call"))
import ToolCall from "./tool-call"
interface PermissionApprovalModalProps {
instanceId: string
@@ -409,17 +408,15 @@ const PermissionApprovalModal: Component<PermissionApprovalModalProps> = (props)
}
>
{(data) => (
<Suspense fallback={<div class="tool-call tool-call-loading" />}>
<LazyToolCall
toolCall={data().toolPart}
toolCallId={data().toolPart.id}
messageId={data().messageId}
messageVersion={data().messageVersion}
partVersion={data().partVersion}
instanceId={props.instanceId}
sessionId={data().sessionId}
/>
</Suspense>
<ToolCall
toolCall={data().toolPart}
toolCallId={data().toolPart.id}
messageId={data().messageId}
messageVersion={data().messageVersion}
partVersion={data().partVersion}
instanceId={props.instanceId}
sessionId={data().sessionId}
/>
)}
</Show>
</div>

View File

@@ -1,5 +1,6 @@
import { Suspense, createEffect, createSignal, lazy, on, onCleanup, onMount, Show } from "solid-js"
import { createSignal, Show, onMount, onCleanup, createEffect, on } from "solid-js"
import { ArrowBigUp, ArrowBigDown } from "lucide-solid"
import UnifiedPicker from "./unified-picker"
import ExpandButton from "./expand-button"
import { clearAttachments, removeAttachment } from "../stores/attachments"
import { resolvePastedPlaceholders } from "../lib/prompt-placeholders"
@@ -19,7 +20,6 @@ import { usePromptAttachments } from "./prompt-input/usePromptAttachments"
import { usePromptPicker } from "./prompt-input/usePromptPicker"
import { usePromptKeyDown } from "./prompt-input/usePromptKeyDown"
const log = getLogger("actions")
const LazyUnifiedPicker = lazy(() => import("./unified-picker"))
function getConsumedPastedTextAttachmentIds(text: string, attachments: Attachment[]): string[] {
if (!text || attachments.length === 0) return []
@@ -467,20 +467,18 @@ export default function PromptInput(props: PromptInputProps) {
onDrop={handleDrop}
>
<Show when={showPicker() && instance()}>
<Suspense fallback={null}>
<LazyUnifiedPicker
open={showPicker()}
mode={pickerMode()}
onClose={handlePickerClose}
onSelect={handlePickerSelect}
agents={instanceAgents()}
commands={getCommands(props.instanceId)}
instanceClient={instance()!.client}
searchQuery={searchQuery()}
textareaRef={textareaRef}
workspaceId={props.instanceId}
/>
</Suspense>
<UnifiedPicker
open={showPicker()}
mode={pickerMode()}
onClose={handlePickerClose}
onSelect={handlePickerSelect}
agents={instanceAgents()}
commands={getCommands(props.instanceId)}
instanceClient={instance()!.client}
searchQuery={searchQuery()}
textareaRef={textareaRef}
workspaceId={props.instanceId}
/>
</Show>
<div class="flex flex-1 flex-col">
@@ -490,7 +488,6 @@ export default function PromptInput(props: PromptInputProps) {
<textarea
ref={textareaRef}
class={`prompt-input ${mode() === "shell" ? "shell-mode" : ""} ${expandState() === "expanded" ? "is-expanded" : ""}`}
dir="auto"
placeholder={getPlaceholder()}
value={prompt()}
onInput={handleInput}

View File

@@ -444,7 +444,7 @@ const SessionList: Component<SessionListProps> = (props) => {
</Show>
{rowProps.isChild ? <Bot class="w-4 h-4 flex-shrink-0" /> : <User class="w-4 h-4 flex-shrink-0" />}
<span class="session-item-title session-item-title--clamp" dir="auto">{title()}</span>
<span class="session-item-title session-item-title--clamp">{title()}</span>
</div>
</div>
<div class="session-item-row session-item-meta">

View File

@@ -76,7 +76,6 @@ const SessionRenameDialog: Component<SessionRenameDialogProps> = (props) => {
inputRef = element
}}
type="text"
dir="auto"
value={title()}
onInput={(event) => setTitle(event.currentTarget.value)}
placeholder={t("sessionRenameDialog.input.placeholder")}

View File

@@ -514,7 +514,6 @@ function ToolCallDetails(props: {
})
const { renderDiffContent } = createDiffContentRenderer({
toolState: props.toolState,
preferences: props.preferences,
setDiffViewMode: props.setDiffViewMode,
isDark: props.isDark,

View File

@@ -20,14 +20,6 @@ export function createAnsiContentRenderer(params: {
const runningAnsiRenderer = createAnsiStreamRenderer()
let runningAnsiSource = ""
const registerTracked = (element: HTMLDivElement | null) => {
params.scrollHelpers.registerContainer(element)
}
const registerUntracked = (element: HTMLDivElement | null) => {
params.scrollHelpers.registerContainer(element, { disableTracking: true })
}
const getMode = () => {
const version = params.partVersion?.()
return typeof version === "number" ? String(version) : undefined
@@ -44,8 +36,6 @@ export function createAnsiContentRenderer(params: {
const cached = cacheHandle.get<AnsiRenderCache>()
const mode = getMode()
const isRunningVariant = options.variant === "running"
const disableScrollTracking = !isRunningVariant
const registerRef = disableScrollTracking ? registerUntracked : registerTracked
let nextCache: AnsiRenderCache
@@ -97,9 +87,9 @@ export function createAnsiContentRenderer(params: {
}
return (
<div class={messageClass} ref={registerRef} onScroll={disableScrollTracking ? undefined : params.scrollHelpers.handleScroll}>
<pre class="tool-call-content tool-call-ansi" dir="auto" innerHTML={nextCache.html} />
{params.scrollHelpers.renderSentinel({ disableTracking: disableScrollTracking })}
<div class={messageClass} ref={params.scrollHelpers.registerContainer} onScroll={params.scrollHelpers.handleScroll}>
<pre class="tool-call-content tool-call-ansi" innerHTML={nextCache.html} />
{params.scrollHelpers.renderSentinel()}
</div>
)
}

View File

@@ -42,7 +42,7 @@ export function renderDiagnosticsSection(
{entry.displayPath}
<span class="tool-call-diagnostic-coords">:L{entry.line || "-"}:C{entry.column || "-"}</span>
</span>
<span class="tool-call-diagnostic-message" dir="auto">{entry.message}</span>
<span class="tool-call-diagnostic-message">{entry.message}</span>
</div>
)}
</For>

View File

@@ -1,5 +1,4 @@
import { Suspense, lazy, onMount, type Accessor, type JSXElement } from "solid-js"
import type { ToolState } from "@opencode-ai/sdk/v2"
import type { RenderCache } from "../../types/message"
import type { DiffViewMode } from "../../stores/preferences"
import type { DiffPayload, DiffRenderOptions, ToolScrollHelpers } from "./types"
@@ -32,7 +31,6 @@ type DiffPrefs = {
}
export function createDiffContentRenderer(params: {
toolState: Accessor<ToolState | undefined>
preferences: Accessor<DiffPrefs>
setDiffViewMode: (mode: DiffViewMode) => void
isDark: Accessor<boolean>
@@ -60,10 +58,7 @@ export function createDiffContentRenderer(params: {
const cacheHandle = selectedVariant === "permission-diff" ? params.permissionDiffCache : params.diffCache
const diffMode = () => (params.preferences().diffViewMode || "split") as DiffViewMode
const themeKey = params.isDark() ? "dark" : "light"
const state = params.toolState()
const disableScrollTracking = Boolean(
options?.disableScrollTracking || (state?.status !== "running" && state?.status !== "pending"),
)
const disableScrollTracking = Boolean(options?.disableScrollTracking)
const registerRef = disableScrollTracking ? registerUntracked : registerTracked
const baseEntryParams = cacheHandle.params() as any

View File

@@ -31,9 +31,10 @@ export function createMarkdownContentRenderer(params: {
const size = options.size || "default"
const disableHighlight = options.disableHighlight || false
const messageClass = `message-text tool-call-markdown${size === "large" ? " tool-call-markdown-large" : ""}`
const state = params.toolState()
const disableScrollTracking = options.disableScrollTracking || (state?.status !== "running" && state?.status !== "pending")
const disableScrollTracking = options.disableScrollTracking || false
const registerRef = disableScrollTracking ? registerUntracked : registerTracked
const state = params.toolState()
const shouldDeferMarkdown = Boolean(state && (state.status === "running" || state.status === "pending") && disableHighlight)
if (shouldDeferMarkdown) {
return (
@@ -42,7 +43,7 @@ export function createMarkdownContentRenderer(params: {
ref={registerRef}
onScroll={disableScrollTracking ? undefined : params.scrollHelpers.handleScroll}
>
<pre class="whitespace-pre-wrap break-words text-sm font-mono" dir="auto">{options.content}</pre>
<pre class="whitespace-pre-wrap break-words text-sm font-mono">{options.content}</pre>
{params.scrollHelpers.renderSentinel({ disableTracking: disableScrollTracking })}
</div>
)

View File

@@ -18,7 +18,6 @@ import {
setWorktreeSlugForParentSession,
} from "../stores/worktrees"
import { sessions } from "../stores/sessions"
import { useI18n } from "../lib/i18n"
const log = getLogger("session")
@@ -26,6 +25,8 @@ type WorktreeOption =
| { kind: "action"; key: "__create__"; label: string }
| { kind: "worktree"; key: string; slug: string; directory: string; raw: WorktreeDescriptor }
const CREATE_OPTION: WorktreeOption = { kind: "action", key: "__create__", label: "+ Create worktree" }
function preventSelectPress(event: PointerEvent | MouseEvent) {
// Prevent Select.Item from treating this as a selection.
// We intentionally prevent default to stop Kobalte's internal press handling.
@@ -70,7 +71,6 @@ interface WorktreeSelectorProps {
}
export default function WorktreeSelector(props: WorktreeSelectorProps) {
const { t } = useI18n()
const [isOpen, setIsOpen] = createSignal(false)
const [createOpen, setCreateOpen] = createSignal(false)
const [createSlug, setCreateSlug] = createSignal("")
@@ -99,8 +99,7 @@ export default function WorktreeSelector(props: WorktreeSelectorProps) {
directory: wt.directory,
raw: wt,
}))
const createOption: WorktreeOption = { kind: "action", key: "__create__", label: t("instanceShell.worktree.create") }
return [createOption, ...mapped]
return [CREATE_OPTION, ...mapped]
})
const selectedOption = createMemo<WorktreeOption | undefined>(() => {

View File

@@ -0,0 +1,200 @@
import { createLowlight, common } from "lowlight"
type AstNode = {
type: string
value?: string
children?: AstNode[]
startIndex?: number
endIndex?: number
lineNumber?: number
}
type SyntaxNodeEntry = {
node: AstNode
wrapper?: AstNode
}
type SyntaxFileLine = {
value: string
lineNumber: number
valueLength: number
nodeList: SyntaxNodeEntry[]
}
type LowlightApi = ReturnType<typeof createLowlight>
export function processAST(ast: { children: AstNode[] }) {
let lineNumber = 1
const syntaxObj: Record<number, SyntaxFileLine> = {}
const loopAST = (nodes: AstNode[], wrapper?: AstNode) => {
nodes.forEach((node) => {
if (node.type === "text") {
const textValue = node.value ?? ""
if (!textValue.includes("\n")) {
const valueLength = textValue.length
if (!syntaxObj[lineNumber]) {
node.startIndex = 0
node.endIndex = valueLength - 1
syntaxObj[lineNumber] = {
value: textValue,
lineNumber,
valueLength,
nodeList: [{ node, wrapper }],
}
} else {
node.startIndex = syntaxObj[lineNumber].valueLength
node.endIndex = node.startIndex + valueLength - 1
syntaxObj[lineNumber].value += textValue
syntaxObj[lineNumber].valueLength += valueLength
syntaxObj[lineNumber].nodeList.push({ node, wrapper })
}
node.lineNumber = lineNumber
return
}
const lines = textValue.split("\n")
node.children = node.children || []
for (let index = 0; index < lines.length; index++) {
const value = index === lines.length - 1 ? lines[index] : `${lines[index]}\n`
const currentLineNumber = index === 0 ? lineNumber : ++lineNumber
const valueLength = value.length
const childNode: AstNode = {
type: "text",
value,
startIndex: Infinity,
endIndex: Infinity,
lineNumber: currentLineNumber,
}
if (!syntaxObj[currentLineNumber]) {
childNode.startIndex = 0
childNode.endIndex = valueLength - 1
syntaxObj[currentLineNumber] = {
value,
lineNumber: currentLineNumber,
valueLength,
nodeList: [{ node: childNode, wrapper }],
}
} else {
childNode.startIndex = syntaxObj[currentLineNumber].valueLength
childNode.endIndex = childNode.startIndex + valueLength - 1
syntaxObj[currentLineNumber].value += value
syntaxObj[currentLineNumber].valueLength += valueLength
syntaxObj[currentLineNumber].nodeList.push({ node: childNode, wrapper })
}
node.children.push(childNode)
}
node.lineNumber = lineNumber
return
}
if (node.children) {
loopAST(node.children, node)
node.lineNumber = lineNumber
}
})
}
loopAST(ast.children)
return { syntaxFileObject: syntaxObj, syntaxFileLineNumber: lineNumber }
}
export function _getAST() {
return {}
}
const lowlight = createLowlight(common)
lowlight.register("vue", function hljsDefineVue(hljs: any) {
return {
subLanguage: "xml",
contains: [
hljs.COMMENT("<!--", "-->", { relevance: 10 }),
{
begin: /^(\s*)(<script>)/gm,
end: /^(\s*)(<\/script>)/gm,
subLanguage: "javascript",
excludeBegin: true,
excludeEnd: true,
},
{
begin: /^(?:\s*)(?:<script\s+lang=(["'])ts\1>)/gm,
end: /^(\s*)(<\/script>)/gm,
subLanguage: "typescript",
excludeBegin: true,
excludeEnd: true,
},
{
begin: /^(\s*)(<style(\s+scoped)?>)/gm,
end: /^(\s*)(<\/style>)/gm,
subLanguage: "css",
excludeBegin: true,
excludeEnd: true,
},
{
begin: /^(?:\s*)(?:<style(?:\s+scoped)?\s+lang=(["'])(?:s[ca]ss)\1(?:\s+scoped)?>)/gm,
end: /^(\s*)(<\/style>)/gm,
subLanguage: "scss",
excludeBegin: true,
excludeEnd: true,
},
{
begin: /^(?:\s*)(?:<style(?:\s+scoped)?\s+lang=(["'])stylus\1(?:\s+scoped)?>)/gm,
end: /^(\s*)(<\/style>)/gm,
subLanguage: "stylus",
excludeBegin: true,
excludeEnd: true,
},
],
}
})
let maxLineToIgnoreSyntax = 2000
const ignoreSyntaxHighlightList: (string | RegExp)[] = []
export const highlighter = {
name: "lowlight",
get maxLineToIgnoreSyntax() {
return maxLineToIgnoreSyntax
},
setMaxLineToIgnoreSyntax(value: number) {
maxLineToIgnoreSyntax = value
},
get ignoreSyntaxHighlightList() {
return ignoreSyntaxHighlightList
},
setIgnoreSyntaxHighlightList(values: (string | RegExp)[]) {
ignoreSyntaxHighlightList.length = 0
ignoreSyntaxHighlightList.push(...values)
},
getAST(raw: string, fileName?: string, lang?: string) {
const language = typeof lang === "string" ? lang.trim() : ""
if (
fileName &&
ignoreSyntaxHighlightList.some((item) => (item instanceof RegExp ? item.test(fileName) : fileName === item))
) {
return undefined
}
if (language && lowlight.registered(language)) {
return lowlight.highlight(language, raw)
}
return lowlight.highlightAuto(raw)
},
processAST(ast: { children: AstNode[] }) {
return processAST(ast)
},
hasRegisteredCurrentLang(lang: string) {
return lowlight.registered(lang)
},
getHighlighterEngine(): LowlightApi {
return lowlight
},
type: "class" as const,
}
export const versions = "local-common"

View File

@@ -2,32 +2,27 @@ import { createContext, createEffect, createMemo, createSignal, onCleanup, onMou
import type { ParentComponent } from "solid-js"
import { useConfig } from "../../stores/preferences"
import { enMessages } from "./messages/en"
import { esMessages } from "./messages/es"
import { frMessages } from "./messages/fr"
import { ruMessages } from "./messages/ru"
import { jaMessages } from "./messages/ja"
import { zhHansMessages } from "./messages/zh-Hans"
type Messages = Record<string, string>
export type TranslateParams = Record<string, unknown>
export type Locale = "en" | "es" | "fr" | "ru" | "ja" | "zh-Hans" | "he"
export type Locale = "en" | "es" | "fr" | "ru" | "ja" | "zh-Hans"
const SUPPORTED_LOCALES: readonly Locale[] = ["en", "es", "fr", "ru", "ja", "zh-Hans", "he"] as const
const SUPPORTED_LOCALES_BY_LOWER = new Map(SUPPORTED_LOCALES.map((locale) => [locale.toLowerCase(), locale]))
const RTL_LOCALES = new Set<Locale>(["he"])
const SUPPORTED_LOCALES: readonly Locale[] = ["en", "es", "fr", "ru", "ja", "zh-Hans"] as const
const localeMessagesCache = new Map<Locale, Messages>([["en", enMessages]])
const localeMessagesPromises = new Map<Locale, Promise<Messages>>()
const localeLoaders: Record<Locale, () => Promise<Messages>> = {
en: async () => enMessages,
es: async () => (await import("./messages/es")).esMessages,
fr: async () => (await import("./messages/fr")).frMessages,
ru: async () => (await import("./messages/ru")).ruMessages,
ja: async () => (await import("./messages/ja")).jaMessages,
"zh-Hans": async () => (await import("./messages/zh-Hans")).zhHansMessages,
he: async () => (await import("./messages/he")).heMessages,
}
function getLocaleDirection(locale: Locale): "ltr" | "rtl" {
return RTL_LOCALES.has(locale) ? "rtl" : "ltr"
const messagesByLocale: Record<Locale, Messages> = {
en: enMessages,
es: esMessages,
fr: frMessages,
ru: ruMessages,
ja: jaMessages,
"zh-Hans": zhHansMessages,
}
function normalizeLocaleTag(value: string): string {
@@ -39,7 +34,8 @@ function matchSupportedLocale(value: string | undefined): Locale | null {
const normalized = normalizeLocaleTag(value)
const lower = normalized.toLowerCase()
const exact = SUPPORTED_LOCALES_BY_LOWER.get(lower)
const supportedLower = new Map(SUPPORTED_LOCALES.map((locale) => [locale.toLowerCase(), locale]))
const exact = supportedLower.get(lower)
if (exact) return exact
const parts = lower.split("-")
@@ -47,11 +43,11 @@ function matchSupportedLocale(value: string | undefined): Locale | null {
if (!base) return null
if (base === "zh") {
const zhHans = SUPPORTED_LOCALES_BY_LOWER.get("zh-hans")
const zhHans = supportedLower.get("zh-hans")
return zhHans ?? null
}
const baseMatch = SUPPORTED_LOCALES_BY_LOWER.get(base)
const baseMatch = supportedLower.get(base)
return baseMatch ?? null
}
@@ -88,54 +84,8 @@ function translateFrom(messages: Messages, key: string, params?: TranslateParams
}
const [globalRevision, setGlobalRevision] = createSignal(0)
let globalMessages: Messages = enMessages
let globalLocale: Locale = "en"
function getMessagesForLocale(locale: Locale): Messages {
return localeMessagesCache.get(locale) ?? enMessages
}
async function loadLocaleMessages(locale: Locale): Promise<Messages> {
const cached = localeMessagesCache.get(locale)
if (cached) {
return cached
}
const pending = localeMessagesPromises.get(locale)
if (pending) {
return pending
}
const loader = localeLoaders[locale]
const promise = loader()
.then((messages) => {
localeMessagesCache.set(locale, messages)
localeMessagesPromises.delete(locale)
return messages
})
.catch((error) => {
localeMessagesPromises.delete(locale)
throw error
})
localeMessagesPromises.set(locale, promise)
return promise
}
export async function preloadLocaleMessages(preferredLocale?: string | null): Promise<Locale> {
const resolvedLocale = matchSupportedLocale(preferredLocale ?? undefined) ?? detectNavigatorLocale() ?? "en"
try {
globalMessages = await loadLocaleMessages(resolvedLocale)
globalLocale = resolvedLocale
setGlobalRevision((value) => value + 1)
return resolvedLocale
} catch {
globalMessages = enMessages
globalLocale = "en"
setGlobalRevision((value) => value + 1)
return "en"
}
}
const initialGlobalLocale: Locale = detectNavigatorLocale() ?? "en"
let globalMessages: Messages = messagesByLocale[initialGlobalLocale]
export function tGlobal(key: string, params?: TranslateParams): string {
globalRevision()
@@ -151,12 +101,9 @@ const I18nContext = createContext<I18nContextValue>()
export const I18nProvider: ParentComponent = (props) => {
const { preferences } = useConfig()
const [detectedLocale, setDetectedLocale] = createSignal<Locale>(globalLocale)
const [resolvedLocale, setResolvedLocale] = createSignal<Locale>(globalLocale)
const previousGlobalMessages = globalMessages
const previousGlobalLocale = globalLocale
const previousDocumentLanguage = typeof document !== "undefined" ? document.documentElement.lang : ""
const previousDocumentDirection = typeof document !== "undefined" ? document.documentElement.dir : ""
const [detectedLocale, setDetectedLocale] = createSignal<Locale>("en")
const previousMessages = globalMessages
onMount(() => {
const detected = detectNavigatorLocale()
@@ -168,56 +115,20 @@ export const I18nProvider: ParentComponent = (props) => {
return configured ?? detectedLocale() ?? "en"
})
const messages = createMemo<Messages>(() => getMessagesForLocale(resolvedLocale()))
const messages = createMemo<Messages>(() => messagesByLocale[locale()])
function t(key: string, params?: TranslateParams): string {
return translateFrom(messages(), key, params)
}
createEffect(() => {
const nextLocale = locale()
let cancelled = false
void loadLocaleMessages(nextLocale)
.then((loadedMessages) => {
if (cancelled) {
return
}
setResolvedLocale(nextLocale)
globalLocale = nextLocale
globalMessages = loadedMessages
setGlobalRevision((value) => value + 1)
})
.catch(() => {
if (cancelled) {
return
}
setResolvedLocale("en")
globalMessages = enMessages
globalLocale = "en"
setGlobalRevision((value) => value + 1)
})
onCleanup(() => {
cancelled = true
})
})
createEffect(() => {
if (typeof document === "undefined") return
const activeLocale = locale()
document.documentElement.dir = getLocaleDirection(activeLocale)
document.documentElement.lang = activeLocale
globalMessages = messages()
setGlobalRevision((value) => value + 1)
})
onCleanup(() => {
globalMessages = previousGlobalMessages
globalLocale = previousGlobalLocale
globalMessages = previousMessages
setGlobalRevision((value) => value + 1)
if (typeof document !== "undefined") {
document.documentElement.lang = previousDocumentLanguage
document.documentElement.dir = previousDocumentDirection
}
})
const value: I18nContextValue = {

View File

@@ -114,7 +114,6 @@ export const instanceMessages = {
"instanceShell.sessionChanges.filesChanged": "{count} files changed",
"instanceShell.sessionChanges.actions.show": "Show changes",
"instanceShell.gitChanges.noSessionSelected": "Select a session to view git changes.",
"instanceShell.gitChanges.loading": "Loading git changes...",
"instanceShell.gitChanges.empty": "No git changes yet.",
"instanceShell.gitChanges.deleted": "Deleted",
@@ -125,15 +124,6 @@ export const instanceMessages = {
"instanceShell.filesShell.viewerTitle": "Change viewer",
"instanceShell.filesShell.viewerPlaceholder": "Detailed change rendering will be added in the next step.",
"instanceShell.filesShell.viewerEmpty": "No file selected.",
"instanceShell.filesShell.hideFiles": "Hide files",
"instanceShell.filesShell.showFiles": "Show files",
"instanceShell.diff.hideUnchanged": "Hide unchanged regions",
"instanceShell.diff.showFull": "Show full file",
"instanceShell.diff.switchToSplit": "Switch to split view",
"instanceShell.diff.switchToUnified": "Switch to unified view",
"instanceShell.diff.enableWordWrap": "Enable word wrap",
"instanceShell.diff.disableWordWrap": "Disable word wrap",
"instanceShell.worktree.create": "+ Create worktree",
"instanceShell.plan.noSessionSelected": "Select a session to view plan.",
"instanceShell.plan.empty": "Nothing planned yet.",

View File

@@ -1,6 +0,0 @@
export const advancedSettingsMessages = {
"advancedSettings.title": "הגדרות מתקדמות",
"advancedSettings.environmentVariables.title": "משתני סביבה",
"advancedSettings.environmentVariables.subtitle": "מוחלים בכל פעם שמופע OpenCode חדש מופעל",
"advancedSettings.actions.close": "סגור",
} as const

View File

@@ -1,42 +0,0 @@
export const appMessages = {
"app.launchError.title": "לא ניתן להפעיל את OpenCode",
"app.launchError.description": "לא הצלחנו להפעיל את קובץ ה-OpenCode שנבחר. בדוק את פלט השגיאה למטה או בחר קובץ בינארי אחר מהגדרות OpenCode.",
"app.launchError.binaryPathLabel": "נתיב הקובץ הבינארי",
"app.launchError.errorOutputLabel": "פלט שגיאה",
"app.launchError.openAdvancedSettings": "פתח הגדרות OpenCode",
"app.launchError.close": "סגור",
"app.launchError.closeTitle": "סגור (Esc)",
"app.launchError.fallbackMessage": "הפעלת סביבת העבודה נכשלה",
"app.stopInstance.confirmMessage": "לעצור את מופע OpenCode? פעולה זו תעצור את השרת.",
"app.stopInstance.title": "עצור מופע",
"app.stopInstance.confirmLabel": "עצור",
"app.stopInstance.cancelLabel": "המשך להריץ",
"emptyState.logoAlt": "לוגו CodeNomad",
"emptyState.brandTitle": "CodeNomad",
"emptyState.tagline": "בחר תיקייה כדי להתחיל לתכנת עם AI",
"emptyState.actions.selectFolder": "בחר תיקייה",
"emptyState.actions.selecting": "בוחר...",
"emptyState.keyboardShortcut": "קיצור מקלדת: {shortcut}",
"emptyState.examples": "דוגמאות: {example}",
"emptyState.multipleInstances": "ניתן לפתוח מספר מופעים של אותה תיקייה",
"releases.upgradeRequired.title": "נדרש שדרוג",
"releases.upgradeRequired.message.withVersion": "שדרג ל-CodeNomad {version} כדי להשתמש בממשק המעודכן.",
"releases.upgradeRequired.message.noVersion": "שדרג את CodeNomad כדי להשתמש בממשק המעודכן.",
"releases.upgradeRequired.action.getUpdate": "קבל עדכון",
"releases.uiUpdated.title": "הממשק עודכן",
"releases.uiUpdated.message": "הממשק עודכן לגרסה {version}.",
"releases.devUpdateAvailable.title": "גרסת פיתוח זמינה",
"releases.devUpdateAvailable.message": "גרסת פיתוח חדשה זמינה: {version}.",
"releases.devUpdateAvailable.action": "צפה בגרסה",
"theme.mode.system": "מערכת",
"theme.mode.light": "בהיר",
"theme.mode.dark": "כהה",
"theme.toggle.title": "ערכת נושא: {mode}",
"theme.toggle.ariaLabel": "ערכת נושא: {mode}",
} as const

View File

@@ -1,176 +0,0 @@
export const commandMessages = {
"commandPalette.title": "לוח פקודות",
"commandPalette.description": "חיפוש והפעלה של פקודות",
"commandPalette.searchPlaceholder": "הקלד פקודה או חיפוש...",
"commandPalette.empty": "לא נמצאו פקודות עבור \"{query}\"",
"commandPalette.category.customCommands": "פקודות מותאמות אישית",
"commandPalette.category.instance": "מופע",
"commandPalette.category.session": "סשן",
"commandPalette.category.agentModel": "סוכן ומודל",
"commandPalette.category.inputFocus": "קלט ופוקוס",
"commandPalette.category.system": "מערכת",
"commandPalette.category.other": "אחר",
"commands.newInstance.label": "מופע חדש",
"commands.newInstance.description": "פתח בורר תיקיות ליצירת מופע חדש",
"commands.newInstance.keywords": "תיקייה, פרויקט, סביבת עבודה",
"commands.closeInstance.label": "סגור מופע",
"commands.closeInstance.description": "עצור את השרת של המופע הנוכחי",
"commands.closeInstance.keywords": "עצור, סגור",
"commands.nextInstance.label": "מופע הבא",
"commands.nextInstance.description": "עבור למופע הבא",
"commands.nextInstance.keywords": "החלף, נווט",
"commands.previousInstance.label": "מופע קודם",
"commands.previousInstance.description": "עבור למופע הקודם",
"commands.previousInstance.keywords": "החלף, נווט",
"commands.newSession.label": "סשן חדש",
"commands.newSession.description": "צור סשן הורה חדש",
"commands.newSession.keywords": "צור, התחל",
"commands.closeSession.label": "סגור סשן",
"commands.closeSession.description": "סגור את סשן ההורה הנוכחי",
"commands.closeSession.keywords": "סגור, עצור",
"commands.scrubSessions.label": "נקה סשנים",
"commands.scrubSessions.description": "הסר סשנים ריקים, סשני תת-סוכן שסיימו את משימתם הראשית, וסשני פיצול מיותרים.",
"commands.scrubSessions.keywords": "ניקוי, ריק, סשנים, הסר, מחק",
"commands.instanceInfo.label": "מידע על מופע",
"commands.instanceInfo.description": "פתח את סקירת המופע ללוגים וסטטוס",
"commands.instanceInfo.keywords": "מידע, לוגים, קונסולה, פלט",
"commands.nextSession.label": "סשן הבא",
"commands.nextSession.description": "עבור לסשן הבא",
"commands.nextSession.keywords": "החלף, נווט",
"commands.previousSession.label": "סשן קודם",
"commands.previousSession.description": "עבור לסשן הקודם",
"commands.previousSession.keywords": "החלף, נווט",
"commands.compactSession.label": "סכם סשן",
"commands.compactSession.description": "סכם ודחוס את הסשן הנוכחי",
"commands.compactSession.keywords": "סיכום, דחיסה",
"commands.compactSession.errorFallback": "סיכום הסשן נכשל",
"commands.compactSession.alert.title": "הסיכום נכשל",
"commands.compactSession.alert.message": "הסיכום נכשל: {message}",
"commands.undoLastMessage.label": "בטל הודעה אחרונה",
"commands.undoLastMessage.description": "בטל את ההודעה האחרונה",
"commands.undoLastMessage.keywords": "חזרה, ביטול",
"commands.undoLastMessage.none.title": "אין פעולות לביטול",
"commands.undoLastMessage.none.message": "אין מה לבטל",
"commands.undoLastMessage.failed.title": "הביטול נכשל",
"commands.undoLastMessage.failed.message": "ביטול ההודעה נכשל",
"commands.openModelSelector.label": "פתח בורר מודלים",
"commands.openModelSelector.description": "בחר מודל אחר",
"commands.openModelSelector.keywords": "מודל, llm, ai",
"commands.selectModelVariant.label": "בחר גרסת מודל",
"commands.selectModelVariant.description": "בחר רמת מאמץ חשיבה למודל הנוכחי",
"commands.selectModelVariant.keywords": "גרסה, חשיבה, מאמץ",
"commands.openAgentSelector.label": "פתח בורר סוכנים",
"commands.openAgentSelector.description": "בחר סוכן אחר",
"commands.openAgentSelector.keywords": "סוכן, מצב",
"commands.clearInput.label": "נקה קלט",
"commands.clearInput.description": "נקה את תיבת הטקסט של הפקודה",
"commands.clearInput.keywords": "נקה, אפס",
"commands.promptSubmitShortcut.label.default": "Enter: שורה חדשה, Cmd/Ctrl+Enter: שלח פקודה",
"commands.promptSubmitShortcut.label.swapped": "Enter: שלח פקודה, Cmd/Ctrl+Enter: שורה חדשה",
"commands.promptSubmitShortcut.description": "החלף את התנהגות Enter ו-Cmd/Ctrl+Enter בקלט הפקודה",
"commands.promptSubmitShortcut.keywords": "enter, cmd, ctrl, שלח, שורה חדשה, קיצור",
"commands.thinkingBlocks.label.show": "הצג חשיבה",
"commands.thinkingBlocks.label.hide": "הסתר חשיבה",
"commands.thinkingBlocks.description": "הצג או הסתר קטעי חשיבה של ה-AI",
"commands.thinkingBlocks.keywords": "חשיבה, הצג, הסתר",
"commands.timelineToolCalls.label.show": "הצג קריאות כלי בציר הזמן",
"commands.timelineToolCalls.label.hide": "הסתר קריאות כלי בציר הזמן",
"commands.timelineToolCalls.description": "הצג/הסתר קריאות כלי בציר הודעות",
"commands.timelineToolCalls.keywords": "ציר זמן, כלי, הצג, הסתר",
"commands.keyboardShortcutHints.label.show": "הצג רמזי קיצורי מקלדת",
"commands.keyboardShortcutHints.label.hide": "הסתר רמזי קיצורי מקלדת",
"commands.keyboardShortcutHints.description": "הצג או הסתר רמזי קיצורי מקלדת בכל הממשק",
"commands.keyboardShortcutHints.description.disabledWeb": "מושבת בממשק Web (רמזי קיצורים תמיד מוסתרים)",
"commands.keyboardShortcutHints.keywords": "קיצור, מקלדת, רמזים",
"commands.common.expanded": "פרוס",
"commands.common.collapsed": "מכווץ",
"commands.common.visible": "גלוי",
"commands.common.hidden": "מוסתר",
"commands.common.enabled": "מופעל",
"commands.common.disabled": "מושבת",
"commands.thinkingBlocksDefault.label": "תצוגת חשיבה: {state}",
"commands.thinkingBlocksDefault.description": "כווץ / פרוס קטעי חשיבה של ה-AI",
"commands.thinkingBlocksDefault.keywords": "חשיבה, פרוס, כווץ, ברירת מחדל",
"commands.diffViewSplit.label": "השתמש בתצוגת diff מפוצלת",
"commands.diffViewSplit.description": "הצג diff של קריאות כלי זה לצד זה",
"commands.diffViewSplit.keywords": "diff, מפוצל, תצוגה",
"commands.diffViewUnified.label": "השתמש בתצוגת diff מאוחדת",
"commands.diffViewUnified.description": "הצג diff של קריאות כלי בשורה אחת",
"commands.diffViewUnified.keywords": "diff, מאוחד, תצוגה",
"commands.toolOutputsDefault.label": "ברירת מחדל לפלטי כלים · {state}",
"commands.toolOutputsDefault.description": "החלף ברירת מחדל לפריסת פלטי כלים",
"commands.toolOutputsDefault.keywords": "כלי, פלט, פרוס, כווץ",
"commands.diagnosticsDefault.label": "ברירת מחדל לאבחון · {state}",
"commands.diagnosticsDefault.description": "החלף ברירת מחדל לפריסת פלט אבחון",
"commands.diagnosticsDefault.keywords": "אבחון, פרוס, כווץ",
"commands.toolInputsVisibility.label": "נראות קלטי כלים · {state}",
"commands.toolInputsVisibility.description": "הגדר נראות ברירת מחדל לארגומנטים של קריאות כלי",
"commands.toolInputsVisibility.keywords": "כלי, קלטים, ארגומנטים, נראות, הסתר, הצג",
"commands.tokenUsageDisplay.label": "תצוגת שימוש בטוקנים · {state}",
"commands.tokenUsageDisplay.description": "הצג או הסתר נתוני טוקנים ועלות להודעות הסוכן",
"commands.tokenUsageDisplay.keywords": "טוקן, שימוש, עלות, נתונים",
"commands.autoCleanupBlankSessions.label": "ניקוי אוטומטי של סשנים ריקים · {state}",
"commands.autoCleanupBlankSessions.description": "נקה אוטומטית סשנים ריקים בעת יצירת סשנים חדשים",
"commands.autoCleanupBlankSessions.keywords": "אוטומטי, ניקוי, ריק, סשנים",
"commands.showHelp.label": "הצג עזרה",
"commands.showHelp.description": "הצג קיצורי מקלדת ועזרה",
"commands.showHelp.keywords": "קיצורים, עזרה",
"commands.custom.argumentsPrompt.message": "ארגומנטים עבור /{name}",
"commands.custom.argumentsPrompt.title": "פקודה מותאמת אישית",
"commands.custom.argumentsPrompt.inputLabel": "ארגומנטים",
"commands.custom.argumentsPrompt.inputPlaceholder": "למשל: foo bar",
"commands.custom.argumentsPrompt.confirmLabel": "הפעל",
"commands.custom.argumentsPrompt.cancelLabel": "ביטול",
"commands.custom.argumentsPrompt.openFailed.message": "פתיחת תיבת ארגומנטים נכשלה.",
"commands.custom.argumentsPrompt.openFailed.title": "ארגומנטים לפקודה",
"commands.custom.entries.descriptionFallback": "פקודה מותאמת אישית",
"commands.custom.sessionRequired.message": "בחר סשן לפני הפעלת פקודה מותאמת אישית.",
"commands.custom.sessionRequired.title": "נדרש סשן",
"commands.custom.runFailed.message": "הפעלת הפקודה המותאמת אישית נכשלה. בדוק את הקונסולה לפרטים.",
"commands.custom.runFailed.title": "הפקודה נכשלה",
"unifiedPicker.loading.searching": "מחפש...",
"unifiedPicker.loading.loadingWorkspace": "טוען סביבת עבודה...",
"unifiedPicker.title.command": "בחר פקודה",
"unifiedPicker.title.mention": "בחר סוכן או קובץ",
"unifiedPicker.empty": "לא נמצאו תוצאות",
"unifiedPicker.sections.commands": "פקודות",
"unifiedPicker.sections.agents": "סוכנים",
"unifiedPicker.sections.files": "קבצים",
"unifiedPicker.sections.workspaceRoot": "שורש סביבת העבודה",
"unifiedPicker.badge.subagent": "תת-סוכן",
"unifiedPicker.footer.navigate": "ניווט",
"unifiedPicker.footer.select": "בחירה",
"unifiedPicker.footer.close": "סגירה",
} as const

View File

@@ -1,16 +0,0 @@
export const dialogMessages = {
"alertDialog.fallbackTitle.info": "לתשומת לבך",
"alertDialog.fallbackTitle.warning": "נא לבדוק",
"alertDialog.fallbackTitle.error": "משהו השתבש",
"alertDialog.actions.confirm": "אישור",
"alertDialog.actions.run": "הפעל",
"alertDialog.actions.ok": "אישור",
"alertDialog.actions.cancel": "ביטול",
"alertDialog.prompt.inputLabel": "קלט",
"backgroundProcessOutputDialog.title": "פלט תהליך רקע",
"backgroundProcessOutputDialog.actions.close": "סגור",
"backgroundProcessOutputDialog.loading": "טוען פלט...",
"backgroundProcessOutputDialog.truncatedNotice": "הפלט קוצר לצורך התצוגה.",
"backgroundProcessOutputDialog.loadErrorFallback": "טעינת הפלט נכשלה.",
} as const

View File

@@ -1,43 +0,0 @@
export const filesystemMessages = {
"directoryBrowser.defaultDescription": "עיון בתיקיות תחת שורש סביבת העבודה המוגדר.",
"directoryBrowser.close": "סגור",
"directoryBrowser.currentFolder": "תיקייה נוכחית",
"directoryBrowser.selectCurrent": "בחר נוכחית",
"directoryBrowser.newFolder": "תיקייה חדשה",
"directoryBrowser.creating": "יוצר…",
"directoryBrowser.loadingFolders": "טוען תיקיות…",
"directoryBrowser.noFolders": "אין תיקיות זמינות.",
"directoryBrowser.upOneLevel": "עלה רמה אחת",
"directoryBrowser.select": "בחר",
"directoryBrowser.load.errorFallback": "לא ניתן לטעון את מערכת הקבצים",
"directoryBrowser.createFolder.promptMessage": "צור תיקייה חדשה בספרייה הנוכחית.",
"directoryBrowser.createFolder.title": "תיקייה חדשה",
"directoryBrowser.createFolder.inputLabel": "שם תיקייה",
"directoryBrowser.createFolder.inputPlaceholder": "למשל: my-new-project",
"directoryBrowser.createFolder.confirmLabel": "צור",
"directoryBrowser.createFolder.cancelLabel": "ביטול",
"directoryBrowser.createFolder.invalidNameMessage": "נא להזין שם תיקייה יחיד.",
"directoryBrowser.createFolder.invalidNameDetail": "שמות תיקיות אינם יכולים לכלול נטויות, '..', או '~'.",
"directoryBrowser.createFolder.errorFallback": "יצירת התיקייה נכשלה",
"filesystemBrowser.descriptionFallback": "חפש נתיב תחת שורש סביבת העבודה המוגדר.",
"filesystemBrowser.rootLabel": "שורש: {root}",
"filesystemBrowser.actions.close": "סגור",
"filesystemBrowser.actions.retry": "נסה שוב",
"filesystemBrowser.actions.select": "בחר",
"filesystemBrowser.filterLabel": "סינון",
"filesystemBrowser.search.placeholder.directories": "חפש תיקיות",
"filesystemBrowser.search.placeholder.files": "חפש קבצים",
"filesystemBrowser.currentFolder.label": "תיקייה נוכחית",
"filesystemBrowser.currentFolder.selectCurrent": "בחר נוכחית",
"filesystemBrowser.loading.filesystem": "מערכת קבצים",
"filesystemBrowser.loading.workspaceRoot": "שורש סביבת עבודה",
"filesystemBrowser.loading.loadingWithPath": "טוען {path}…",
"filesystemBrowser.empty.noEntries": "לא נמצאו רשומות.",
"filesystemBrowser.navigation.upOneLevel": "עלה רמה אחת",
"filesystemBrowser.hints.navigate": "ניווט",
"filesystemBrowser.hints.select": "בחירה",
"filesystemBrowser.hints.close": "סגירה",
"filesystemBrowser.errors.loadFilesystemFallback": "לא ניתן לטעון את מערכת הקבצים",
"filesystemBrowser.errors.openDirectoryFallback": "לא ניתן לפתוח את הספרייה",
} as const

View File

@@ -1,42 +0,0 @@
export const folderSelectionMessages = {
"folderSelection.language.ariaLabel": "שפה",
"folderSelection.logoAlt": "לוגו CodeNomad",
"folderSelection.tagline": "בחר תיקייה כדי להתחיל לתכנת עם AI",
"folderSelection.links.github": "CodeNomad GitHub",
"folderSelection.links.githubStars": "כוכבי CodeNomad ב-GitHub",
"folderSelection.links.discord": "CodeNomad Discord",
"folderSelection.empty.title": "אין תיקיות אחרונות",
"folderSelection.empty.description": "עיין בתיקייה כדי להתחיל",
"folderSelection.recent.title": "תיקיות אחרונות",
"folderSelection.recent.subtitle.one": "תיקייה אחת זמינה",
"folderSelection.recent.subtitle.other": "{count} תיקיות זמינות",
"folderSelection.recent.remove": "הסר מהרשימה האחרונה",
"folderSelection.browse.title": "עיון בתיקייה",
"folderSelection.browse.subtitle": "בחר כל תיקייה במחשב שלך",
"folderSelection.browse.button": "עיון בתיקיות",
"folderSelection.browse.buttonOpening": "פותח...",
"folderSelection.advancedSettings": "הגדרות מתקדמות",
"folderSelection.opencode": "OpenCode",
"folderSelection.hints.navigate": "ניווט",
"folderSelection.hints.select": "בחירה",
"folderSelection.hints.remove": "הסרה",
"folderSelection.hints.browse": "עיון",
"folderSelection.loading.title": "מפעיל מופע...",
"folderSelection.loading.subtitle": "המתן בזמן שאנו מכינים את סביבת העבודה שלך.",
"folderSelection.drop.title": "שחרר תיקייה כדי לפתוח אותה",
"folderSelection.drop.subtitle": "התחל מופע חדש בתיקייה שנשחררה.",
"folderSelection.drop.invalidTitle": "לא ניתן לפתוח את הפריט שנשחרר",
"folderSelection.drop.invalidMessage": "שחרר תיקייה כדי להתחיל מופע חדש.",
"folderSelection.dialog.title": "בחר סביבת עבודה",
"folderSelection.dialog.description": "בחר סביבת עבודה כדי להתחיל לתכנת.",
} as const

View File

@@ -1,36 +0,0 @@
import { advancedSettingsMessages } from "./advancedSettings"
import { appMessages } from "./app"
import { commandMessages } from "./commands"
import { dialogMessages } from "./dialogs"
import { filesystemMessages } from "./filesystem"
import { folderSelectionMessages } from "./folderSelection"
import { instanceMessages } from "./instance"
import { loadingScreenMessages } from "./loadingScreen"
import { logMessages } from "./logs"
import { markdownMessages } from "./markdown"
import { messagingMessages } from "./messaging"
import { remoteAccessMessages } from "./remoteAccess"
import { sessionMessages } from "./session"
import { settingsMessages } from "./settings"
import { timeMessages } from "./time"
import { toolCallMessages } from "./toolCall"
import { mergeMessageParts } from "../merge"
export const heMessages = mergeMessageParts(
folderSelectionMessages,
advancedSettingsMessages,
loadingScreenMessages,
timeMessages,
appMessages,
dialogMessages,
filesystemMessages,
instanceMessages,
logMessages,
sessionMessages,
messagingMessages,
toolCallMessages,
markdownMessages,
settingsMessages,
remoteAccessMessages,
commandMessages,
)

View File

@@ -1,166 +0,0 @@
export const instanceMessages = {
"instanceTabs.new.title": "מופע חדש (Cmd/Ctrl+N)",
"instanceTabs.new.ariaLabel": "מופע חדש",
"instanceTabs.remote.title": "חיבור מרוחק",
"instanceTabs.remote.ariaLabel": "חיבור מרוחק",
"instanceInfo.title": "מידע על המופע",
"instanceInfo.labels.folder": "תיקייה",
"instanceInfo.labels.project": "פרויקט",
"instanceInfo.labels.versionControl": "בקרת גרסאות",
"instanceInfo.labels.opencodeVersion": "גרסת OpenCode",
"instanceInfo.labels.binaryPath": "נתיב קובץ בינארי",
"instanceInfo.labels.environmentVariables": "משתני סביבה ({count})",
"instanceInfo.loading": "טוען...",
"instanceInfo.server.title": "שרת",
"instanceInfo.server.port": "פורט:",
"instanceInfo.server.pid": "PID:",
"instanceInfo.server.status": "סטטוס:",
"instanceTab.status.permission": "ממתין לאישור",
"instanceTab.status.compacting": "מסכם",
"instanceTab.status.working": "עובד",
"instanceTab.status.idle": "מוכן",
"instanceTab.status.ariaLabel": "סטטוס מופע: {status}",
"instanceTab.actions.close.ariaLabel": "סגור מופע",
"instanceShell.leftPanel.sessionsTitle": "סשנים",
"instanceShell.leftPanel.instanceInfo": "מידע על המופע",
"instanceShell.leftDrawer.pin": "נעץ מגירה שמאלית",
"instanceShell.leftDrawer.unpin": "שחרר נעיצת מגירה שמאלית",
"instanceShell.leftDrawer.toggle.pinned": "המגירה השמאלית נעוצה",
"instanceShell.leftDrawer.toggle.open": "פתח מגירה שמאלית",
"instanceShell.leftDrawer.toggle.close": "סגור מגירה שמאלית",
"instanceShell.rightDrawer.pin": "נעץ מגירה ימנית",
"instanceShell.rightDrawer.unpin": "שחרר נעיצת מגירה ימנית",
"instanceShell.rightDrawer.toggle.pinned": "המגירה הימנית נעוצה",
"instanceShell.rightDrawer.toggle.open": "פתח מגירה ימנית",
"instanceShell.rightDrawer.toggle.close": "סגור מגירה ימנית",
"instanceShell.fullscreen.enter": "מסך מלא",
"instanceShell.fullscreen.exit": "יציאה ממסך מלא",
"instanceShell.metrics.usedLabel": "בשימוש",
"instanceShell.metrics.availableLabel": "זמין",
"instanceShell.commandPalette.openAriaLabel": "פתח לוח פקודות",
"instanceShell.commandPalette.button": "לוח פקודות",
"instanceShell.connection.ariaLabel": "חיבור {status}",
"instanceShell.connection.connected": "מחובר",
"instanceShell.connection.connecting": "מתחבר...",
"instanceShell.connection.disconnected": "מנותק",
"instanceShell.connection.unknown": "לא ידוע",
"instanceWelcome.shortcuts.newSession": "סשן חדש",
"instanceWelcome.empty.title": "אין סשנים קודמים",
"instanceWelcome.empty.description": "צור סשן חדש למטה כדי להתחיל",
"instanceWelcome.loading.title": "טוען סשנים",
"instanceWelcome.loading.description": "מאחזר את הסשנים הקודמים שלך...",
"instanceWelcome.resume.title": "המשך סשן",
"instanceWelcome.resume.subtitle.one": "סשן אחד זמין",
"instanceWelcome.resume.subtitle.other": "{count} סשנים זמינים",
"instanceWelcome.session.untitled": "סשן ללא שם",
"instanceWelcome.new.title": "התחל סשן חדש",
"instanceWelcome.new.subtitle": "ישתמש אוטומטית בסוכן/מודל האחרון שלך",
"instanceWelcome.new.createButton": "צור סשן",
"instanceWelcome.overlay.close": "סגור",
"instanceWelcome.actions.viewInstanceInfo": "צפה במידע על המופע",
"instanceWelcome.actions.renameTitle": "שנה שם סשן",
"instanceWelcome.actions.deleteTitle": "מחק סשן",
"instanceWelcome.hints.navigate": "ניווט",
"instanceWelcome.hints.jump": "קפיצה",
"instanceWelcome.hints.firstLast": "ראשון/אחרון",
"instanceWelcome.hints.resume": "המשך",
"instanceWelcome.hints.delete": "מחיקה",
"instanceWelcome.toasts.renameError": "לא ניתן לשנות שם הסשן",
"instanceDisconnected.title": "המופע התנתק",
"instanceDisconnected.folderFallback": "סביבת עבודה זו",
"instanceDisconnected.reasonFallback": "השרת הפסיק להגיב",
"instanceDisconnected.description": "לא ניתן עוד להגיע ל-{folder}. סגור את הלשונית כדי להמשיך לעבוד.",
"instanceDisconnected.details.title": "פרטים",
"instanceDisconnected.details.folderLabel": "תיקייה:",
"instanceDisconnected.actions.closeInstance": "סגור מופע",
"instanceShell.empty.title": "לא נבחר סשן",
"instanceShell.empty.description": "בחר סשן לצפייה בהודעות",
"instanceShell.rightPanel.title": "לוח סטטוס",
"instanceShell.rightPanel.tabs.changes": "שינויי סשן",
"instanceShell.rightPanel.tabs.gitChanges": "שינויי Git",
"instanceShell.rightPanel.tabs.files": "קבצים",
"instanceShell.rightPanel.tabs.status": "סטטוס",
"instanceShell.rightPanel.tabs.ariaLabel": "לשוניות לוח ימני",
"instanceShell.rightPanel.actions.refresh": "רענן",
"instanceShell.rightPanel.sections.sessionChanges": "שינויי סשן",
"instanceShell.rightPanel.sections.sessionChanges.tooltip": "קבצים שהשתנו בסשן הנוכחי. מציג הוספות ומחיקות לכל קובץ.",
"instanceShell.rightPanel.sections.plan": "תוכנית",
"instanceShell.rightPanel.sections.plan.tooltip": "מפת הדרכים של הסוכן לסשן זה. עוקב אחר משימות, תת-משימות וסטטוס השלמתן.",
"instanceShell.rightPanel.sections.backgroundProcesses": "מעטפות רקע",
"instanceShell.rightPanel.sections.backgroundProcesses.tooltip": "תהליכים ממושכים שהופעלו על ידי הסוכן. ניתן לעקוב אחר פלטם, לעצור אותם או לסיים אותם.",
"instanceShell.rightPanel.sections.mcp": "שרתי MCP",
"instanceShell.rightPanel.sections.mcp.tooltip": "שרתי Model Context Protocol המרחיבים את יכולות הסוכן עם כלים ושירותים חיצוניים.",
"instanceShell.rightPanel.sections.lsp": "שרתי LSP",
"instanceShell.rightPanel.sections.lsp.tooltip": "שרתי Language Server Protocol המספקים בינת קוד, אבחון ותכונות ספציפיות לשפה.",
"instanceShell.rightPanel.sections.plugins": "תוספים",
"instanceShell.rightPanel.sections.plugins.tooltip": "תוספים המתאימים אישית את הממשק ואת התנהגות השרת, ומוסיפים תכונות מעבר ל-MCP ו-LSP.",
"instanceShell.sessionChanges.noSessionSelected": "בחר סשן לצפייה בשינויים.",
"instanceShell.sessionChanges.loading": "מאחזר שינויי סשן...",
"instanceShell.sessionChanges.empty": "אין שינויי סשן עדיין.",
"instanceShell.sessionChanges.filesChanged": "{count} קבצים שונו",
"instanceShell.sessionChanges.actions.show": "הצג שינויים",
"instanceShell.filesShell.fileListTitle": "רשימת קבצים",
"instanceShell.filesShell.mobileSelectorLabel": "בחר קובץ",
"instanceShell.filesShell.mobileSelectorEmpty": "בחר קובץ",
"instanceShell.filesShell.viewerTitle": "מציג שינויים",
"instanceShell.filesShell.viewerPlaceholder": "תצוגת שינויים מפורטת תתווסף בשלב הבא.",
"instanceShell.filesShell.viewerEmpty": "לא נבחר קובץ.",
"instanceShell.filesShell.hideFiles": "הסתר קבצים",
"instanceShell.filesShell.showFiles": "הצג קבצים",
"instanceShell.gitChanges.noSessionSelected": "בחר סשן לצפייה בשינויי Git.",
"instanceShell.gitChanges.loading": "טוען שינויי Git…",
"instanceShell.gitChanges.empty": "אין שינויי Git עדיין.",
"instanceShell.diff.hideUnchanged": "הסתר אזורים ללא שינוי",
"instanceShell.diff.showFull": "הצג קובץ מלא",
"instanceShell.diff.switchToSplit": "עבור לתצוגה מפוצלת",
"instanceShell.diff.switchToUnified": "עבור לתצוגה מאוחדת",
"instanceShell.diff.enableWordWrap": "הפעל גלישת מילים",
"instanceShell.diff.disableWordWrap": "כבה גלישת מילים",
"instanceShell.worktree.create": "+ צור worktree",
"instanceShell.plan.noSessionSelected": "בחר סשן לצפייה בתוכנית.",
"instanceShell.plan.empty": "עדיין לא תוכנן דבר.",
"instanceShell.backgroundProcesses.empty": "אין תהליכי רקע.",
"instanceShell.backgroundProcesses.status": "סטטוס: {status}",
"instanceShell.backgroundProcesses.output": "פלט: {sizeKb}KB",
"instanceShell.backgroundProcesses.actions.output": "פלט",
"instanceShell.backgroundProcesses.actions.stop": "עצור",
"instanceShell.backgroundProcesses.actions.terminate": "סיים",
"versionPill.appWithVersion": "אפליקציה {version}",
"versionPill.ui": "ממשק",
"versionPill.uiWithVersion": "ממשק {version}",
"versionPill.source": " ({source})",
"opencodeBinarySelector.title": "קובץ בינארי של OpenCode",
"opencodeBinarySelector.subtitle": "בחר איזה קובץ הרצה OpenCode ישתמש",
"opencodeBinarySelector.customPath.placeholder": "הזן נתיב לקובץ בינארי של opencode…",
"opencodeBinarySelector.actions.add": "הוסף",
"opencodeBinarySelector.actions.browse": "עיין אחר קובץ בינארי…",
"opencodeBinarySelector.actions.removeTitle": "הסר קובץ בינארי",
"opencodeBinarySelector.badge.systemPath": "השתמש בקובץ בינארי מנתיב המערכת",
"opencodeBinarySelector.status.checkingVersions": "בודק גרסאות…",
"opencodeBinarySelector.status.checking": "בודק…",
"opencodeBinarySelector.dialog.title": "בחר קובץ בינארי של OpenCode",
"opencodeBinarySelector.dialog.description": "עיין בקבצים החשופים על ידי שרת ה-CLI.",
"opencodeBinarySelector.validation.invalidBinary": "קובץ בינארי לא תקין של OpenCode",
"opencodeBinarySelector.validation.alreadyValidating": "כבר מאמת",
"opencodeBinarySelector.display.systemPath": "{name} (נתיב מערכת)",
"opencodeBinarySelector.versionLabel": "v{version}",
} as const

View File

@@ -1,17 +0,0 @@
export const loadingScreenMessages = {
"loadingScreen.logoAlt": "לוגו CodeNomad",
"loadingScreen.status.issue": "נתקלנו בבעיה",
"loadingScreen.actions.showAnother": "הצג עוד",
"loadingScreen.errors.missingRoot": "אלמנט השורש לטעינה לא נמצא",
"loadingScreen.phrases.neurons": "מחמם את הנוירונים של ה-AI…",
"loadingScreen.phrases.daydreaming": "משכנע את ה-AI להפסיק לחלום בהקיץ…",
"loadingScreen.phrases.goggles": "מצחצח את משקפי הקוד של ה-AI…",
"loadingScreen.phrases.reorganizingFiles": "מבקש מה-AI להפסיק לארגן מחדש את הקבצים שלך…",
"loadingScreen.phrases.coffee": "מאכיל את ה-AI עוד קפה…",
"loadingScreen.phrases.nodeModules": "מלמד את ה-AI לא למחוק node_modules (שוב)…",
"loadingScreen.phrases.actNatural": "אומר ל-AI להיראות טבעי לפני שתגיע…",
"loadingScreen.phrases.rewritingHistory": "מבקש מה-AI בבקשה להפסיק לשכתב היסטוריה…",
"loadingScreen.phrases.stretch": "מאפשר ל-AI להתמתח לפני ספרינט הקוד שלו…",
"loadingScreen.phrases.keyboardControl": "משכנע את ה-AI לתת לך שליטה על המקלדת…",
} as const

View File

@@ -1,27 +0,0 @@
export const logMessages = {
"logsView.title": "לוגי שרת",
"logsView.actions.show": "הצג לוגי שרת",
"logsView.actions.hide": "הסתר לוגי שרת",
"logsView.envVars.title": "משתני סביבה ({count})",
"logsView.paused.title": "לוגי השרת מושהים",
"logsView.paused.description": "הפעל זרימה לצפייה בפעילות שרת OpenCode שלך.",
"logsView.empty.waiting": "ממתין לפלט שרת...",
"logsView.scrollToBottom": "גלול למטה",
"infoView.logs.title": "לוגי שרת",
"infoView.logs.actions.show": "הצג לוגי שרת",
"infoView.logs.actions.hide": "הסתר לוגי שרת",
"infoView.logs.paused.title": "לוגי השרת מושהים",
"infoView.logs.paused.description": "הפעל זרימה לצפייה בפעילות שרת OpenCode שלך.",
"infoView.logs.empty.waiting": "ממתין לפלט שרת...",
"infoView.logs.scrollToBottom": "גלול למטה",
"infoView.dispose.actions.dispose": "בטל מופע",
"infoView.dispose.actions.disposing": "מבטל...",
"infoView.dispose.confirm.title": "לבטל את המופע?",
"infoView.dispose.confirm.message": "פעולה זו מנקה את המצב השמור לפי פרויקט עבור ספרייה זו ומטעינה מחדש את המופע.",
"infoView.dispose.confirm.confirmLabel": "בטל",
"infoView.dispose.confirm.cancelLabel": "ביטול",
"infoView.dispose.toast.success": "המופע בוטל. מטעין מחדש...",
"infoView.dispose.toast.error": "ביטול המופע נכשל.",
} as const

View File

@@ -1,7 +0,0 @@
export const markdownMessages = {
"markdown.codeBlock.copy.label": "העתק",
"markdown.codeBlock.copy.copied": "הועתק!",
"markdown.codeBlock.copy.failed": "נכשל",
"markdown.copy": "העתק",
} as const

View File

@@ -1,141 +0,0 @@
export const messagingMessages = {
"messageListHeader.sidebar.openSessionListAriaLabel": "פתח רשימת סשנים",
"messageListHeader.metrics.usedLabel": "בשימוש",
"messageListHeader.metrics.availableLabel": "זמין",
"messageListHeader.commandPalette.ariaLabel": "פתח לוח פקודות",
"messageListHeader.commandPalette.button": "לוח פקודות",
"messageListHeader.connection.connected": "מחובר",
"messageListHeader.connection.connecting": "מתחבר...",
"messageListHeader.connection.disconnected": "מנותק",
"messageSection.empty.logoAlt": "לוגו CodeNomad",
"messageSection.empty.brandTitle": "CodeNomad",
"messageSection.empty.title": "התחל שיחה",
"messageSection.empty.description": "הקלד הודעה למטה או פתח את לוח הפקודות:",
"messageSection.empty.tips.commandPalette": "לוח פקודות",
"messageSection.empty.tips.askAboutCodebase": "שאל על בסיס הקוד שלך",
"messageSection.empty.tips.attachFilesPrefix": "צרף קבצים עם",
"messageSection.loading.messages": "טוען הודעות...",
"messageSection.scroll.toFirstAriaLabel": "גלול להודעה הראשונה",
"messageSection.scroll.toLatestAriaLabel": "גלול להודעה האחרונה",
"messageSection.quote.addAsQuote": "הוסף כציטוט",
"messageSection.quote.addAsCode": "הוסף כקוד",
"messageSection.quote.copy": "העתק",
"messageSection.quote.copied": "הועתק!",
"messageSection.quote.copyFailed": "ההעתקה נכשלה",
"messageTimeline.ariaLabel": "ציר זמן הודעות",
"messageTimeline.segment.user.label": "אתה",
"messageTimeline.segment.assistant.label": "סוכן",
"messageTimeline.segment.compaction.label": "סיכום",
"messageTimeline.tool.fallbackLabel": "קריאת כלי",
"messageTimeline.tooltip.userFallback": "הודעת משתמש",
"messageTimeline.tooltip.assistantFallback": "תגובת הסוכן",
"messageTimeline.tooltip.compaction.auto": "סיכום אוטומטי",
"messageTimeline.tooltip.compaction.manual": "סיכום ידני",
"messageTimeline.text.filePrefix": "[קובץ] {filename}",
"messageTimeline.text.attachment": "קובץ מצורף",
"messageBlock.tool.header": "קריאת כלי",
"messageBlock.tool.unknown": "לא ידוע",
"messageBlock.tool.goToSession.label": "עבור לסשן",
"messageBlock.tool.goToSession.title": "עבור לסשן",
"messageBlock.tool.goToSession.unavailableTitle": "הסשן עדיין אינו זמין",
"messageBlock.tool.deletePart.label": "מחק חלק",
"messageBlock.tool.deletePart.deleting": "מוחק...",
"messageBlock.tool.deletePart.title": "מחק את פלט קריאת הכלי הזו",
"messageBlock.tool.deletePart.failed.title": "המחיקה נכשלה",
"messageBlock.tool.deletePart.failed.message": "מחיקת פלט קריאת הכלי נכשלה",
"messageBlock.compaction.ariaLabel": "סיכום סשן",
"messageBlock.compaction.autoLabel": "הסשן סוכם אוטומטית",
"messageBlock.compaction.manualLabel": "הסשן סוכם על ידך",
"messageBlock.usage.input": "קלט",
"messageBlock.usage.output": "פלט",
"messageBlock.usage.reasoning": "חשיבה",
"messageBlock.usage.cacheRead": "קריאת מטמון",
"messageBlock.usage.cacheWrite": "כתיבת מטמון",
"messageBlock.usage.cost": "עלות",
"messageBlock.step.agentLabel": "סוכן: {agent}",
"messageBlock.step.modelLabel": "מודל: {model}",
"messageBlock.reasoning.thinkingLabel": "חשיבה",
"messageBlock.reasoning.expandAriaLabel": "פרוס חשיבה",
"messageBlock.reasoning.collapseAriaLabel": "כווץ חשיבה",
"messageBlock.reasoning.indicator.hide": "הסתר",
"messageBlock.reasoning.indicator.view": "צפה",
"messageBlock.reasoning.detailsAriaLabel": "פרטי חשיבה",
"codeBlockInline.actions.copy": "העתק",
"codeBlockInline.actions.copied": "הועתק!",
"messageItem.speaker.you": "אתה",
"messageItem.speaker.assistant": "סוכן",
"messageItem.actions.revert": "בטל שינויים",
"messageItem.actions.revertTitle": "בטל שינויים עד כאן (מוחק הודעות)",
"messageItem.actions.fork": "פצל",
"messageItem.actions.forkTitle": "פצל מהודעה זו",
"messageItem.actions.copy": "העתק",
"messageItem.actions.copyTitle": "העתק הודעה",
"messageItem.actions.copied": "הועתק!",
"messageItem.actions.deleteMessage": "מחק הודעה (לא מבטל שינויים)",
"messageItem.actions.deleteMessagesUpTo": "מחק הודעות עד כאן (לא מבטל שינויים)",
"messageItem.actions.deletingMessage": "מוחק...",
"messageItem.actions.deleteMessageFailedTitle": "המחיקה נכשלה",
"messageItem.actions.deleteMessageFailedMessage": "מחיקת ההודעה נכשלה",
"messageItem.selection.checkboxAriaLabel": "בחר הודעה למחיקה",
"messageSection.bulkDelete.toolbarAriaLabel": "פריטים נבחרים ({count})",
"messageSection.bulkDelete.deleteSelectedTitle": "מחק פריטים נבחרים",
"messageSection.bulkDelete.selectAllTitle": "בחר את כל ההודעות",
"messageSection.bulkDelete.moreOptionsTitle": "אפשרויות נוספות",
"messageSection.bulkDelete.selectionModeLabel": "בחירה",
"messageSection.bulkDelete.selectionModeAll": "הכל",
"messageSection.bulkDelete.selectionModeTools": "כלים בלבד",
"messageSection.bulkDelete.selectionHint.toggle": "בחר פריט",
"messageSection.bulkDelete.selectionHint.range": "בחר טווח",
"messageSection.bulkDelete.selectionHint.clear": "נקה בחירה",
"messageSection.bulkDelete.cancelTitle": "בטל בחירה",
"messageSection.bulkDelete.failedTitle": "המחיקה נכשלה",
"messageSection.bulkDelete.failedMessage": "מחיקת הפריטים הנבחרים נכשלה",
"messageItem.status.queued": "בתור",
"messageItem.status.generating": "מייצר...",
"messageItem.status.sending": "שולח...",
"messageItem.status.failedToSend": "שליחת ההודעה נכשלה",
"messagePart.actions.delete": "מחק חלק",
"messagePart.actions.deleting": "מוחק...",
"messagePart.actions.deleteTitle": "מחק פריט זה",
"messagePart.actions.deleteFailedTitle": "המחיקה נכשלה",
"messagePart.actions.deleteFailedMessage": "מחיקת הפריט נכשלה",
"messageItem.attachment.defaultName": "קובץ מצורף",
"messageItem.attachment.downloadAriaLabel": "הורד {name}",
"messageItem.agentMeta.agentLabel": "סוכן: {agent}",
"messageItem.agentMeta.modelLabel": "מודל: {model}",
"messageItem.errors.authenticationFallback": "שגיאת אימות",
"messageItem.errors.outputLengthExceeded": "אורך פלט ההודעה חרג מהמגבלה",
"messageItem.errors.requestAborted": "הבקשה בוטלה",
"messageItem.errors.unknownFallback": "אירעה שגיאה לא ידועה",
"attachmentChip.removeAriaLabel": "הסר קובץ מצורף",
"expandButton.toggleAriaLabel": "שנה גובה תיבת הקלט",
"promptInput.placeholder.shell": "הפעל פקודת מעטפת (Esc ליציאה)...",
"promptInput.placeholder.default": "הקלד הודעה, @file, @agent, או הדבק תמונות וטקסט...",
"promptInput.hints.shell.exit": "לצאת ממצב מעטפת",
"promptInput.hints.shell.enable": "מצב מעטפת",
"promptInput.hints.commands": "פקודות",
"promptInput.history.previousAriaLabel": "פקודה קודמת",
"promptInput.history.nextAriaLabel": "פקודה הבאה",
"promptInput.overlay.newLine": "שורה חדשה",
"promptInput.overlay.send": "שלח",
"promptInput.overlay.filesAgents": "קבצים/סוכנים",
"promptInput.overlay.history": "היסטוריה",
"promptInput.overlay.attachments": "• {count} קובץ/ים מצורף/ים",
"promptInput.overlay.shellModeActive": "מצב מעטפת פעיל",
"promptInput.overlay.press": "לחץ",
"promptInput.overlay.againToAbort": "שוב כדי לבטל את הסשן",
"promptInput.stopSession.ariaLabel": "עצור סשן",
"promptInput.stopSession.title": "עצור סשן",
"promptInput.send.ariaLabel": "שלח הודעה",
"promptInput.send.errorFallback": "שליחת ההודעה נכשלה",
"promptInput.send.errorTitle": "השליחה נכשלה",
} as const

View File

@@ -1,51 +0,0 @@
export const remoteAccessMessages = {
"remoteAccess.eyebrow": "גישה מרוחקת",
"remoteAccess.title": "התחבר ל-CodeNomad מרחוק",
"remoteAccess.subtitle": "השתמש בכתובות למטה כדי לפתוח את CodeNomad ממכשיר אחר.",
"remoteAccess.close": "סגור גישה מרוחקת",
"remoteAccess.refresh": "רענן",
"remoteAccess.sections.listeningMode.label": "מצב האזנה",
"remoteAccess.sections.listeningMode.help": "אפשר או הגבל גישה מרוחקת על ידי קישור לכל הממשקים או רק ל-localhost.",
"remoteAccess.toggle.on": "פועל",
"remoteAccess.toggle.off": "כבוי",
"remoteAccess.toggle.title": "אפשר חיבורים מכתובות IP אחרות",
"remoteAccess.toggle.caption.all": "מקושר ל-0.0.0.0",
"remoteAccess.toggle.caption.local": "מקושר ל-127.0.0.1",
"remoteAccess.toggle.note": "שינוי זה דורש הפעלה מחדש ועוצר זמנית את כל המופעים הפעילים. שתף את הכתובות למטה לאחר שהשרת יופעל מחדש.",
"remoteAccess.listeningMode.restartConfirm.message": "להפעיל מחדש כדי להחיל מצב האזנה? פעולה זו תעצור את כל המופעים הפעילים.",
"remoteAccess.listeningMode.restartConfirm.title.all": "פתוח למכשירים אחרים",
"remoteAccess.listeningMode.restartConfirm.title.local": "מוגבל למכשיר זה",
"remoteAccess.listeningMode.restartConfirm.confirmLabel": "הפעל מחדש עכשיו",
"remoteAccess.listeningMode.restartConfirm.cancelLabel": "ביטול",
"remoteAccess.restart.errorManual": "לא ניתן להפעיל מחדש אוטומטית. אנא הפעל מחדש את האפליקציה כדי להחיל את השינוי.",
"remoteAccess.sections.serverPassword.label": "סיסמת שרת",
"remoteAccess.sections.serverPassword.help": "גישה מרוחקת דורשת סיסמה. הגדר סיסמה קלה לזכירה כדי לאפשר כניסות ממכשירים אחרים.",
"remoteAccess.authStatus.unavailable": "סטטוס האימות אינו זמין.",
"remoteAccess.username": "שם משתמש: {username}",
"remoteAccess.password.status.set": "סיסמה מוגדרת לגישה מרוחקת.",
"remoteAccess.password.status.unset": "לא הוגדרה סיסמה קלה לזכירה. הגדר סיסמה כדי לאפשר כניסות גישה מרוחקת.",
"remoteAccess.password.actions.cancel": "ביטול",
"remoteAccess.password.actions.change": "שנה סיסמה",
"remoteAccess.password.actions.set": "הגדר סיסמה",
"remoteAccess.password.form.newPassword": "סיסמה חדשה",
"remoteAccess.password.form.confirmPassword": "אשר סיסמה",
"remoteAccess.password.form.placeholder": "לפחות 8 תווים",
"remoteAccess.password.error.tooShort": "הסיסמה חייבת להכיל לפחות 8 תווים.",
"remoteAccess.password.error.mismatch": "הסיסמאות אינן תואמות.",
"remoteAccess.password.save.saving": "שומר…",
"remoteAccess.password.save.label": "שמור סיסמה",
"remoteAccess.sections.addresses.label": "כתובות נגישות",
"remoteAccess.sections.addresses.help": "הפעל או סרוק ממכונה אחרת להעברת שליטה.",
"remoteAccess.addresses.loading": "טוען כתובות…",
"remoteAccess.addresses.none": "אין כתובות זמינות עדיין.",
"remoteAccess.address.scope.network": "רשת",
"remoteAccess.address.scope.loopback": "לולאה מקומית",
"remoteAccess.address.scope.internal": "פנימי",
"remoteAccess.address.open": "פתח",
"remoteAccess.address.showQr": "הצג QR",
"remoteAccess.address.hideQr": "הסתר QR",
"remoteAccess.address.qrAlt": "QR עבור {url}",
} as const

View File

@@ -1,90 +0,0 @@
export const sessionMessages = {
"sessionPicker.title": "OpenCode • {folder}",
"sessionPicker.empty.noPrevious": "אין סשנים קודמים",
"sessionPicker.resume.title": "המשך סשן ({count}):",
"sessionPicker.session.untitled": "ללא שם",
"sessionPicker.divider.or": "או",
"sessionPicker.new.title": "התחל סשן חדש:",
"sessionPicker.agents.loading": "טוען סוכנים...",
"sessionPicker.actions.creating": "יוצר...",
"sessionPicker.actions.createSession": "צור סשן",
"sessionPicker.actions.cancel": "ביטול",
"sessionList.header.title": "סשנים",
"sessionList.session.untitled": "ללא שם",
"sessionList.status.working": "עובד",
"sessionList.status.compacting": "מסכם",
"sessionList.status.idle": "מוכן",
"sessionList.status.needsPermission": "נדרש אישור",
"sessionList.status.needsInput": "נדרש קלט",
"sessionList.expand.collapseAriaLabel": "כווץ סשן",
"sessionList.expand.expandAriaLabel": "פרוס סשן",
"sessionList.expand.collapseTitle": "כווץ",
"sessionList.expand.expandTitle": "פרוס",
"sessionList.actions.newSession.ariaLabel": "סשן חדש",
"sessionList.actions.newSession.title": "סשן חדש",
"sessionList.actions.copyId.ariaLabel": "העתק מזהה סשן",
"sessionList.actions.copyId.title": "העתק מזהה סשן",
"sessionList.actions.rename.ariaLabel": "שנה שם סשן",
"sessionList.actions.rename.title": "שנה שם סשן",
"sessionList.actions.delete.ariaLabel": "מחק סשן",
"sessionList.actions.delete.title": "מחק סשן",
"sessionList.copyId.success": "מזהה סשן הועתק",
"sessionList.copyId.error": "לא ניתן להעתיק מזהה סשן",
"sessionList.delete.error": "לא ניתן למחוק סשן",
"sessionList.delete.title": "מחק סשן",
"sessionList.delete.confirmMessage": "למחוק את \"{label}\"? לא ניתן לבטל פעולה זו.",
"sessionList.delete.confirmLabel": "מחק",
"sessionList.delete.cancelLabel": "ביטול",
"sessionList.rename.error": "לא ניתן לשנות שם הסשן",
"sessionList.filter.placeholder": "חפש סשנים…",
"sessionList.filter.ariaLabel": "חפש סשנים",
"sessionList.selection.selectAllLabel": "בחר הכל",
"sessionList.selection.selectAllAriaLabel": "בחר את כל הסשנים",
"sessionList.selection.clearLabel": "נקה",
"sessionList.selection.clearAriaLabel": "נקה בחירה",
"sessionList.selection.checkboxAriaLabel": "בחר סשן",
"sessionList.bulkDelete.button": "מחק {count}",
"sessionList.bulkDelete.ariaLabel": "מחק {count} סשנים נבחרים",
"sessionList.bulkDelete.title": "מחק סשנים",
"sessionList.bulkDelete.confirmMessage": "למחוק {count} סשנים נבחרים? לא ניתן לבטל פעולה זו.",
"sessionList.bulkDelete.confirmLabel": "מחק",
"sessionList.bulkDelete.cancelLabel": "ביטול",
"sessionList.bulkDelete.error": "לא ניתן למחוק {count} סשנים",
"sessionRenameDialog.title": "שנה שם סשן",
"sessionRenameDialog.description.withLabel": "עדכן את הכותרת עבור \"{label}\".",
"sessionRenameDialog.description.default": "הגדר כותרת חדשה לסשן זה.",
"sessionRenameDialog.input.label": "שם סשן",
"sessionRenameDialog.input.placeholder": "הזן שם סשן",
"sessionRenameDialog.actions.cancel": "ביטול",
"sessionRenameDialog.actions.rename": "שנה שם",
"sessionRenameDialog.actions.renaming": "משנה שם…",
"sessionView.fallback.sessionNotFound": "הסשן לא נמצא",
"sessionView.alerts.abortFailed.message": "עצירת הסשן נכשלה",
"sessionView.alerts.abortFailed.title": "העצירה נכשלה",
"sessionView.alerts.revertFailed.message": "החזרה להודעה נכשלה",
"sessionView.alerts.revertFailed.title": "החזרה נכשלה",
"sessionView.alerts.deleteUpToFailed.message": "מחיקת הודעות נכשלה",
"sessionView.alerts.deleteUpToFailed.title": "המחיקה נכשלה",
"sessionView.alerts.forkFailed.message": "פיצול הסשן נכשל",
"sessionView.alerts.forkFailed.title": "הפיצול נכשל",
"sessionView.attachments.expandPastedTextAriaLabel": "פרוס טקסט שהודבק",
"sessionView.attachments.insertPastedTextTitle": "הכנס טקסט שהודבק",
"sessionView.attachments.removeAriaLabel": "הסר קובץ מצורף",
"sessionEvents.sessionCompactedToast": "הסשן {label} סוכם",
"sessionEvents.sessionError.unknown": "שגיאה לא ידועה",
"sessionEvents.sessionError.title": "שגיאת סשן",
"sessionEvents.sessionError.message": "שגיאה: {message}",
"sessionState.cleanup.deepConfirm.message": "ניקוי עמוק זה עשוי להיות איטי, ועלול למחוק סשנים שלא התכוונת למחוק. האם אתה בטוח?",
"sessionState.cleanup.deepConfirm.title": "ניקוי עמוק של סשנים",
"sessionState.cleanup.deepConfirm.detail": "ניקוי עמוק של סשנים ימחק את כל הסשנים ללא הודעות, יסיר סשני תת-סוכן שסיימו, וינקה פיצולים לא בשימוש של סשן.",
"sessionState.cleanup.deepConfirm.confirmLabel": "המשך",
"sessionState.cleanup.deepConfirm.cancelLabel": "ביטול",
"sessionState.cleanup.toast.one": "נוקה {count} סשן ריק",
"sessionState.cleanup.toast.other": "נוקו {count} סשנים ריקים",
} as const

View File

@@ -1,142 +0,0 @@
export const settingsMessages = {
"instanceServiceStatus.sections.lsp": "שרתי LSP",
"instanceServiceStatus.sections.mcp": "שרתי MCP",
"instanceServiceStatus.sections.plugins": "תוספים",
"instanceServiceStatus.lsp.loading": "טוען שרתי LSP...",
"instanceServiceStatus.lsp.empty": "לא זוהו שרתי LSP.",
"instanceServiceStatus.lsp.status.connected": "מחובר",
"instanceServiceStatus.lsp.status.error": "שגיאה",
"instanceServiceStatus.mcp.loading": "טוען שרתי MCP...",
"instanceServiceStatus.mcp.empty": "לא זוהו שרתי MCP.",
"instanceServiceStatus.mcp.toggleAriaLabel": "הפעל/כבה שרת MCP {name}",
"instanceServiceStatus.plugins.loading": "טוען תוספים...",
"instanceServiceStatus.plugins.empty": "לא הוגדרו תוספים.",
"permissionBanner.pendingRequests.one": "בקשה אחת ממתינה",
"permissionBanner.pendingRequests.other": "{count} בקשות ממתינות",
"permissionBanner.detail.permission.one": "אישור אחד",
"permissionBanner.detail.permission.other": "{count} אישורים",
"permissionBanner.detail.question.one": "שאלה אחת",
"permissionBanner.detail.question.other": "{count} שאלות",
"permissionBanner.detail.wrapper": " ({detail})",
"agentSelector.placeholder": "בחר סוכן...",
"agentSelector.badge.subagent": "תת-סוכן",
"agentSelector.none": "ללא",
"agentSelector.trigger.primary": "סוכן: {agent}",
"modelSelector.placeholder.search": "חפש מודלים...",
"modelSelector.none": "ללא",
"modelSelector.trigger.primary": "מודל: {model}",
"modelSelector.favoritesOnly.toggle.ariaLabel": "הצג מועדפים בלבד",
"modelSelector.favoritesOnly.showAll": "הצג את כל המודלים",
"modelSelector.favorite.add": "הוסף למועדפים",
"modelSelector.favorite.remove": "הסר ממועדפים",
"thinkingSelector.variant.default": "ברירת מחדל",
"thinkingSelector.label": "חשיבה: {variant}",
"envEditor.title": "משתני סביבה",
"envEditor.count.one": "(משתנה אחד)",
"envEditor.count.other": "({count} משתנים)",
"envEditor.fields.name.placeholder": "שם משתנה",
"envEditor.fields.name.readOnlyTitle": "שם משתנה (לקריאה בלבד)",
"envEditor.fields.value.placeholder": "ערך משתנה",
"envEditor.actions.remove.title": "הסר משתנה",
"envEditor.actions.add.title": "הוסף משתנה",
"envEditor.empty": "לא הוגדרו משתני סביבה. הוסף משתנים למעלה להתאמת סביבת OpenCode.",
"envEditor.help": "משתנים אלו יהיו זמינים בסביבת OpenCode בעת הפעלת מופעים.",
"contextUsagePanel.headings.tokens": "טוקנים",
"contextUsagePanel.headings.context": "הקשר",
"contextUsagePanel.labels.input": "קלט",
"contextUsagePanel.labels.output": "פלט",
"contextUsagePanel.labels.cost": "עלות",
"contextUsagePanel.labels.used": "בשימוש",
"contextUsagePanel.labels.available": "זמין",
"contextUsagePanel.unavailable": "--",
"settings.title": "הגדרות",
"settings.navigationAriaLabel": "קטגוריות הגדרות",
"settings.close": "סגור הגדרות",
"settings.content.eyebrow": "העדפות סביבת עבודה",
"settings.open.title": "פתח הגדרות",
"settings.open.ariaLabel": "פתח הגדרות",
"settings.nav.appearance": "מראה",
"settings.nav.notifications": "התראות",
"settings.nav.remote": "גישה מרוחקת",
"settings.nav.opencode": "OpenCode",
"settings.scope.device": "מכשיר זה",
"settings.scope.server": "הגדרת שרת",
"settings.common.enabled": "מופעל",
"settings.common.disabled": "מושבת",
"settings.section.appearance.title": "מראה",
"settings.section.appearance.subtitle": "שנה כיצד האפליקציה נראית במכשיר זה.",
"settings.appearance.theme.title": "ערכת נושא",
"settings.appearance.theme.subtitle": "בחר את מצב הצבע שישמש בכל האפליקציה.",
"settings.appearance.theme.option.system": "התאם להגדרת מערכת ההפעלה",
"settings.appearance.theme.option.light": "השתמש במראה בהיר",
"settings.appearance.theme.option.dark": "השתמש במראה כהה",
"settings.section.notifications.title": "התראות",
"settings.section.notifications.subtitle": "שלוט בהתראות ברמת מערכת ההפעלה עבור פעילות סשן.",
"settings.notifications.permission.granted": "ניתן",
"settings.notifications.permission.denied": "נדחה",
"settings.notifications.permission.default": "לא ניתן",
"settings.notifications.permission.unsupported": "לא נתמך",
"settings.notifications.messages.unsupportedEnvironment": "התראות מערכת ההפעלה אינן נתמכות בסביבה זו.",
"settings.notifications.messages.permissionDenied": "הרשאת התראות נדחתה. הפעל התראות בהגדרות המערכת או הדפדפן.",
"settings.notifications.messages.permissionNotGranted": "הרשאת התראות לא ניתנה.",
"settings.notifications.messages.unsupportedGeneral": "התראות אינן נתמכות בסביבה זו.",
"settings.notifications.messages.permissionGranted": "ההרשאה ניתנה. כעת ניתן להפעיל התראות.",
"settings.notifications.messages.permissionRequestDenied": "ההרשאה נדחתה. ייתכן שתצטרך להפעיל התראות בהגדרות המערכת או הדפדפן.",
"settings.notifications.sessionStatus.title": "התראות סטטוס סשן",
"settings.notifications.sessionStatus.subtitle": "קבל התראות כאשר סשנים דורשים את תשומת לבך.",
"settings.notifications.enable.title": "הפעל התראות",
"settings.notifications.enable.permission": "הרשאה: {permission}",
"settings.notifications.requestPermission.title": "בקש הרשאה",
"settings.notifications.requestPermission.subtitle": "אפשר לאפליקציה לשלוח התראות במכשיר זה.",
"settings.notifications.requestPermission.action": "בקש",
"settings.notifications.allowVisible.title": "התרע כאשר האפליקציה ממוקדת",
"settings.notifications.allowVisible.subtitle": "שמור על התראות פעילות גם כאשר חלון זה גלוי.",
"settings.notifications.unsupportedNote": "התראות אינן נתמכות בסביבה זו. פקד ההתראות נשאר מושבת.",
"settings.notifications.events.title": "התרע אותי כאשר",
"settings.notifications.events.subtitle": "בחר אילו אירועי סשן ישלחו התראות.",
"settings.notifications.events.needsInput": "הסשן דורש קלט",
"settings.notifications.events.idle": "הסשן עובר למצב סרלה",
"settings.notifications.status.enabled": "התראות מופעלות",
"settings.notifications.status.disabled": "התראות מושבתות",
"settings.notifications.status.unsupported": "התראות לא נתמכות",
"settings.section.remote.title": "גישה מרוחקת",
"settings.section.remote.subtitle": "בדוק כיצד שרת זה חשוף ברשת שלך ואבטח אישורי גישה.",
"settings.section.opencode.title": "OpenCode",
"settings.section.opencode.subtitle": "בחר את הקובץ הבינארי של OpenCode והסביבה לשימוש במופעים חדשים.",
"settings.opencode.runtime.title": "סביבת ריצה",
"settings.opencode.runtime.subtitle": "הגדר עם איזה קובץ בינארי של OpenCode מופעים חדשים יופעלו.",
"settings.appearance.behavior.title": "אינטראקציה",
"settings.appearance.behavior.subtitle": "ברירות מחדל להודעות, diff וקלט.",
"settings.behavior.keyboardHints.title": "רמזי קיצורי מקלדת",
"settings.behavior.keyboardHints.subtitle": "הצג רמזי קיצורי מקלדת בכל הממשק.",
"settings.behavior.thinking.title": "קטעי חשיבה",
"settings.behavior.thinking.subtitle": "הצג או הסתר קטעי חשיבה של ה-AI בהודעות.",
"settings.behavior.thinkingDefault.title": "ברירת מחדל לחשיבה",
"settings.behavior.thinkingDefault.subtitle": "בחר האם קטעי חשיבה מתחילים פרוסים או מכווצים.",
"settings.behavior.timelineTools.title": "קריאות כלי בציר הזמן",
"settings.behavior.timelineTools.subtitle": "הצג או הסתר קריאות כלי בציר הודעות.",
"settings.behavior.diffView.title": "תצוגת diff",
"settings.behavior.diffView.subtitle": "בחר כיצד מוצגים diff של קריאות כלי.",
"settings.behavior.diffView.option.split": "מפוצל",
"settings.behavior.diffView.option.unified": "מאוחד",
"settings.behavior.toolOutputsDefault.title": "ברירת מחדל לפלטי כלים",
"settings.behavior.toolOutputsDefault.subtitle": "בחר האם פלטי כלים מתחילים פרוסים או מכווצים.",
"settings.behavior.diagnosticsDefault.title": "ברירת מחדל לאבחון",
"settings.behavior.diagnosticsDefault.subtitle": "בחר האם פלט אבחון מתחיל פרוס או מכווץ.",
"settings.behavior.toolInputsVisibility.title": "נראות קלטי כלים",
"settings.behavior.toolInputsVisibility.subtitle": "הגדר נראות ברירת מחדל לארגומנטים של קריאות כלי.",
"settings.behavior.usageMetrics.title": "מדדי שימוש בטוקנים",
"settings.behavior.usageMetrics.subtitle": "הצג או הסתר נתוני טוקנים ועלות להודעות הסוכן.",
"settings.behavior.autoCleanup.title": "ניקוי אוטומטי של סשנים ריקים",
"settings.behavior.autoCleanup.subtitle": "נקה אוטומטית סשנים ריקים בעת יצירת סשנים חדשים.",
"settings.behavior.promptSubmit.title": "Enter לשליחה",
"settings.behavior.promptSubmit.subtitle": "השתמש ב-Enter לשליחת פקודות; Cmd/Ctrl+Enter מוסיף שורה חדשה.",
} as const

View File

@@ -1,6 +0,0 @@
export const timeMessages = {
"time.relative.justNow": "עכשיו",
"time.relative.daysAgoShort": "לפני {count} ימים",
"time.relative.hoursAgoShort": "לפני {count} שעות",
"time.relative.minutesAgoShort": "לפני {count} דקות",
} as const

View File

@@ -1,132 +0,0 @@
export const toolCallMessages = {
"toolCall.pending.waitingToRun": "ממתין להרצה...",
"toolCall.error.label": "שגיאה:",
"toolCall.header.copyTitle": "העתק כותרת קריאת כלי",
"toolCall.header.copyAriaLabel": "העתק כותרת קריאת כלי",
"toolCall.header.showInputTitle": "הצג ארגומנטי כלי",
"toolCall.header.showInputAriaLabel": "הצג ארגומנטי כלי",
"toolCall.header.hideInputTitle": "הסתר ארגומנטי כלי",
"toolCall.header.hideInputAriaLabel": "הסתר ארגומנטי כלי",
"toolCall.io.input": "קלט כלי",
"toolCall.io.output": "פלט כלי",
"toolCall.diff.label": "Diff",
"toolCall.diff.label.withPath": "Diff · {path}",
"toolCall.diff.viewMode.ariaLabel": "מצב תצוגת diff",
"toolCall.diff.viewMode.split": "מפוצל",
"toolCall.diff.viewMode.unified": "מאוחד",
"toolCall.diagnostics.title": "אבחון",
"toolCall.diagnostics.ariaLabel": "אבחון",
"toolCall.diagnostics.ariaLabel.withLabel": "אבחון {label}",
"toolCall.diagnostics.severity.error.short": "שגיאה",
"toolCall.diagnostics.severity.warning.short": "אזהרה",
"toolCall.diagnostics.severity.info.short": "מידע",
"toolCall.renderer.toolName.shell": "מעטפת",
"toolCall.renderer.toolName.fetch": "Fetch",
"toolCall.renderer.toolName.invalid": "לא תקין",
"toolCall.renderer.toolName.plan": "תוכנית",
"toolCall.renderer.toolName.applyPatch": "החל תיקון",
"toolCall.renderer.action.working": "עובד...",
"toolCall.renderer.action.writingCommand": "כותב פקודה...",
"toolCall.renderer.action.preparingEdit": "מכין עריכה...",
"toolCall.renderer.action.readingFile": "קורא קובץ...",
"toolCall.renderer.action.preparingWrite": "מכין כתיבה...",
"toolCall.renderer.action.preparingPatch": "מכין תיקון...",
"toolCall.renderer.action.planning": "מתכנן...",
"toolCall.renderer.action.fetchingFromWeb": "מאחזר מהאינטרנט...",
"toolCall.renderer.action.findingFiles": "מחפש קבצים...",
"toolCall.renderer.action.searchingContent": "מחפש תוכן...",
"toolCall.renderer.action.listingDirectory": "מפרט ספרייה...",
"toolCall.renderer.bash.title.timeout": "פסק זמן: {timeout}",
"toolCall.renderer.read.detail.offset": "היסט: {offset}",
"toolCall.renderer.read.detail.limit": "מגבלה: {limit}",
"toolCall.renderer.todo.empty": "אין פריטי תוכנית עדיין.",
"toolCall.renderer.todo.status.pending": "ממתין",
"toolCall.renderer.todo.status.inProgress": "בביצוע",
"toolCall.renderer.todo.status.completed": "הושלם",
"toolCall.renderer.todo.status.cancelled": "בוטל",
"toolCall.renderer.todo.title.plan": "תוכנית",
"toolCall.renderer.todo.title.creating": "יוצר תוכנית",
"toolCall.renderer.todo.title.completing": "משלים תוכנית",
"toolCall.renderer.todo.title.updating": "מעדכן תוכנית",
"toolCall.permission.status.required": "נדרש אישור",
"toolCall.permission.status.queued": "אישור בתור",
"toolCall.permission.requestedDiff.label": "diff מבוקש",
"toolCall.permission.requestedDiff.withPath": "diff מבוקש · {path}",
"toolCall.permission.queuedText": "ממתין לתגובות אישור קודמות.",
"toolCall.permission.actions.allowOnce": "אפשר פעם אחת",
"toolCall.permission.actions.alwaysAllow": "אפשר תמיד",
"toolCall.permission.actions.deny": "דחה",
"toolCall.permission.shortcuts.allowOnce": "אפשר פעם אחת",
"toolCall.permission.shortcuts.alwaysAllow": "אפשר תמיד",
"toolCall.permission.shortcuts.deny": "דחה",
"toolCall.permission.errors.unableToUpdate": "לא ניתן לעדכן אישור",
"permissionApproval.title": "בקשות",
"permissionApproval.empty": "אין בקשות ממתינות.",
"permissionApproval.kind.permission": "אישור",
"permissionApproval.kind.question": "שאלה",
"permissionApproval.questionCount.one": "שאלה אחת",
"permissionApproval.questionCount.other": "{count} שאלות",
"permissionApproval.status.active": "פעיל",
"permissionApproval.actions.closeAriaLabel": "סגור",
"permissionApproval.actions.goToSession": "עבור לסשן",
"permissionApproval.actions.loadingSession": "טוען…",
"permissionApproval.actions.loadSession": "טען סשן",
"permissionApproval.actions.allowOnce": "אפשר פעם אחת",
"permissionApproval.actions.alwaysAllow": "אפשר תמיד",
"permissionApproval.actions.deny": "דחה",
"permissionApproval.fallbackHint": "טען סשן לקבלת מידע נוסף.",
"permissionApproval.errors.unableToUpdatePermission": "לא ניתן לעדכן אישור",
"toolCall.question.status.required": "נדרשת תשובה",
"toolCall.question.status.queued": "שאלה בתור",
"toolCall.question.status.questions": "שאלות",
"toolCall.question.action.awaitingAnswers": "ממתין לתשובות...",
"toolCall.question.title.questions": "שאלות",
"toolCall.question.title.askingQuestions": "שואל שאלות",
"toolCall.question.type.one": "שאלה",
"toolCall.question.type.other": "שאלות",
"toolCall.question.number": "ש{number}:",
"toolCall.question.multiple": "מרובות",
"toolCall.question.custom.title": "הקלד תשובה מותאמת אישית",
"toolCall.question.custom.label": "תשובה מותאמת אישית",
"toolCall.question.custom.placeholder": "הקלד תשובה משלך",
"toolCall.question.actions.submit": "שלח",
"toolCall.question.actions.dismiss": "סגור",
"toolCall.question.shortcuts.submit": "שלח",
"toolCall.question.shortcuts.dismiss": "סגור",
"toolCall.question.queuedText": "ממתין לתגובות קודמות.",
"toolCall.question.validation.answerAll": "אנא ענה על כל השאלות לפני השליחה.",
"toolCall.question.errors.unableToReply": "לא ניתן לשלוח תשובה",
"toolCall.question.errors.unableToDismiss": "לא ניתן לסגור",
"toolCall.task.action.delegating": "מאציל...",
"toolCall.task.sections.prompt": "פקודה",
"toolCall.task.sections.steps": "שלבים",
"toolCall.task.sections.output": "פלט",
"toolCall.task.steps.count": "{count} שלבים",
"toolCall.task.meta.agentModel": "סוכן: {agent} • מודל: {model}",
"toolCall.task.meta.agent": "סוכן: {agent}",
"toolCall.task.meta.model": "מודל: {model}",
"toolCall.status.pending": "ממתין",
"toolCall.status.running": "רץ",
"toolCall.status.completed": "הושלם",
"toolCall.status.error": "שגיאה",
"toolCall.status.unknown": "לא ידוע",
"toolCall.applyPatch.action.preparing": "מכין apply_patch...",
"toolCall.applyPatch.title.withFileCount.one": "{tool} (קובץ אחד)",
"toolCall.applyPatch.title.withFileCount.other": "{tool} ({count} קבצים)",
"toolCall.applyPatch.fileFallback": "קובץ {number}",
} as const

View File

@@ -4,7 +4,7 @@ import { ThemeProvider } from "./lib/theme"
import { ConfigProvider } from "./stores/preferences"
import { InstanceConfigProvider } from "./stores/instance-config"
import { runtimeEnv } from "./lib/runtime-env"
import { I18nProvider, preloadLocaleMessages } from "./lib/i18n"
import { I18nProvider } from "./lib/i18n"
import { storage } from "./lib/storage"
import "./index.css"
import "@git-diff-view/solid/styles/diff-view-pure.css"
@@ -31,19 +31,15 @@ async function bootstrap() {
try {
const uiConfig = await storage.loadConfigOwner("ui")
const theme = (uiConfig as any)?.theme
const locale = typeof (uiConfig as any)?.settings?.locale === "string" ? (uiConfig as any).settings.locale : undefined
const theme = (uiConfig as any)?.theme ?? "system"
if (theme === "light" || theme === "dark") {
document.documentElement.setAttribute("data-theme", theme)
} else {
if (theme === "system") {
document.documentElement.removeAttribute("data-theme")
} else {
document.documentElement.setAttribute("data-theme", theme)
}
await preloadLocaleMessages(locale)
} catch {
// If config fails to load, fall back to CSS defaults.
await preloadLocaleMessages()
}
}

View File

@@ -124,7 +124,7 @@
display: flex;
align-items: center;
gap: var(--space-md);
text-align: start;
text-align: left;
background: transparent;
border: none;
color: var(--text-primary);

View File

@@ -40,11 +40,11 @@
}
.selector-trigger-primary--align-left {
@apply text-start w-full;
@apply text-left w-full;
}
.selector-trigger-secondary {
@apply text-xs text-start truncate w-full;
@apply text-xs text-left truncate;
color: var(--text-muted);
}

View File

@@ -43,7 +43,7 @@
padding: 1.25rem;
background:
linear-gradient(180deg, color-mix(in oklab, var(--surface-secondary) 92%, var(--accent-primary) 8%), var(--surface-secondary));
border-inline-end: 1px solid var(--border-base);
border-right: 1px solid var(--border-base);
}
.settings-screen-nav-header {
@@ -121,9 +121,6 @@
color: var(--text-primary);
transform: translateX(2px);
}
[dir="rtl"] .settings-nav-button[data-selected="true"] {
transform: translateX(-2px);
}
.settings-nav-button-icon {
width: 1rem;
@@ -363,7 +360,7 @@
border: 1px solid var(--border-base);
background: var(--surface-base);
color: var(--text-primary);
text-align: start;
text-align: left;
transition: border-color 140ms ease, background-color 140ms ease, box-shadow 140ms ease, transform 140ms ease;
outline: none;
cursor: pointer;
@@ -421,7 +418,7 @@
}
.settings-choice-check {
margin-inline-start: auto;
margin-left: auto;
color: var(--accent-primary);
opacity: 0;
}
@@ -491,7 +488,7 @@
.settings-screen-nav {
gap: 0.75rem;
padding: 1rem;
border-inline-end: none;
border-right: none;
border-bottom: 1px solid var(--border-base);
}

View File

@@ -24,21 +24,6 @@
color: inherit;
}
/* Auto-detect text direction per block element for RTL language support (e.g. Hebrew, Arabic) */
.markdown-body p,
.markdown-body li,
.markdown-body h1,
.markdown-body h2,
.markdown-body h3,
.markdown-body h4,
.markdown-body h5,
.markdown-body h6,
.markdown-body blockquote,
.markdown-body td,
.markdown-body th {
unicode-bidi: plaintext;
}
.markdown-body h1,
.markdown-body h2,
.markdown-body h3,
@@ -136,7 +121,6 @@
border-radius: 8px;
padding: 0.75rem;
margin: 1rem 0;
direction: ltr;
}
.markdown-body pre:not(.shiki) code,
@@ -145,19 +129,16 @@
}
.markdown-body blockquote {
border-inline-start: 3px solid var(--border-base);
border-left: 3px solid var(--border-base);
color: var(--text-secondary);
background-color: var(--surface-muted);
padding: 0.5rem 1rem;
border-start-start-radius: 0;
border-start-end-radius: 8px;
border-end-end-radius: 8px;
border-end-start-radius: 0;
border-radius: 0 8px 8px 0;
}
.markdown-body ul,
.markdown-body ol {
padding-inline-start: 1.5rem;
padding-left: 1.5rem;
margin: 0.5rem 0;
}
@@ -185,7 +166,7 @@
.markdown-body td {
border: 1px solid var(--border-base);
padding: 0.5rem 0.75rem;
text-align: start;
text-align: left;
color: var(--text-primary);
background-color: transparent;
}
@@ -240,7 +221,7 @@
cursor: pointer;
color: var(--text-secondary);
transition: background-color 150ms ease, color 150ms ease, border-color 150ms ease;
margin-inline-start: auto;
margin-left: auto;
font-size: var(--font-size-sm);
}

View File

@@ -28,7 +28,7 @@
/* Message error block */
.message-error-block {
@apply text-sm p-3 rounded border-s-[3px] my-2;
@apply text-sm p-3 rounded border-l-[3px] my-2;
color: var(--status-error);
background-color: var(--message-error-bg);
border-color: var(--status-error);

View File

@@ -132,22 +132,15 @@
margin-bottom: 0;
}
.message-stream-block {
display: flex;
flex-direction: column;
gap: 2px;
margin-bottom: 2px;
}
.message-step-start {
background-color: var(--message-assistant-bg);
border-inline-start: 4px solid var(--message-assistant-border);
border-left: 4px solid var(--message-assistant-border);
margin-top: 0;
}
.message-step-finish {
background-color: var(--message-assistant-bg);
border-inline-start: 4px solid var(--message-assistant-border);
border-left: 4px solid var(--message-assistant-border);
margin: 0;
}
@@ -172,7 +165,7 @@
font-size: 9px;
color: var(--text-muted);
font-weight: var(--font-weight-medium);
margin-inline-end: 0.35rem;
margin-right: 0.35rem;
}
.message-step-heading {
@@ -189,7 +182,7 @@
}
.message-error-block {
@apply text-sm p-3 rounded border-s-[3px] my-2;
@apply text-sm p-3 rounded border-l-[3px] my-2;
color: var(--status-error);
background-color: var(--message-error-bg);
border-color: var(--status-error);
@@ -258,7 +251,6 @@
padding: 8px;
background-color: var(--surface-code);
border-radius: 4px;
direction: ltr;
}
.message-error-part {
@@ -336,12 +328,12 @@
.message-step-start {
background-color: var(--message-assistant-bg);
border-inline-start: 4px solid var(--message-assistant-border);
border-left: 4px solid var(--message-assistant-border);
}
.message-step-finish {
background-color: var(--message-assistant-bg);
border-inline-start: 4px solid var(--message-assistant-border);
border-left: 4px solid var(--message-assistant-border);
}
.message-step-heading {
@@ -364,7 +356,7 @@
}
.message-step-time {
@apply text-[11px] text-[var(--text-muted)] font-normal ms-auto;
@apply text-[11px] text-[var(--text-muted)] font-normal ml-auto;
}
.message-step-meta-inline {
@@ -391,7 +383,7 @@
.message-reasoning-card {
--reasoning-border-color: var(--border-strong, var(--border-base));
background-color: var(--message-assistant-bg);
border-inline-start: 4px solid var(--message-assistant-border);
border-left: 4px solid var(--message-assistant-border);
margin-top: 0;
margin-bottom: 0;
padding: 0;
@@ -425,7 +417,7 @@
padding: 0.25rem 0.6rem;
font: inherit;
color: inherit;
text-align: start;
text-align: left;
cursor: pointer;
transition: background-color 0.2s ease, box-shadow 0.2s ease;
}
@@ -547,5 +539,4 @@
color: var(--text-primary);
white-space: pre-wrap;
margin: 0;
unicode-bidi: plaintext;
}

View File

@@ -207,7 +207,7 @@
.message-scroll-button-wrapper {
position: absolute;
inset-inline-end: 1rem;
right: 1rem;
bottom: 1rem;
display: flex;
flex-direction: column;
@@ -274,7 +274,7 @@
}
.message-quote-button + .message-quote-button {
border-inline-start: 1px solid var(--list-item-highlight-border);
border-left: 1px solid var(--list-item-highlight-border);
}
.message-quote-button:hover {

View File

@@ -3,7 +3,7 @@
.message-select-checkbox {
width: 14px;
height: 14px;
margin-inline-end: 0.5rem;
margin-right: 0.5rem;
cursor: pointer;
accent-color: var(--status-error);
flex: 0 0 auto;
@@ -134,7 +134,7 @@
}
.message-delete-mode-menu {
inset-inline-end: 0;
right: 0;
bottom: calc(100% + 6px);
min-width: 150px;
width: max-content;

View File

@@ -20,7 +20,7 @@
position: absolute;
top: 0;
bottom: 0;
inset-inline-end: 64px;
right: 64px;
width: 1px;
background-color: var(--border-muted);
pointer-events: none;
@@ -32,7 +32,7 @@
}
.message-layout--with-timeline::after {
inset-inline-end: 40px;
right: 40px;
}
}
@@ -311,12 +311,12 @@
/* Tool segments that are part of a group get a left accent border. */
.message-timeline-group-child {
border-inline-start: 3px solid color-mix(in oklab, var(--accent-primary) 35%, transparent);
border-left: 3px solid color-mix(in oklab, var(--accent-primary) 35%, transparent);
}
/* The assistant "parent" at the bottom of a tool group gets the same border. */
.message-timeline-group-parent {
border-inline-start: 3px solid color-mix(in oklab, var(--accent-primary) 35%, transparent);
border-left: 3px solid color-mix(in oklab, var(--accent-primary) 35%, transparent);
}
/* Extra spacing before the first tool in a group to separate from the
@@ -346,7 +346,7 @@
/* Extend the overlay box into the stream so ribs are not relying on
overflow-visible behavior (which is brittle around scroll containers). */
--xray-overhang: calc(var(--max-rib-width, 50vw) + 84px);
inset-inline-start: calc(-1 * var(--xray-overhang));
left: calc(-1 * var(--xray-overhang));
width: calc(100% + var(--xray-overhang));
overflow: hidden;
padding: 0.25rem;
@@ -374,10 +374,10 @@
.message-timeline-xray-token-label {
position: absolute;
inset-inline-end: 100%;
right: 100%;
top: 50%;
transform: translateY(-50%);
margin-inline-end: 4px;
margin-right: 4px;
height: 1.5rem;
display: flex;
align-items: center;
@@ -403,25 +403,16 @@
var(--status-success) calc(100% - var(--segment-weight) * 100%),
var(--status-error) calc(var(--segment-weight) * 100%)
);
border-start-start-radius: 3px;
border-end-start-radius: 3px;
border-start-end-radius: 0;
border-end-end-radius: 0;
border-radius: 3px 0 0 3px;
transition: width 0.3s ease, background-color 0.3s ease;
box-shadow: -2px 0 4px rgba(0, 0, 0, 0.25);
}
[dir="rtl"] .message-timeline-relative-bar {
box-shadow: 2px 0 4px rgba(0, 0, 0, 0.25);
}
.message-timeline-absolute-bar {
height: 3px;
width: calc(var(--segment-weight) * var(--max-rib-width, 50vw));
background-color: var(--text-muted);
border-start-start-radius: 2px;
border-end-start-radius: 2px;
border-start-end-radius: 0;
border-end-end-radius: 0;
border-radius: 2px 0 0 2px;
transition: width 0.3s ease;
opacity: 0.5;
position: relative;
@@ -434,7 +425,7 @@
.message-timeline-absolute-bar-overflow::before {
content: "";
position: absolute;
inset-inline-start: -1px;
left: -1px;
top: -3px;
bottom: -3px;
width: 3px;

View File

@@ -35,9 +35,7 @@
}
.prompt-input {
@apply w-full pt-2.5 border text-sm resize-none outline-none transition-colors;
padding-inline-start: 2.5rem;
padding-inline-end: 0.75rem;
@apply w-full pl-3 pr-10 pt-2.5 border text-sm resize-none outline-none transition-colors;
font-family: inherit;
background-color: var(--surface-base);
color: var(--text-primary);
@@ -67,8 +65,8 @@
.prompt-input-overlay {
position: absolute;
bottom: 1rem;
inset-inline-start: 0.75rem;
inset-inline-end: 0.75rem;
left: 0.75rem;
right: 0.75rem;
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
@@ -83,13 +81,11 @@
color: var(--text-primary);
}
/* Navigation buttons container (expand, prev, next).
Intentionally at inline-start (left in LTR, right in RTL) so buttons never overlap
the scrollbar, which browsers always place at inline-end. */
/* Navigation buttons container (expand, prev, next) */
.prompt-nav-buttons {
position: absolute;
top: 0.25rem;
inset-inline-start: 0.25rem;
right: 0.25rem;
bottom: 0.25rem;
display: flex;
flex-direction: column;
@@ -164,11 +160,6 @@
@apply opacity-60 cursor-not-allowed;
}
/* In RTL: override dir="auto" which defaults to LTR on empty textarea */
[dir="rtl"] .prompt-input {
direction: rtl;
}
.prompt-input::placeholder {
color: var(--text-muted);
}
@@ -265,7 +256,7 @@
display: none;
position: absolute;
bottom: calc(100% + 6px);
inset-inline-start: 0;
left: 0;
padding: 8px;
background-color: var(--surface-base);
border: 1px solid var(--border-base);
@@ -344,7 +335,6 @@
.prompt-input {
min-height: 0;
padding: 0.5rem 0.75rem;
padding-inline-start: 2.5rem; /* preserve space for nav buttons */
padding-bottom: 0.75rem;
}

View File

@@ -5,7 +5,7 @@
.tool-call-message {
@apply flex flex-col gap-2 p-3 w-full;
background-color: var(--message-tool-bg);
border-inline-start: 4px solid var(--message-tool-border);
border-left: 4px solid var(--message-tool-border);
color: inherit;
}
@@ -94,7 +94,7 @@
}
.tool-call-header-toggle {
@apply flex items-center gap-2 p-2 w-full bg-transparent border-none cursor-pointer text-start;
@apply flex items-center gap-2 p-2 w-full bg-transparent border-none cursor-pointer text-left;
font-family: var(--font-family-mono);
font-size: 13px;
border-radius: 0;
@@ -105,7 +105,7 @@
.tool-call-header-toggle::before {
content: "▶";
font-size: 11px;
margin-inline-end: 0.35rem;
margin-right: 0.35rem;
color: var(--text-secondary);
}
@@ -159,7 +159,7 @@
}
.tool-call-summary {
@apply flex-1 text-start inline-flex items-center gap-2;
@apply flex-1 text-left inline-flex items-center gap-2;
color: var(--text-primary);
}
@@ -168,26 +168,26 @@
}
.tool-call-summary[data-tool-icon=""]::before {
margin-inline-end: 0;
margin-right: 0;
content: "";
}
.tool-call-summary[data-tool-icon]:not([data-tool-icon=""])::before {
margin-inline-end: 0.35rem;
margin-right: 0.35rem;
}
/* ToolState uses status="completed"; keep "success" as a legacy alias. */
.tool-call-status-completed,
.tool-call-status-success {
border-inline-start: 3px solid var(--status-success);
border-left: 3px solid var(--status-success);
}
.tool-call-status-error {
border-inline-start: 3px solid var(--status-error);
border-left: 3px solid var(--status-error);
}
.tool-call-status-running {
border-inline-start: 3px solid var(--status-warning);
border-left: 3px solid var(--status-warning);
}
.tool-call-status-running .tool-call-status {
@@ -195,7 +195,7 @@
}
.tool-call-status-pending {
border-inline-start: 3px solid var(--accent-primary);
border-left: 3px solid var(--accent-primary);
}
.tool-call-status-pending .tool-call-summary {
@@ -257,7 +257,7 @@
border: none;
border-bottom: 1px solid var(--tool-call-border-color);
width: 100%;
text-align: start;
text-align: left;
font-size: 0.875rem;
font-weight: normal;
color: var(--text-primary);
@@ -267,7 +267,7 @@
.tool-call-io-toggle::before {
content: "▶";
font-size: 11px;
margin-inline-end: 0.35rem;
margin-right: 0.35rem;
color: var(--text-secondary);
}
@@ -393,7 +393,7 @@
}
.tool-call-awaiting-permission {
border-inline-start-color: var(--status-warning);
border-left-color: var(--status-warning);
}
.tool-call-permission {
@@ -484,7 +484,7 @@
}
.tool-call-permission-shortcuts .kbd {
margin-inline-end: 0.25rem;
margin-right: 0.25rem;
}
.tool-call-permission-queued-text {
@@ -549,7 +549,6 @@
min-height: auto;
max-height: none;
overflow-y: visible;
direction: ltr;
}
/* Shiki injects inline background colors; force token surfaces. */
@@ -611,7 +610,7 @@
}
.tool-call-diagnostics-heading {
@apply flex items-center gap-2 p-2 w-full border-none cursor-pointer text-start;
@apply flex items-center gap-2 p-2 w-full border-none cursor-pointer text-left;
font-family: var(--font-family-mono);
font-size: 13px;
color: var(--text-primary);
@@ -635,7 +634,7 @@
.tool-call-diagnostics-heading {
@apply flex items-center gap-2 p-2 w-full border-none cursor-pointer text-start;
@apply flex items-center gap-2 p-2 w-full border-none cursor-pointer text-left;
font-family: var(--font-family-mono);
font-size: 13px;
color: var(--text-primary);
@@ -694,8 +693,8 @@
gap: var(--space-xs);
max-height: calc(4 * var(--tool-call-line-unit, 1.4em));
overflow-y: scroll;
padding-inline-end: 0;
margin-inline-end: 0;
padding-right: 0;
margin-right: 0;
scrollbar-gutter: stable both-edges;
scrollbar-width: thin;
}
@@ -763,7 +762,6 @@
overflow-x: auto;
max-height: var(--tool-call-max-height-compact, calc(25 * 1.4em));
overflow-y: scroll;
direction: ltr;
}
.tool-call-section code {
@@ -845,7 +843,7 @@
.tool-call-error-content {
background-color: var(--message-error-bg);
border-inline-start: 3px solid var(--status-error);
border-left: 3px solid var(--status-error);
padding: 12px;
margin: 8px 0;
border-radius: 4px;

View File

@@ -86,7 +86,7 @@
.tool-call-task-summary .tool-call::before {
content: "";
position: absolute;
inset-inline-start: 0;
left: 0;
top: 0;
bottom: 0;
width: 3px;
@@ -117,7 +117,7 @@
align-items: center;
gap: 0.4rem;
padding: 0.35rem 0.5rem 0.35rem 0.75rem;
border-inline-start: 2px solid var(--tool-call-border-color, var(--border-base));
border-left: 2px solid var(--tool-call-border-color, var(--border-base));
font-size: var(--font-size-sm);
font-family: var(--font-family-mono);
line-height: 1.35;
@@ -134,19 +134,19 @@
}
.tool-call-task-item[data-task-status="completed"] {
border-inline-start-color: var(--status-success);
border-left-color: var(--status-success);
}
.tool-call-task-item[data-task-status="running"] {
border-inline-start-color: var(--status-warning);
border-left-color: var(--status-warning);
}
.tool-call-task-item[data-task-status="pending"] {
border-inline-start-color: var(--accent-primary);
border-left-color: var(--accent-primary);
}
.tool-call-task-item[data-task-status="error"] {
border-inline-start-color: var(--status-error);
border-left-color: var(--status-error);
}
.tool-call-task-icon {

View File

@@ -46,7 +46,7 @@
.virtual-follow-list-controls-container {
position: absolute;
bottom: calc(var(--space-md) + env(safe-area-inset-bottom, 0px));
inset-inline-end: var(--space-md);
right: var(--space-md);
z-index: 20;
}

View File

@@ -42,7 +42,7 @@
}
.modal-item {
@apply w-full px-4 py-3 flex items-start gap-3 transition-colors cursor-pointer border-none text-start;
@apply w-full px-4 py-3 flex items-start gap-3 transition-colors cursor-pointer border-none text-left;
color: var(--text-primary);
}

View File

@@ -77,7 +77,7 @@
}
.panel-list-item-content {
@apply flex-1 text-start px-4 py-3 flex items-center justify-between gap-3 outline-none transition-colors w-full min-w-0;
@apply flex-1 text-left px-4 py-3 flex items-center justify-between gap-3 outline-none transition-colors w-full min-w-0;
}
.panel-list-item-content:hover {

View File

@@ -11,8 +11,8 @@
content: '';
position: absolute;
bottom: -1px;
inset-inline-start: 0;
inset-inline-end: 0;
left: 0;
right: 0;
height: 1px;
background-color: var(--border-base);
z-index: 0;
@@ -42,7 +42,7 @@
border: 1px solid transparent;
border-bottom: none;
border-radius: 8px 8px 0 0;
margin-inline-end: 2px;
margin-right: 2px;
z-index: 1;
}
@@ -90,8 +90,8 @@
.file-split-handle {
cursor: col-resize;
background-color: transparent;
border-inline-start: 1px solid var(--border-base);
border-inline-end: 1px solid var(--border-base);
border-left: 1px solid var(--border-base);
border-right: 1px solid var(--border-base);
user-select: none;
touch-action: none;
}
@@ -148,7 +148,7 @@
display: flex;
flex-direction: column;
background-color: var(--surface-secondary);
border-inline-start: 1px solid var(--border-base);
border-left: 1px solid var(--border-base);
/* Monaco uses layered positioned elements; keep overlay well above it. */
z-index: 200;
}
@@ -334,7 +334,6 @@
.monaco-viewer {
width: 100%;
height: 100%;
direction: ltr;
}
.file-viewer-empty {
@@ -460,7 +459,7 @@
}
.section-label {
margin-inline-start: 2px;
margin-left: 2px;
}
.section-info-icon {
@@ -529,7 +528,7 @@
}
.right-panel-empty--left {
@apply items-start justify-start text-start w-full;
@apply items-start justify-start text-left w-full;
}
.right-panel-empty-text {

View File

@@ -26,10 +26,10 @@
position: absolute;
top: 0;
bottom: 0;
inset-inline-start: 0;
left: 0;
width: min(90vw, 360px);
max-width: 360px;
border-inline-end: 1px solid var(--border-base);
border-right: 1px solid var(--border-base);
box-shadow: var(--folder-card-shadow);
transform: translateX(0);
transition: transform 0.25s ease, opacity 0.2s ease;
@@ -41,9 +41,6 @@
opacity: 0;
pointer-events: none;
}
[dir="rtl"] .session-sidebar-collapsed {
transform: translateX(100%);
}
.session-sidebar-backdrop {
@apply absolute inset-0;
@@ -57,7 +54,7 @@
.session-sidebar-menu-button--floating {
position: absolute;
top: 1rem;
inset-inline-start: 1rem;
left: 1rem;
z-index: 20;
}
@@ -131,7 +128,7 @@ session-sidebar-controls .selector-trigger-primary {
.mobile-fullscreen-exit-wrapper {
position: fixed;
top: calc(env(safe-area-inset-top, 0px) + 12px);
inset-inline-end: calc(env(safe-area-inset-right, 0px) + 12px);
right: calc(env(safe-area-inset-right, 0px) + 12px);
z-index: 1250;
pointer-events: none;
}
@@ -146,11 +143,11 @@ session-sidebar-controls .selector-trigger-primary {
}
.session-resize-handle--left {
inset-inline-end: 0;
right: 0;
}
.session-resize-handle--right {
inset-inline-start: 0;
left: 0;
}
.session-resize-handle:hover {
@@ -163,20 +160,14 @@ session-sidebar-controls .selector-trigger-primary {
}
.session-resize-handle--left::before {
inset-inline-end: 0;
right: 0;
transform: translateX(50%);
}
[dir="rtl"] .session-resize-handle--left::before {
transform: translateX(-50%);
}
.session-resize-handle--right::before {
inset-inline-start: 0;
left: 0;
transform: translateX(-50%);
}
[dir="rtl"] .session-resize-handle--right::before {
transform: translateX(50%);
}
.session-list-header {
@apply border-b relative;
@@ -199,14 +190,14 @@ session-sidebar-controls .selector-trigger-primary {
}
.session-item-base {
@apply w-full flex flex-col gap-1 px-3 py-2.5 text-start transition-colors outline-none;
@apply w-full flex flex-col gap-1 px-3 py-2.5 text-left transition-colors outline-none;
font-family: var(--font-family-sans);
font-size: var(--font-size-sm);
}
.session-item-base.session-item-child {
padding-inline-start: 2.25rem;
padding-left: 2.25rem;
position: relative;
}
@@ -215,7 +206,7 @@ session-sidebar-controls .selector-trigger-primary {
position: absolute;
top: 0;
bottom: 0;
inset-inline-start: 1.125rem;
left: 1.125rem;
width: 1px;
background-color: var(--text-secondary);
opacity: 0.95;
@@ -230,7 +221,7 @@ session-sidebar-controls .selector-trigger-primary {
content: "";
position: absolute;
top: 50%;
inset-inline-start: 1.125rem;
left: 1.125rem;
width: 0.875rem;
height: 1px;
background-color: var(--text-secondary);
@@ -240,11 +231,11 @@ session-sidebar-controls .selector-trigger-primary {
}
.session-item-base.session-item-border-user {
border-inline-start: 4px solid var(--message-user-border);
border-left: 4px solid var(--message-user-border);
}
.session-item-base.session-item-border-assistant {
border-inline-start: 4px solid var(--message-assistant-border);
border-left: 4px solid var(--message-assistant-border);
}
.session-item-expander {