дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 11:15:40 +02:00
Merge origin/main into feat/agents
Claude-Session: https://claude.ai/code/session_01VUYsdXA2nkF5v6zsC93dpn
Цей коміт міститься в:
коміт
4548cf05ea
286 files changed
+16464
-993
No files matched your search
@@ -34,6 +34,7 @@ dirs.workspace = true
|
||||
|
||||
ide-core.workspace = true
|
||||
dunce.workspace = true
|
||||
ide-clipboard.workspace = true
|
||||
ide-text.workspace = true
|
||||
ide-vfs.workspace = true
|
||||
ide-workspace.workspace = true
|
||||
|
||||
@@ -42,6 +42,28 @@ pub(crate) fn load(root: &Path, cache: &Path, settings: &Settings) -> BuildModel
|
||||
if let Some(g) = &mut model.gradle {
|
||||
g.env = java_env(jdk.as_ref(), g.java_version);
|
||||
}
|
||||
// A failed sync of a project no installed JDK can build says so first (with a Download JDK action).
|
||||
let missing = |release: Option<u32>| match jdk {
|
||||
Some(_) => None,
|
||||
None => ide_sdk::java::missing_jdk_message(&ide_sdk::java::installed_jdks(), release),
|
||||
};
|
||||
let explain = |error: &mut Option<String>, hint: Option<String>| {
|
||||
if let (Some(e), Some(hint)) = (error.as_mut(), hint) {
|
||||
*e = format!("{hint}\n{e}");
|
||||
}
|
||||
};
|
||||
if let Some(m) = &mut model.maven
|
||||
&& m.sync_error.is_some()
|
||||
{
|
||||
let hint = m.projects.iter().find_map(|p| missing(p.java_version));
|
||||
explain(&mut m.sync_error, hint);
|
||||
}
|
||||
if let Some(g) = &mut model.gradle
|
||||
&& g.sync_error.is_some()
|
||||
{
|
||||
let hint = missing(g.java_version);
|
||||
explain(&mut g.sync_error, hint);
|
||||
}
|
||||
model
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
//! The OS clipboard's file list, so the project view and Explorer exchange files.
|
||||
|
||||
use crate::commands::doc::blocking;
|
||||
use ide_core::IdeResult;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// Absolute paths on the clipboard, empty when it holds no file list.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn clipboard_file_list() -> IdeResult<Vec<String>> {
|
||||
blocking(|| Ok(ide_clipboard::files().iter().map(|p| slash(p)).collect())).await
|
||||
}
|
||||
|
||||
/// Whether the clipboard holds files: cheap, for the Paste item's enabled state.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub fn clipboard_has_files() -> bool {
|
||||
ide_clipboard::has_files()
|
||||
}
|
||||
|
||||
/// Replaces the clipboard with `paths`, so Explorer can paste them. Best-effort:
|
||||
/// false when the clipboard stayed busy; the caller keeps working either way.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn clipboard_set_file_list(paths: Vec<PathBuf>) -> IdeResult<bool> {
|
||||
blocking(move || Ok(ide_clipboard::set_files(&paths))).await
|
||||
}
|
||||
|
||||
/// Paths cross the IPC boundary `/`-separated, as every other backend path does.
|
||||
fn slash(path: &Path) -> String {
|
||||
path.to_string_lossy().replace('\\', "/")
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
use crate::state::AppState;
|
||||
use ide_core::{IdeError, IdeResult, IpcPath, RequestId};
|
||||
use ide_db::split::SqlStatement;
|
||||
use ide_db::{CompletionModel, DataSource, DbRowEdit, QueryResult, SortKey, TableDetails, TableInfo, TablePage};
|
||||
use ide_db::{
|
||||
CompletionModel, DataSource, DbObjectKind, DbRowEdit, HistoryEntry, PlanNode, QueryResult, SchemaObjects, SortKey,
|
||||
TableDetails, TableInfo, TablePage,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specta::Type;
|
||||
use tauri::{State, Window};
|
||||
@@ -175,6 +178,72 @@ pub async fn db_ddl(
|
||||
db.ddl(&id, &schema, &table).await
|
||||
}
|
||||
|
||||
/// The routines and sequences of `schema` (the tree's `routines` / `sequences`).
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn db_schema_objects(
|
||||
window: Window,
|
||||
state: State<'_, AppState>,
|
||||
id: String,
|
||||
schema: String,
|
||||
) -> IdeResult<SchemaObjects> {
|
||||
let db = state.project(window.label())?.db.clone();
|
||||
db.schema_objects(&id, &schema).await
|
||||
}
|
||||
|
||||
/// The definition of a routine, trigger or sequence (Go to DDL, Quick Documentation).
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn db_object_ddl(
|
||||
window: Window,
|
||||
state: State<'_, AppState>,
|
||||
id: String,
|
||||
schema: String,
|
||||
kind: DbObjectKind,
|
||||
name: String,
|
||||
) -> IdeResult<String> {
|
||||
let db = state.project(window.label())?.db.clone();
|
||||
db.object_ddl(&id, &schema, kind, &name).await
|
||||
}
|
||||
|
||||
/// Explain Plan: the execution plan of `statement` on `id`, without running it.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn db_explain(
|
||||
window: Window,
|
||||
state: State<'_, AppState>,
|
||||
request: RequestId,
|
||||
id: String,
|
||||
statement: String,
|
||||
) -> IdeResult<Vec<PlanNode>> {
|
||||
let db = state.project(window.label())?.db.clone();
|
||||
let cancel = state.begin_request(request);
|
||||
let result = db.explain(&id, &statement, &cancel).await;
|
||||
state.end_request(request);
|
||||
result
|
||||
}
|
||||
|
||||
/// The statements data source `id` ran, newest first (Browse Query History).
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn db_history(window: Window, state: State<'_, AppState>, id: String) -> IdeResult<Vec<HistoryEntry>> {
|
||||
let db = state.project(window.label())?.db.clone();
|
||||
db.history(&id).await
|
||||
}
|
||||
|
||||
/// Removes statements from the query history of `id` (`None`: all of them).
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn db_remove_history(
|
||||
window: Window,
|
||||
state: State<'_, AppState>,
|
||||
id: String,
|
||||
texts: Option<Vec<String>>,
|
||||
) -> IdeResult<()> {
|
||||
let db = state.project(window.label())?.db.clone();
|
||||
db.remove_history(&id, texts).await
|
||||
}
|
||||
|
||||
/// Writes exported result data (CSV, JSON, SQL…) to `path`.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
|
||||
@@ -264,6 +264,22 @@ pub async fn debug_attachable_processes() -> IdeResult<Vec<attach::AttachablePro
|
||||
.map_err(|e| ide_core::IdeError::internal(format!("listing processes failed: {e}")))?
|
||||
}
|
||||
|
||||
/// Remote JVM Debug in listen mode: waits (cancellable through `request`) for the remote
|
||||
/// JVM to connect to `host:port`; returns the loopback port java-debug attaches to.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn debug_listen_for_jvm(
|
||||
state: State<'_, AppState>,
|
||||
request: ide_core::RequestId,
|
||||
host: String,
|
||||
port: u16,
|
||||
) -> IdeResult<u16> {
|
||||
let token = state.begin_request(request);
|
||||
let result = jdwp::listen_for_jvm(&host, port, token).await;
|
||||
state.end_request(request);
|
||||
result
|
||||
}
|
||||
|
||||
/// HotSwap (Reload Changed Classes) in a java-debug session.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
|
||||
@@ -48,17 +48,23 @@ pub async fn java_resolve_launch(
|
||||
let java = project_jdk.map(|j| ide_sdk::java::java_executable(&j.home)).or(from_jdtls).or_else(|| {
|
||||
let release = ide_sdk::java::project_java_version(&module);
|
||||
let jdks = ide_sdk::java::installed_jdks();
|
||||
info.hints.extend(ide_sdk::java::missing_jdk_message(&jdks, release));
|
||||
ide_sdk::java::pick_jdk(&jdks, release).map(|j| ide_sdk::java::java_executable(&j.home))
|
||||
});
|
||||
info.java_exec = Some(java.map(|p| p.to_string_lossy().into_owned()).unwrap_or_else(|| "java".to_owned()));
|
||||
let exec = PathBuf::from(info.java_exec.as_deref().unwrap_or("java"));
|
||||
let shorten = ide_run::java_cmdline::shortening_for(&exec, &info.class_paths, &info.module_paths, cfg!(windows));
|
||||
info.shorten_command_line = Some(shorten.as_str().to_owned());
|
||||
if info.error_count > 0 {
|
||||
let vm_args = &settings.languages.java.jdtls_vm_args;
|
||||
let lombok = ide_sdk::lombok::lombok_agent(
|
||||
&project.root,
|
||||
ide_sdk::lombok::agent_in_args(vm_args.split_whitespace()),
|
||||
ide_tools::managed_bundle(&state.data_dir, "lombok"),
|
||||
ide_sdk::apply::maven_local_repository(&project.root, &settings),
|
||||
);
|
||||
let own = ide_sdk::lombok::agent_in_args(settings.languages.java.jdtls_vm_args.split_whitespace());
|
||||
let managed = ide_tools::managed_bundle(&state.data_dir, "lombok");
|
||||
let repo = ide_sdk::apply::maven_local_repository(&project.root, &settings);
|
||||
let root = project.root.clone();
|
||||
// Detection may read the project's sources.
|
||||
let lombok =
|
||||
tauri::async_runtime::spawn_blocking(move || ide_sdk::lombok::lombok_agent(&root, own, managed, repo))
|
||||
.await
|
||||
.map_err(|e| IdeError::internal(e.to_string()))?;
|
||||
info.hints.extend(lombok.problem());
|
||||
}
|
||||
Ok(info)
|
||||
|
||||
@@ -12,6 +12,7 @@ pub mod app;
|
||||
pub mod build;
|
||||
pub mod bytes;
|
||||
pub mod caches;
|
||||
pub mod clipboard;
|
||||
pub mod coverage;
|
||||
pub mod db;
|
||||
pub mod debug;
|
||||
@@ -84,6 +85,9 @@ pub fn builder() -> tauri_specta::Builder<tauri::Wry> {
|
||||
fs::fs_delete,
|
||||
fs::fs_copy,
|
||||
fs::fs_read_text,
|
||||
clipboard::clipboard_file_list,
|
||||
clipboard::clipboard_has_files,
|
||||
clipboard::clipboard_set_file_list,
|
||||
doc::doc_open,
|
||||
doc::doc_open_virtual,
|
||||
doc::doc_close,
|
||||
@@ -168,6 +172,7 @@ pub fn builder() -> tauri_specta::Builder<tauri::Wry> {
|
||||
vcs::vcs_blame,
|
||||
vcs::vcs_log,
|
||||
vcs::vcs_commit_files,
|
||||
vcs::vcs_commits_changes,
|
||||
vcs::vcs_branches,
|
||||
vcs::vcs_stashes,
|
||||
vcs::vcs_stage,
|
||||
@@ -263,6 +268,10 @@ pub fn builder() -> tauri_specta::Builder<tauri::Wry> {
|
||||
tools::tools_list,
|
||||
tools::tools_install,
|
||||
tools::tools_uninstall,
|
||||
tools::jdk_download_vendors,
|
||||
tools::jdk_download_releases,
|
||||
tools::jdk_download_location,
|
||||
tools::jdk_download,
|
||||
sdk::sdk_detect,
|
||||
sdk::sdk_python_effective,
|
||||
sdk::sdk_project_libraries,
|
||||
@@ -301,6 +310,7 @@ pub fn builder() -> tauri_specta::Builder<tauri::Wry> {
|
||||
debug::debug_set_data_breakpoints,
|
||||
debug::debug_data_breakpoint_info,
|
||||
debug::debug_attachable_processes,
|
||||
debug::debug_listen_for_jvm,
|
||||
debug::debug_redefine_classes,
|
||||
coverage::coverage_prepare,
|
||||
coverage::coverage_collect,
|
||||
@@ -370,6 +380,11 @@ pub fn builder() -> tauri_specta::Builder<tauri::Wry> {
|
||||
db::db_tables,
|
||||
db::db_table_details,
|
||||
db::db_ddl,
|
||||
db::db_schema_objects,
|
||||
db::db_object_ddl,
|
||||
db::db_explain,
|
||||
db::db_history,
|
||||
db::db_remove_history,
|
||||
db::db_completion,
|
||||
db::db_export_file,
|
||||
db::db_set_manual_commit,
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::state::{AppState, Project};
|
||||
use ide_core::settings::SpellingSettings;
|
||||
use ide_core::{DocId, IdeError, IdeResult, RequestId};
|
||||
use ide_language::SpellingKind;
|
||||
use ide_spell::{Region, RegionKind, Speller, Typo};
|
||||
use ide_spell::{Region, RegionKind, SpellScope, Speller, Typo};
|
||||
use std::path::{Path, PathBuf};
|
||||
use tauri::{State, Window};
|
||||
|
||||
@@ -33,13 +33,15 @@ fn speller(project: &Project) -> Speller {
|
||||
}
|
||||
|
||||
/// Misspelt words in the comments, string literals and declared names of
|
||||
/// `doc` (all of its text for prose). Cancel with `cancel_request(request)`.
|
||||
/// `doc` that `scope` includes (all of its text for prose). Cancel with
|
||||
/// `cancel_request(request)`.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn spell_check(
|
||||
window: Window,
|
||||
state: State<'_, AppState>,
|
||||
doc: DocId,
|
||||
scope: SpellScope,
|
||||
request: RequestId,
|
||||
) -> IdeResult<Vec<Typo>> {
|
||||
let project = state.project(window.label())?;
|
||||
@@ -65,6 +67,7 @@ pub async fn spell_check(
|
||||
SpellingKind::Name => RegionKind::Name,
|
||||
},
|
||||
})
|
||||
.filter(|r| scope.includes(r.kind))
|
||||
.collect()
|
||||
};
|
||||
if cancel.is_cancelled() {
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
//! Language server / debug adapter installer (`ide-tools`).
|
||||
//! Language server / debug adapter installer and JDK downloads (`ide-tools`).
|
||||
|
||||
use crate::state::AppState;
|
||||
use ide_core::{IdeError, IdeResult, RequestId};
|
||||
use ide_sdk::Sdk;
|
||||
use ide_tools::jdk::{JdkReleases, JdkVendor};
|
||||
use ide_tools::{InstallEvent, ToolInfo, Tools};
|
||||
use std::path::PathBuf;
|
||||
use tauri::ipc::Channel;
|
||||
use tauri::{AppHandle, Manager, State, Window};
|
||||
|
||||
@@ -81,6 +84,81 @@ pub async fn tools_uninstall(
|
||||
Ok(info)
|
||||
}
|
||||
|
||||
/// JDK distributions Download JDK offers.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub fn jdk_download_vendors() -> Vec<JdkVendor> {
|
||||
ide_tools::jdk::vendors()
|
||||
}
|
||||
|
||||
/// Java versions `vendor` publishes; cancellable with `cancel_request(request)`.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn jdk_download_releases(
|
||||
app: AppHandle,
|
||||
window: Window,
|
||||
state: State<'_, AppState>,
|
||||
vendor: String,
|
||||
request: RequestId,
|
||||
) -> IdeResult<JdkReleases> {
|
||||
let tools = tools(&app, &state, &window)?;
|
||||
let cancel = state.begin_request(request);
|
||||
let result =
|
||||
tauri::async_runtime::spawn_blocking(move || tools.jdk_releases(&vendor, &cancel)).await.map_err(join_err);
|
||||
state.end_request(request);
|
||||
result?
|
||||
}
|
||||
|
||||
/// Where Download JDK puts JDKs unless told otherwise.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub fn jdk_download_location(app: AppHandle) -> IdeResult<ide_core::IpcPath> {
|
||||
let data = app.path().app_data_dir().map_err(|e| IdeError::internal(e.to_string()))?;
|
||||
Ok(ide_tools::jdk::jdks_dir(&data).into())
|
||||
}
|
||||
|
||||
/// Downloads the latest `vendor` JDK of Java `version` into `location` (default:
|
||||
/// [`jdk_download_location`]), streaming progress through `on_event`; cancellable
|
||||
/// with `cancel_request(request)`. The JDK is then detected like an installed one,
|
||||
/// and language servers waiting for a JDK (jdtls) start on it.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn jdk_download(
|
||||
app: AppHandle,
|
||||
window: Window,
|
||||
state: State<'_, AppState>,
|
||||
vendor: String,
|
||||
version: u32,
|
||||
location: Option<PathBuf>,
|
||||
request: RequestId,
|
||||
on_event: Channel<InstallEvent>,
|
||||
) -> IdeResult<Sdk> {
|
||||
let tools = tools(&app, &state, &window)?;
|
||||
let location = location.filter(|p| !p.as_os_str().is_empty());
|
||||
let cancel = state.begin_request(request);
|
||||
let result = tauri::async_runtime::spawn_blocking(move || {
|
||||
let jdk = tools.download_jdk(&vendor, version, location.as_deref(), &cancel, &|e| {
|
||||
let _ = on_event.send(e);
|
||||
})?;
|
||||
let home = jdk.home.into_path_buf();
|
||||
// A chosen location is scanned from now on too (the default one is from startup).
|
||||
if let Some(location) = &location {
|
||||
ide_sdk::java::add_jdk_root(location);
|
||||
}
|
||||
ide_sdk::java::rescan_jdks();
|
||||
let jdk = ide_sdk::java::jdk_at(&home)
|
||||
.ok_or_else(|| IdeError::internal(format!("{} is not a JDK home", home.display())))?;
|
||||
IdeResult::Ok(ide_sdk::jdk_sdk(&jdk))
|
||||
})
|
||||
.await
|
||||
.map_err(join_err);
|
||||
state.end_request(request);
|
||||
let sdk = result??;
|
||||
reconfigure_servers(&state);
|
||||
Ok(sdk)
|
||||
}
|
||||
|
||||
fn reconfigure_servers(state: &AppState) {
|
||||
for project in state.projects() {
|
||||
let lsp = project.lsp.clone();
|
||||
|
||||
@@ -121,6 +121,19 @@ pub async fn vcs_commit_files(
|
||||
blocking(move || project.with_repo_at(repo.as_deref(), |r| r.commit_files(&commit))).await
|
||||
}
|
||||
|
||||
/// Combined changes of several commits, `commits` oldest first.
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn vcs_commits_changes(
|
||||
window: Window,
|
||||
state: State<'_, AppState>,
|
||||
repo: Option<PathBuf>,
|
||||
commits: Vec<String>,
|
||||
) -> IdeResult<Vec<RevisionChange>> {
|
||||
let project = state.project(window.label())?;
|
||||
blocking(move || project.with_repo_at(repo.as_deref(), |r| r.commits_changes(&commits))).await
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[specta::specta]
|
||||
pub async fn vcs_branches(window: Window, state: State<'_, AppState>, repo: Option<PathBuf>) -> IdeResult<Vec<Branch>> {
|
||||
|
||||
@@ -41,6 +41,7 @@ pub fn run() {
|
||||
if let Some(data) = dirs::data_dir() {
|
||||
ide_tools::extend_process_path(&data.join(&context.config().identifier));
|
||||
ide_dap::adapter::set_tools_root(ide_tools::tools_dir(&data.join(&context.config().identifier)));
|
||||
ide_sdk::java::add_jdk_root(&ide_tools::jdk::jdks_dir(&data.join(&context.config().identifier)));
|
||||
}
|
||||
if let Some(refresh) = refresh_shell_path {
|
||||
refresh();
|
||||
|
||||
@@ -630,6 +630,8 @@ pub fn spec(os: Os) -> Vec<Entry> {
|
||||
act("db.showToolWindow", "Database"),
|
||||
act("db.newDataSource", "New Data Source…"),
|
||||
act("db.openConsole", "Open Query Console"),
|
||||
SEP,
|
||||
act("vim.toggle", "Vim"),
|
||||
],
|
||||
));
|
||||
|
||||
@@ -1432,6 +1434,7 @@ mod tests {
|
||||
"code.quickDefinition",
|
||||
"code.quickTypeDefinition",
|
||||
"localHistory.recentChanges",
|
||||
"vim.toggle",
|
||||
"editor.resetFontSize",
|
||||
"diff.compareWithClipboard",
|
||||
"view.quickSwitchScheme",
|
||||
|
||||
@@ -399,7 +399,11 @@ impl AppState {
|
||||
mark("recent");
|
||||
tracing::info!("opened {} in window {window} in {} ms {laps:?}", root.display(), started.elapsed().as_millis());
|
||||
|
||||
let db = Arc::new(ide_db::DbManager::new(root.clone(), self.data_dir.join("db-passwords.json")));
|
||||
let db = Arc::new(ide_db::DbManager::new(
|
||||
root.clone(),
|
||||
self.data_dir.join("db-passwords.json"),
|
||||
self.data_dir.join("db-history.json"),
|
||||
));
|
||||
let http_history = Arc::new(ide_http::History::new(self.data_dir.join("http-history").join(&project_key)));
|
||||
let agents = Arc::new(ide_agent::AgentManager::new(
|
||||
ide_agent::AgentPaths {
|
||||
|
||||
@@ -240,6 +240,7 @@ fn build_project_window(app: &AppHandle, mut config: WindowConfig) -> IdeResult<
|
||||
let window = WebviewWindowBuilder::from_config(app, &config)
|
||||
.and_then(|b| b.build())
|
||||
.map_err(|e| IdeError::internal(format!("cannot create window: {e}")))?;
|
||||
apply_zoom(app, &window);
|
||||
// "Sync with OS" needs the OS mode, known once the window exists.
|
||||
if let Some(a) = appearance.filter(|a| a.sync_with_os || a.theme == "system")
|
||||
&& window.theme().is_ok_and(|t| t == Theme::Dark)
|
||||
@@ -249,6 +250,17 @@ fn build_project_window(app: &AppHandle, mut config: WindowConfig) -> IdeResult<
|
||||
Ok(window)
|
||||
}
|
||||
|
||||
/// Sets the saved IDE zoom before the page loads; applied later by the frontend,
|
||||
/// it would visibly scale up the splash.
|
||||
fn apply_zoom(app: &AppHandle, window: &WebviewWindow) {
|
||||
let Some(zoom) = app.try_state::<AppState>().map(|s| s.global_settings.effective().appearance.zoom) else {
|
||||
return;
|
||||
};
|
||||
if zoom != 100 {
|
||||
let _ = window.set_zoom(f64::from(zoom) / 100.0);
|
||||
}
|
||||
}
|
||||
|
||||
/// The first window (`create: false` in tauri.conf.json so it can take the theme's colour).
|
||||
pub fn create_main_window(app: &AppHandle) -> IdeResult<WebviewWindow> {
|
||||
build_project_window(app, window_config(app)?)
|
||||
@@ -414,6 +426,7 @@ pub fn open_settings_window(
|
||||
.and_then(|b| b.parent(&parent))
|
||||
.and_then(|b| b.build())
|
||||
.map_err(|e| IdeError::internal(format!("cannot create settings window: {e}")))?;
|
||||
apply_zoom(app, &window);
|
||||
// window-state restores the remembered size; the position always follows the parent.
|
||||
if let (Ok(pos), Ok(size), Ok(own), Ok(scale)) =
|
||||
(parent.outer_position(), parent.outer_size(), window.outer_size(), parent.scale_factor())
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "OpenIDEA",
|
||||
"version": "0.1.14",
|
||||
"version": "0.1.15",
|
||||
"identifier": "ai.l0ng.openidea",
|
||||
"build": {
|
||||
"beforeDevCommand": "pnpm dev",
|
||||
|
||||
Reference in new issue
Заблокувати користувача