From afcb970fabf2c751f1c4a6b0246bb07371126946 Mon Sep 17 00:00:00 2001 From: Aaron Olin Date: Fri, 13 Mar 2026 08:54:44 -0500 Subject: [PATCH 01/17] fix: On a large monitor, when commands run in CodexMontior their output is vertically cut off at half of the screen. This fixes it so that it spans the whole content area to see the full output --- src/styles/messages.css | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/styles/messages.css b/src/styles/messages.css index 2149f5581..fb14d4bc6 100644 --- a/src/styles/messages.css +++ b/src/styles/messages.css @@ -592,15 +592,17 @@ border-radius: 6px; border: 1px solid var(--border-subtle); display: inline-flex; + flex: 1 1 auto; min-width: 0; - max-width: 420px; + max-width: 100%; } .tool-inline-command-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - display: inline-block; + display: block; + flex: 1 1 auto; min-width: 0; } @@ -1182,7 +1184,7 @@ } .message.assistant .markdown :where(p, ul, ol, blockquote) { - max-width: 94ch; + max-width: none; } .markdown :where(strong) { From 386109317293ed209055f2e8a76e58678ef6ddbc Mon Sep 17 00:00:00 2001 From: Aaron Olin Date: Tue, 17 Mar 2026 02:06:42 -0500 Subject: [PATCH 02/17] feat: Added PHPStorm IDE as an IDE option --- src-tauri/src/shared/settings_core.rs | 56 ++++++++++++- src-tauri/src/shared/workspaces_core.rs | 2 +- src-tauri/src/shared/workspaces_core/io.rs | 89 ++++++++++++++++++++- src/assets/app-icons/phpstorm.png | Bin 0 -> 33833 bytes src/features/app/utils/openAppIcons.ts | 3 + 5 files changed, 147 insertions(+), 3 deletions(-) create mode 100644 src/assets/app-icons/phpstorm.png diff --git a/src-tauri/src/shared/settings_core.rs b/src-tauri/src/shared/settings_core.rs index 211fcf666..ccfb14a1e 100644 --- a/src-tauri/src/shared/settings_core.rs +++ b/src-tauri/src/shared/settings_core.rs @@ -3,9 +3,13 @@ use std::path::PathBuf; use tokio::sync::Mutex; use crate::codex::config as codex_config; +use crate::types::OpenAppTarget; +use crate::shared::workspaces_core::optional_open_app_targets_core; use crate::storage::write_settings; use crate::types::AppSettings; +const OPTIONAL_OPEN_APP_TARGET_IDS: &[&str] = &["phpstorm"]; + fn normalize_personality(value: &str) -> Option<&'static str> { match value.trim() { "friendly" => Some("friendly"), @@ -36,6 +40,7 @@ pub(crate) async fn get_app_settings_core(app_settings: &Mutex) -> .unwrap_or("friendly") .to_string(); } + inject_optional_open_app_targets(&mut settings); settings } @@ -52,7 +57,9 @@ pub(crate) async fn update_app_settings_core( write_settings(settings_path, &settings)?; let mut current = app_settings.lock().await; *current = settings.clone(); - Ok(settings) + let mut response = settings; + inject_optional_open_app_targets(&mut response); + Ok(response) } pub(crate) fn get_codex_config_path_core() -> Result { @@ -64,3 +71,50 @@ pub(crate) fn get_codex_config_path_core() -> Result { .ok_or_else(|| "Unable to resolve CODEX_HOME".to_string()) }) } + +fn inject_optional_open_app_targets(settings: &mut AppSettings) { + let optional_targets = optional_open_app_targets_core(); + let mut available_optional_targets_by_id = optional_targets + .into_iter() + .map(|target| (target.id.clone(), target)) + .collect::>(); + let mut existing_targets = std::mem::take(&mut settings.open_app_targets); + existing_targets.retain(|target| { + !OPTIONAL_OPEN_APP_TARGET_IDS.contains(&target.id.as_str()) + || available_optional_targets_by_id.contains_key(&target.id) + }); + + let mut merged_targets = + Vec::with_capacity(existing_targets.len() + available_optional_targets_by_id.len()); + let mut inserted_optional = false; + + for target in existing_targets { + if OPTIONAL_OPEN_APP_TARGET_IDS.contains(&target.id.as_str()) { + available_optional_targets_by_id.remove(&target.id); + } + if !inserted_optional && target.kind == "finder" { + merged_targets.extend(available_optional_targets_by_id.into_values()); + available_optional_targets_by_id = std::collections::HashMap::new(); + inserted_optional = true; + } + merged_targets.push(target); + } + + if !inserted_optional { + merged_targets.extend(available_optional_targets_by_id.into_values()); + } + + settings.open_app_targets = merged_targets; + + if !settings + .open_app_targets + .iter() + .any(|target| target.id == settings.selected_open_app_id) + { + settings.selected_open_app_id = settings + .open_app_targets + .first() + .map(|target| target.id.clone()) + .unwrap_or_default(); + } +} diff --git a/src-tauri/src/shared/workspaces_core.rs b/src-tauri/src/shared/workspaces_core.rs index 90eb5f911..451d515e0 100644 --- a/src-tauri/src/shared/workspaces_core.rs +++ b/src-tauri/src/shared/workspaces_core.rs @@ -15,7 +15,7 @@ pub(crate) use git_orchestration::{apply_worktree_changes_core, run_git_command_ pub(crate) use helpers::{is_workspace_path_dir_core, list_workspaces_core}; pub(crate) use io::{ get_open_app_icon_core, list_workspace_files_core, open_workspace_in_core, - read_workspace_file_core, + optional_open_app_targets_core, read_workspace_file_core, }; pub(crate) use runtime_codex_args::{ set_workspace_runtime_codex_args_core, WorkspaceRuntimeCodexArgsResult, diff --git a/src-tauri/src/shared/workspaces_core/io.rs b/src-tauri/src/shared/workspaces_core/io.rs index 68873e1fa..5b0151db8 100644 --- a/src-tauri/src/shared/workspaces_core/io.rs +++ b/src-tauri/src/shared/workspaces_core/io.rs @@ -7,7 +7,7 @@ use tokio::sync::Mutex; use crate::shared::process_core::tokio_command; #[cfg(target_os = "windows")] use crate::shared::process_core::{build_cmd_c_command, resolve_windows_executable}; -use crate::types::WorkspaceEntry; +use crate::types::{OpenAppTarget, WorkspaceEntry}; use crate::utils::normalize_windows_namespace_path; use super::helpers::resolve_workspace_root; @@ -15,6 +15,7 @@ use super::helpers::resolve_workspace_root; #[derive(Clone, Copy, Debug, Eq, PartialEq)] enum LineAwareLaunchStrategy { GotoFlag, + JetBrainsLineColumnFlags, PathWithLineColumn, } @@ -53,6 +54,9 @@ fn command_launch_strategy(command: &str) -> Option { { return Some(LineAwareLaunchStrategy::GotoFlag); } + if identifier == "phpstorm" || identifier == "phpstorm64" { + return Some(LineAwareLaunchStrategy::JetBrainsLineColumnFlags); + } if identifier == "zed" || identifier == "zed-preview" { return Some(LineAwareLaunchStrategy::PathWithLineColumn); } @@ -125,6 +129,54 @@ fn find_executable_in_path(program: &str) -> Option { None } +#[cfg(target_os = "windows")] +fn first_available_command(candidates: &[&str]) -> Option { + candidates + .iter() + .copied() + .find(|candidate| resolve_windows_executable(candidate, None).is_some()) + .map(str::to_string) +} + +#[cfg(not(target_os = "windows"))] +fn first_available_command(candidates: &[&str]) -> Option { + candidates + .iter() + .copied() + .find(|candidate| find_executable_in_path(candidate).is_some()) + .map(str::to_string) +} + +#[cfg(target_os = "windows")] +fn phpstorm_command_candidates() -> &'static [&'static str] { + &["phpstorm64.exe", "phpstorm.bat", "phpstorm"] +} + +#[cfg(target_os = "macos")] +fn phpstorm_command_candidates() -> &'static [&'static str] { + &["phpstorm"] +} + +#[cfg(all(not(target_os = "windows"), not(target_os = "macos")))] +fn phpstorm_command_candidates() -> &'static [&'static str] { + &["phpstorm", "phpstorm.sh"] +} + +pub(crate) fn optional_open_app_targets_core() -> Vec { + let Some(command) = first_available_command(phpstorm_command_candidates()) else { + return Vec::new(); + }; + + vec![OpenAppTarget { + id: "phpstorm".to_string(), + label: "PHPStorm".to_string(), + kind: "command".to_string(), + app_name: None, + command: Some(command), + args: Vec::new(), + }] +} + fn build_launch_args( path: &str, args: &[String], @@ -141,6 +193,15 @@ fn build_launch_args( launch_args.push("--goto".to_string()); launch_args.push(located_path); } + Some(LineAwareLaunchStrategy::JetBrainsLineColumnFlags) => { + launch_args.push("--line".to_string()); + launch_args.push(line.to_string()); + if let Some(column) = column { + launch_args.push("--column".to_string()); + launch_args.push(column.to_string()); + } + launch_args.push(path.to_string()); + } Some(LineAwareLaunchStrategy::PathWithLineColumn) => { let sanitized_path = normalize_windows_namespace_path(path); let located_path = format_path_with_location(&sanitized_path, line, column); @@ -323,6 +384,10 @@ mod tests { command_launch_strategy("zed"), Some(LineAwareLaunchStrategy::PathWithLineColumn) ); + assert_eq!( + command_launch_strategy("phpstorm64.exe"), + Some(LineAwareLaunchStrategy::JetBrainsLineColumnFlags) + ); assert_eq!(command_launch_strategy("vim"), None); } @@ -490,6 +555,28 @@ mod tests { assert_eq!(args, vec!["/tmp/project/src/App.tsx:33".to_string()]); } + #[test] + fn builds_line_and_column_flags_for_phpstorm_targets() { + let args = build_launch_args( + "/tmp/project/src/App.tsx", + &[], + Some(33), + Some(7), + Some(LineAwareLaunchStrategy::JetBrainsLineColumnFlags), + ); + + assert_eq!( + args, + vec![ + "--line".to_string(), + "33".to_string(), + "--column".to_string(), + "7".to_string(), + "/tmp/project/src/App.tsx".to_string(), + ] + ); + } + #[test] fn falls_back_to_plain_path_for_unknown_targets() { let args = build_launch_args( diff --git a/src/assets/app-icons/phpstorm.png b/src/assets/app-icons/phpstorm.png new file mode 100644 index 0000000000000000000000000000000000000000..909d26f9a8fe6a2fc488e0f34988019525de58e4 GIT binary patch literal 33833 zcmdRW1ydYdu=Xx2u7Thl9D=(;aM$3j!QE|v;2PW|SkU0Ef#9yeWpQ_1;N$(OZr$H- zYwAq*oO5cXdgipu(>)WZq9l!mOo$8s0MKM*B-8)^;5!QhAi}>BeV1RB?*wEerYHsg z)W)MgP2k>-$<1Wc6afGq8UO(N9RPTG?*bnH0Pd^+z>zTkz@H8P;5lWtsS3UiNLXmg zS}H067~XkA05IGd0D9;Cy9VKV0{~$2fdJTd3jD7xALf5Xfq;D2|Kl?~TZwN; z-E-$yOiaZ8Z)xYhs0hMqK?7L$^2VQo{%)9`_y_CsbYPcsb;fl_st6dJ z6Wx-;cpi@Mmz)`+u@3gg+j^sK!p&cu)UR5)*Je=N^cUziWC@};bDRchdS&zR`k1yq zC&!O&-)!o3!0?eA$sNC3r&ExhKJu=Wq2l*mMq4ux=V>N`Od+Sq@S5K}gQw{Sue~JQ z-7sS#aeOhMnlKpW>cmNg2l;ElL!^T@7WDgcSK#UnVIAd;v6^NUpPI7yU#W&~GxT`2 z)%R9jTvNVf#RR(B5UE_w+ETI0D=&Q#rKK<7kiTq)U&-|GD>^CH<9TULrH*UU21i&g zv&Z9&-sW!Omz@0UuLtaHn;dj_*!=t5tW8HHTAQ4Ivezm_l*m_^D^k-dC(Bhu+Nhsq3!eTB zekp-rFH5#?T_N*YZKd+#c$5=qj_t%rUd5!KPrmzRf%LxTsh{~-fwafiYU6Rabl2TM z;3<^K&_*e)MIa&u-W71~#+LetiPEsK_r`3VTjU@xz>=GU6T;J88^kcI(0R76?04CO zQaUO8`n^#)SJ)s+*!`!5Kx?W-Tt$klo+RpQ3RMO}$_RFqB{oE9g!;n=ah5i6WZK$v z|4&bQN93X26b$SjnEM8bL39{q)(NL%3FRIaCyL1<)?8~t!k>Pyt`NeDP9OVdzQmZg z@@%c|_&v@qQV?F;c4M*0P)&{{+JV5yF*YO;(j$I5gKXPxtplDwX zlXZ3w+0ILXIVM%vSKN+}ipr(Ko&#-ph>#S^8jw{R`|Wr~i{xbt4KK&@WVs4YS!=vYtYd27xf3nCPD?~;7`lZFcnxF>sW$SwHlSiKjR6vU4Y-4v<-r^ zdMN9J2c4Lbo46ChD`=^Bl!fRs=oRJla=KG6ASZD$&opT2*J-G1tWeJ-WS!EKezxTP z;n7x&YUKP+7IiMhfIF_l*6^d-$ETvUpK zya~yJiEhI`^hv}d>-jUwt8VVF+6%Bp(`|BrWv{DjB0eW5@f|m^gOw{!F^#)rgVDWt z8nSXp`0kymX-N6Z$5JsJ6xf|(n8e$N5`jA`V`uEu=JPUhw$FFe!dzD)Vdwh<4GR2meaDUGZk4TYv`g>qLEUf)@r${x=Q^v#ac z+35$$!{=wLY6)<)njFoTHwjX^V~jo+WUgEK$gQyR%1##7QaOQ4A81f0ekG(9syU=E z?7EcMC>7`fYjyH$sjfXqzL7p0!6KYJp6q+Rj=ps~r+Th4rHqdT@g5qVplTLVIz7r5 zY5L_?1+W4f9`FOzn=(sY4^BJ<_kz*(JHEB?zl=q?+-f*K%s={c&VQR2bvHGz>&)dO zssvg5#QS?M9xG)fuI^^(jjd49Z*x*|t>)YlEwP8@DqbMt4I<=`GDYvXRLD$`5Blh{ z{(z5dn^e?D48&h`;?>I-NObj!uKM@L&t$PTRL4)Uv323v)}UZA6#+@US!;AnJYEpy z%3!Jb;bGx|ebJZ|>Du9{q4_Ziblr94GGMHlX#99zov z?9Tr>K44G4x9c(T?R+`!DN~>3BqWyj3~`k}rGP9^e~BW(T#{#Img!o z-C91Bs?iI+kaNzi%;9-pBFGcuoH4yzGyknyeBT+H z7r}r+CUX>~$^9=yG7U&OMLS!}y-tmuEAi8Zh&)Nad6EA6LeBm}SL)@zWuSBgM>Nh{ z-0xep)Gc`>@nw%wqI|(Per~2~?w!MIT6+$|0zr2_C24&*2c2teK2nxWi_y7}7}MQs zdy)i4y|R?ph-Qsv#?mp0bdH2I<7_bT?u>+_M+76zbnnwE?5(tj>sq1F8)VV{8KIj_ zu!S=}v5gqDi>&7=1;x1dyH|{sTvi831DZdzWyWOCWwT-6jl7cAI1)?1Y3eYOh?;#$ z^90x4(e*7~t+&qED3%c#8APiXXQ@=`ST44kR*?Yb--G!dC&B2I&9W_7TPj}vOwHV# z?Htc7i+M!BIi@n=s#MQkdXEIo=y?n-_RhpinCpMKdy*0zl)&t#6}$??^Bypbn~cEJ zhjBH&R_}4@Vf&6^0jtP;Jtq$*>$kjI^?BBHTS0gwTfnO^jajMS^?+z|2t|pMxf!T#HrT=|RSn{-jjvWE zPAB+_O-7TEBp5rNl!v!UeA$t8!g=|E|JK);*0^qXo^o|`o0zaz*9DSEOY5nas~Bpn z$LWfgV;XKe(UT4Eg>#WLj?33AS4d?@S=#He?QY-C-0+!ioP75D0IUEKg9TuW(8zNX zREJ1*z^YY6#!cp7@p0^s#hqXJe*LXPA|C6$8*Wce>H(cI<#u`YV-qClS=Qr=`yrQ; zkqs7>SZ1Q{8rP>3PZ+od9HR@_3X#S%7Gx-bg_8`!oCvdu3;n{AfExXAK3tn`Qwc`7 zSTzP9E1xY&*P&@z(fi;XIUUZ>P+@37+IL~R@3ddpXfq(+GYs?ddJ}&PdcE5?jAomc zy2|05+VuIX8$!m;MsnYsdi$}JYxjHZ@C%g#`G`=F1Sw$|NxS{H^JKtulU8->ZI5Sl zr|;g~!@^b+&aj*d;2UxH2ZmW?9n|fZ==ue}V?%X*G zDQ-UzI%5GUq%0rf|B~5qPHSM3Pc1lGR~&`OkN{b1``^17eZHlWyw`gaM^T_ zB8`M*UP_U>ErAT~FEO@{9~aY#tA@U@e;epMIu0%6!jMb*WF7Mc7QFF$ncE$PZJhm1 zg1u_;5tAXXmNMb&_&eoX_8){WBMsu((~eT3Lwa2VGptHPH#2X9I%>0sQ8{SyqSh(bm2qccn6fo z9TdZ;Rj$pg^>Rr4p6r=8qCQO$ll|6uFo8ag5G5Ti3LB8#@X20P+F{Isr!<^CDHC80 zNSMhwxe~A`e_hCNy}L1+SW#Pfie)){8tV%VR>BrFQZ=FG1>D5-#pdY*21DDG{6{54 zoaS5u9PKSmlguabva-^2MDV*#E|1;2mUC@7C+`Ln4yb!&bZU4u6ICC zyoBJ|{Wtdt!nd=d+VJMf(nJa4Ef&KM5ii#))Z>v?neQhTx!*obDm8j;&BBE zID+I=xLqB~9=q<+KM)T~*Li+ok^f|~v==m^-E1_0wlfS4alP#hua7jar>G zpcxQhRK7lJ>>7L0|5}kVj9V39;PO2m+WRNl8FkW_UgHqPdvjBX{6gbru!)H%mMI-3 z9h$+!vZ!PH6-#Bt*I3iRy z8#4segxDS@mPZp`b`=vv#;PAbX#56lUEyiMCWN-D#M)SF|1w+1_PVUKReE@OTX{Uc zm;dlo&HO2f+S}<*uLt0A?Dyy^Pu|UPefQhJK6FFZdRs8#)+*!HyGEK@=O=Zb<*y&~ z`IBYvp21c%|H#2d{|@mp>7R{gPrA9Ucp#e*rN z(fN4FcOwgxviVS9GrVIYOh!fvcsDZQ_87uI7#!%Ld{@-ILncj?V%2s_9bV z8VgiGM5j~Zj_TA(ODPM;g5t7M|45+kTfA`P|7jSLgHyLo=Ki=R--Fn3&|bn313 z_GVmC6;LO=@~#tH6yf3yz?s>mB~x_!7$jGjX#>gRq!~@3bujWhv*x(%H0m(fSiDCZ z#MuI&hrx=@@?wIC0#CPN_em~MXuB^BX6J7waSQWuQo?=Aw^%UlBFH799H2B)Jzrvm z6y?0=XFp=atZgNKDW+go2=R$T(~q&G4&J5vkQZTw><&$q*3kMT*Z`Y5lhuM6EY&xDqb2= zfHG{!p}R8hq^br{n@+LCJVE5jw0zH#4|KKHZf8msSZaiD-p@klT(-2?3wj|EqgUn| zp~ECec#+VZoR*Z@FOBfkHLkR_r-0dM^_Sbb(GUt-M)iIDhRb1uSMR~_YII{6vn$@y zWbtT>Fj(Wg_}Fe}SN+@MeQx7fO!IJb8<#WSYTJP|;6rFfy$M4W;fG~=x!5Ak;-6Ad z<&UTP8iypb_>v>oEv}8p?9(n@1I&`&8Z&#u4`E_UAl&%H1y&EnR*i74Qn<^cCWxz1 zS61WBiG6&&m8-D9QuI=rStGE>x!ILeo=W=Jt3u9Q1QCA zDlVCod@s4JMqRsfZ`!FcjJa~{pwETqrysz*SNk88M>{`<;u7OHrNtEm+i=Ta51-cU zbpIYG?1WNGJBz}sQB5PQf54R>wT+YMqRu1G?)JV)Pa@0Mczo2mH|jg?2W@;j=^tSY za~gP#VMym4gVf$E?!L*#Jr$7U(JAZmZX_c>Z)Ww`DI*ax53}&4_SBG$f%f$%3siU) z5udoiCN800zC@y2pi0SQd_%1ggwXej6o~O(d~CVq#<$|3h!7kHvNO0wU zVWTqSGzAZ#m43BBuBi_15GQXF*3tF69F6mzjYs15=rP1cfXzZnH4h_^YJ|yGcZ=)e zd1W}~y{%Ffwt6}-N?ZyER(2b1esH_nw@?SAjce)X7WdiZPOOHd^p)}ssa}TS!T*?% z%Qd$6Ah??`EkF};BjqMKiFJ%A;J{YSHZ$9GkwNb4CHR5&aou7Wj#{0YT(K~*aO;h$b zM8WwPWhYWr>87|-pDo*O#cP%~UPRCp8$7mv%G7Kp5TSIQCZ`;vgXm0cPX4^WbVO&i-EH{ zvxzxZ`rTvlZf7;9G_GX76+q~dM zNiR>d>~-s{*`Ot?Noe7cab3B}_wMIa1gD#jA*tn@X_i{V6HYZ_38V!qw$^r+Bz9#SB9a z!jnPX`I$H7pSW~-1kPg{ltX_&3+^|0>AWoE=I9DzIq1aslEgIQdXGn=S@4)PE^re2 zJL2}7@E2jTU-}4vgpIJ9iG^*>E|4eJl8Hd;83yIiH2w^z{h?7^aW@KkwBqAD;48eP z0%FFd)XqZ>JMAVrC*j}tY{Uk7vQVO~YmG@o68hLpl&I<2^rj}A){iQma z**Hiy7oB^K&Hyxe&y@X!hhi2Q?jm&=c`5VI?^scAkdDcPZBVwBO~W^8Jev^dz|_=P`@Db zSZtfP~EobPdPT zT(w|9w_rjaO&C~XM1A!js~D3MP=w|YmuM;R{16jPIudbiK~Sb?vNtfMXUV#^wgY#V zzVNFZUn4}`9(d-CFH)=mtz11)`&)!;A3mSb*n6}&yB9x_<=3n@bpPaCZFHxL9==(} zcF42}QR1DdU?`F^W@mvZm@lLZ=rUBnC)*`4CB?}tw||)7YJW@W1bo@sXA)`PkdIRZ zdBV)`IQO;<3L6C^nz$MSbjhL_711(_nwt$2U8Sna7wuq1D^7Wo60$LrOu=(}5WhQL z&?)1?y+HCqP;+25f9Od@X(Y1e`v&6#L8$^V?e154s3X#Cf8^XWZ_qK^92xkwU5^_f z&S|LRC}iA;nVGhb!k<6IQ^q+r>I zWZ`EI1l6VaTbMZs@LQ?wIrP3jY(3`HinZTJTogQ6*dah6-2QH{xSO-v z?{RN5enor9t?Z_J0^wuJ-r&tw8s^;mJ$%^{(19>~C`>yxYdR65opG4gnjeSXW-T_k z@>DO{10OcWw3%)gH7|(6U%uTn(xd=xS(6%5BV^Ya2Y1BOr;4)JnHIn~mpTJTifN8d zwxMDw*{CwN+MU}uJCdy1d^ar7Bac#a$>HXR!RU23V&r7#Nr}Tc^@m8SN?~@8+@IE; zc5#m)<*QQhx496s^MR9Y={cmps5*yoQuiflJ)?(xEg`qRYicnDbqceOs-|CP?=l~DITdrf2>$0N2>H`GbPc3prJehE_x1s zI(FK8uP^_WkPu1pj*!DXRdO2Rr=pVf|7G%8hOg4$Ln%S9MQBadJ!GGE=2hnQ&TN{& zw&ju?^qff5^4qoQwlw4X+ZK~{*}y*UT?YAaL@@lq^%$f?dTP=2qg>N=h)SHy087;o z8F7i#D`+OtSP^+hUVaDEU+YKpX+pPJBkm&(VKX9TBE8^GS(9K&vr!Rdoo$y+O+()i z3nIfyFK58L3|rG527pamx<*|HN}n5G-76*!2QZikS;&6SVEgts{BNQ1hGDGw7e61s zIS_4q94TF6H5Zx=;&0_{W4|{rr|@f0S(o_!!QXZL0`V3i~@o|dJv;JQ`@K7QN6zmk8#kT zQB_nvZR6w+Nl;Ixf{NtVQQJ5J&k1!9^ibt{r?G(ZBDKIjRs{r6`41ri;46#SG!h{L zgL`~uKfzl{i}a9a(x3<#aQPg^Wj`a(tQ=G9E3#-}Q}!Ud`fxe6aTr1fxT>~qLdU~Q zk-kW2F0@Ws&o}8g2|@0Do{Uat)vu_5XDM&2aVH7V#-NUCPs>k;FzQRz0s6+H&Z)mI zJ~6vYXZ4|v5?}V1tSVauo%6SxuB%QS%C8D$y=3!JbY66bcG62++04LB#1&E%WyOl# z3IlEPLK=@elG-*pV!U3)qNE(J+T4V<`JYJ}xlwkNFpSS6H4(~q`jR-3(l0HDsYVSp zmA~!V4xEV$op!XyUeek_$4CIbh;z#0TUeO*J|k?#(s&@Kw3%NgP2|!kk`Dg92BN;_ zl`_ynivDgPrA&4$oExt?sq?GbDGbY@p7EI;B;eC;Z1(NL7E0s_sr1pZ+%5mDa0jJ8-A+&K)49TTN2H)PP*D-`o znZ5^WCj(ptDV^X`?#WA{^Z+E1DB-8ViV6mNlU|_3RwuHR&JM@)+uI*O{XA(!lfwsW z%Q3)eDML{>WJmrh9+T)q3sA_Sz)@jg1U_0Aa|L+QGA^yHsz2Q?O{R1U2D(3rvQvt{ z>-<>53n0T~=R+!Kithk<5t9aW{Z7o43JLZwM@wsf&Z5zvXnLn;e#L!r9lpJtOm$p) zp1+bzVzD#izJlZC=U$goF>gjjBFeYgjF+TpMWtj)+yeT#6&+Mtl4BQ1(7Qk*aQp9MdeGGl{N8_O{Sdr_|qP zt-*`m*8+ZOe$=b!fU*(;Vq-Bnv=47?_eu4b6dis%@+d%U{siNjW&`|xsPBO)H*`M@ zk0F8a>EOEZu@hW!J8`>Kq1feeE@qI08Kd(z?cLV}*y2p+L=GV2SDD1?uq|TP@5jxL z9S1d$>L$efr1!J%^Pg-h@C9HoB(xAvKbMoYY1-6%N{GyVZM+)1>u9_QISy=eRy#HI zw(yi9_=S$|GZ-My(3g_imoRkl|W2k6Y7MCepu0C~uQ4Xau6ej+T@H8RkOxJm;lUxd!c;|r_=2i+8T z@n;k&jhzrW3~ooJm%d!1st&t8NA9?aTt>zqooRw;ASDNP@bR475R!o+$7y6!9k*YD zeuz-Ur{;kjGm7K541TkA(MNM_KP2OHSm9Cz5#z3#(b&9}HfPi8;krgmi$?TzA5-u8cC+P*WF#{s|K>`X;Q`B9{w?Ox*GWq!mnm@uSrb<(aF8g&`W{>;h~ z&|IieZ-zk=_Lv{Sll+fJgiXi5w`1&y0TvGn2QLTwC!q$KwHcd(#Nq*CtR{u1)RXv0 zKLooTGRXm0eSEnY3)FBZa~)?vA+y$z4Y*jVeoT=p=dGd0mx0vOQRQRAM-wuA#M4`~ zCbs`(;U_!PE4LZ<#8o_b@+>OpfKgssGQxmrM#*h3X5;LywDl{N*qhcw%7_!1u4ylJ zP*OfSh=kCQu%f(~KF{}Su7(BJur;VeBEY3%=-Yt$z4{NJt%j3{HdPP5GOMlx?x2F3 z5g0}OmrIn6qMZsn(@My~{o2y1mb&!1{OFj{#qRmFl>zsBJ#WPywM(*pV9u-pn5>ck zJ8&tMBr0O|gXz&8by%RY#-P$_yYewR7ppT|=5$Xo#7YQZ#l=!!$wH@bGM1Fea79%YCzD z;w15dzPFU=SAfdRP>d0|bp#Fq`D5R%OsEqg6~3*=L0qX2{J?Im;Eh;8(WndArs!3% zKiLNZ6YSitEuk99)LDmx=HEY(%u#Osyc3>9cuP#Bv+s#_U-34jY`ogpJ3VI3<~}Pa zQ{~^~is;YR0?agWCM=jicAC&|mR4&Xvk5Ye$%J1m4#rlCFirnusYZ++qMD|!NXkv& z2S}kfH(})mw4Wk~oE-<+o(;?}-RfD~r2)|v?@K_Ny5#2+N&+WNkw?~Y z!bD4we$^vV(T^f_745KU0yw`1IDdmQE0K1};%Z-k$UBoPTDt(mmQFQr^ST*(cCdVa^WR zXSKLehQkK`SS316LVZK!0bk0Q4$_Y@`p8EteHVqOk=@ECe;KF<4LaxMBJ_DjW9z!^ z>>y=*~w;d<9=wmP+!=<35?)oPpGA6ams0ko+2a%$Xh> zu7NbEVPs>Mat|tW@aDXa-4rnAlC=+UU3>e=-2G+1vg|Y23cZ;n%(jzwa|hC}r4y6F z=r~dl>$lyF>MGM^JC#l9@N{pk*7g8pYpr8dv6JCQ%kbnKb9TOC8$V)sl_a{SplSFAThUNz2`AZ92K5wcW}u3$x=XYe z$v6A~WO9dm?f^ya(*{Jm!zE-x)|m_;MDV<5(-+oDmHxU2f8bzvYsnHGE9Z}d-|+wR zYNTU2PO2Z<<*u*n;k;4Ua0<{lV~|Zw5RqZ{BuP8}ZD+4G z{lr*Kb$Wr7QqM@$eJwVC=WkTDDE!~ae?|tvFqg*H6b!S3kve!Wea6&aNOFAp(U#Jj znN6KMOH#XV45b-nAN)$@-`44-7Jp|#n|ab+?;_{4ZwW6B!PcKGu#g$%#(R821=>Fv zrH){;TZd)(;fRid9HVwqy-6EjBeKNB`53O|Zj=4)WWEnd|LD_a*0lG%-NPcc^Mv8X zUpC{b#ZnP07~=!Njx6PCRzcTePd3l2g&9`h;jK1$!V-ANqU2P5FYe++*(31A^ZnCz z=62~LkBhaaX=IKks{100@877aSW>d%|E!pmNEyfbDI+&miQ8c{y;7%MDweq8<@ui0 z_wGdY?sO$h@adp%_-w5mhnt6|6s;gH;PY>bPIXEnGFLL#t7@GN^seUHn$0nJk@8AL z#j2kSY9R)oy}vmZ^wC>3urQE${0r^3N18NX6?x1rFgSbkpR4-9`|OPN40l`2{CCpHgEja` zq~=}DO|-eN(Q2Dm5J|j%^58gStW&#D98$XFMPa9VL# zf#fiw(WBf~-Se>;Gh&ReKee9c>pNTFWJ+fp*AqKoL=H05op-ui$>F3vGHA$@ zCJV%pqNEL{|IFeQncGk5c6__`eG^?Lg|w{vmPHAq?PR9BL?@Dxl6)$Sx`p}9S|D@I zDsis9J3piovmCql9!Ipg5}}iCP-PcLEKfP4F#b#?dvVHa8ja?|7fPst|B&1p=$oW2 z>72~$Wto-N=I(<|OseK8P=afq5tT;hJw$ugo7@&At?`>bb|gg+Vpo`{s}B+6Cg^xA zd_W$34jZeZ3ZN%pg!t@X7a%}DaD<*(_Y7-+_?9p~iP8z~jyC~S=*wY-k`E|NWW`)!VJnTZ$iqXk*IamX!o})k{F)KBqd20qwz{VhOm6 zPC8v_u7H?WPj0d6E3mQ|epWZ{c0bmvg3v2_%UxwIcF;U7G7KN7&?(9DFP1J~)CQIn`KzXS{Io_h{OzcuboIdO5Rs+*k~TxB)n4 zh>p-^J9xpF(H|(?*-VedX3jw5E*y)zPS^6mn}o^DEvDs?QOl+2 z>zE9FjQYVKEgiE3=$!bc!g`~wEX2hJiQ$;T{N1{jxpV${cAhxtx5sgW=tP)9yriZl zvv%>ke*Ut)QyOqo>NZen(8C55gWEs!|9ffRb6AWt0ha%+?6-|QjH9JrFyZ)bLdjiY zFnc|cM)r+widZD-1>O+Vo^^VbTbrEWk9wyP!#^4w^x(l%qABkLbaDr^RVSTNIV(9q z*q+i1$&vyV#MK-yfQv~6%QrG|(DE5IikH3s%LoY{N3jNLlL`&Hr6?swl#^S#w4TVg zv@Qg$P*M2U^hDWbCMj`H=*)RItsA<#155S*9tc^M>BDF1&w~!6d(*j>fj>Q2e#ZF-dYQKPW@y${ri>o< z`W_>qCjWtn5~D6`(Vw?2ZbR6lBSk5& z4tKqtRV;I#C!rkuD8oH6)j-9sELPI+k)_!0udI5nTV$^$SM?&#^=*93t4jnv9@l{o zo&ZYkmt&(m1qFqeu?NVdb9gvM&j;wpZ)nsV_MkM8v)U#q6fssbPoY#Fk16jkgM!}> zSsIT|?TOi|K;Ic>!+qXOpH$Y;rNjk!TV-s}!oL*Z5YYCOSqo5yK#t{1;Vey#tztPx zuP|zGk*t?zMr+)Wtyzw4%0>#$LtG}0)r93F(_B)`%sUbvR8xCb!qZ9rIKBZL|Gwr` zy^@B-%HRO$gpi!e7(V>LD=*lxL-%kB8GLx##CR2bmJ>L9qx({edlm5Q<)DHr6n^*O z3&GQ*34az%180>ya~26@0va`832eR4SJ_x5zbzNRr@v z>*Uu#b_UpE$$^HyIcr%P(7m1gfV2JN$~@tL7K7t4z7!e9?_hmoF|H}qI2ew>=q-mE zn+Kc6prEO3Q$Av{wG>8-+Kd_}rPAgvlCVSQvp}!wqS7l&6$UzfrJ8@ClES#)2?06S zTu7KUWe8Dh?^ZeV3pbA^pKAhoFW6;rV&aGzIC?*x#dFNwx>&u<+Ol#HrPcAt!0=v9 zKu$3GA;qFF!9tmVi6sD-9?AwVGXxiMR!U*=II zTq)yPEJ=*;KRn}L$_Y5lb}G$!$7<1jv5Lg2bOM^e0Smx-Su>sS3Ze(k)Fp6-9rnP) zB6RBUtNboUq`io-5){7&K}r3aU-`1<`=+JIayquq9H)G58BLZp6c26=-;L~K(%VD) z8~!*4%z^3@7Z0?>KI6<%CGS-6D{M$*3sbS5IEh&2nu@7DKAIrvD9}rMR?4i$rr%9N ze%x4~c)@D?MOhxaFJLSe%rFJd( z;!=3dy<#<&>f$rI0i#gI|46{wNHL0jCs?@xc4pkI_Tv_4ZlNV#gQ$%PQf{*hyiN^kBA2H|Q*B^#FksrLiiyMy@I zB(!z{4$b_$j6aVZtkU>x1|hTwbIIfgMqCHub&y+5YZ1bRV<_KTZld2_4zUEG$uUbe z*)9FNy})NTm-|mo_9SbZoHmuD0xX_zQ%rPp~3WBKEA~%-&M5jtkkO%{51E; zDC(!rr!fcOG=y^_^)&rFKHzcwXTgig8TGMraAc;e)!Kc>;AZ3*qQSl=;sq$9EX2Nm z?ysTc&thKS!&U#fee+rg{MIcCE{Chj28pvQ+pvjG}> z$DqjT+SDj*mqwS4J!)ou!_$-FoN=BkSwr67{+gyI_waF6;jNuIiR;WGMv--8NH zu-fk-kp4#9z7i{y-UC$D3s+EDCJ$x_)#xT}!oeo(ii5Om#EQJMlu`0)xdo0`GGE1mcPF*8Q?AExHj zpDa9=yc`o};eij~NwmV3kw=KzIKVW#4%FNB?&o%&v=Hb;u&sUE95S2$f;a z?aYuMvTrUIe=GF3r5x4g*ehyd1&<3nF*#_p4Kpcp6#n%BZW>EjV^}0;Bx6% z2E#WFV4~K~dru-&*|2}^UN^UPd|uen;wgGo|RQ&4IV`S`{aLT)lEp zRzwcnFCN7O%HjeNN#Ff>?O;qNM`#kygw#4E_7yC?N(Ys#;|+bp)9#`6z-Q~dP9-|? zra~v@QY$$%o^xa5(4RR~>H#Hs!Vvi13xMRpjU}fmb3Ka1w(!)bsP+F?Q{=uRZ;3j= zFB*8}2xA1k+%9@@ZvV|3V%U5SUS|h=We4p=;ZFo=kOAwqJ-puGd5Wz8vEAC?_elbH zm3}RLKKlY2WxhzfESdxzFEUTYa8&AU@jW-JDGMchxoO_MLdqv$c_Dm1`=hXT!*hru zVK0;}G90cGE$4dHkhJ;FL8t?K9xyt>p$KNpv@(g(sicp zJK5gK_zaP;HPcwh5RE?fLg{mfbYna#-@n)`Gk6y`dm)%k#3C^9RDA3Rn>kJMV1hE= z`OqA&K=c?A{_ly4e@Jw>*&3~VGi<3gl{b>1v6oMZc-~CO=b=1lpbnx*X5a3`p1E<< zNnsdxBh;=r=0A8N&m+K)&^K~eMwo}XtKP%My>0Six+@#c@>8YMNLv+ozawdzfTB~R zxG%?YL4ArbJwWqw&h&z7`yO)R%cttZJ$zJBBlL71cWw_O;6ob&3Dp`@+^K-n#MEy& z^l)Iwkex7Mv0Ei3*o6tYG#Sw37!ty=xUw32tPwuB{c?j#*d^a%)vMj+x!tahA2-ax zk~&&}=W{OdI(E7HN%eNX^KF_U@6gQu(`k5l_OG1%_HFObeqf$?jB>y~6A-#6za+x# zQy%(^Bq~3?yA#a1vnTo<=qe2qW+tV$eC@0YR{~RPJ7=QJUas4}i6yu&#=z&iHa^qt zKcL$JyLsK&WeS*ZyVA^ zlMnW^_z%J+s!DVN@FZv6oXtRYDO|IUq2ihgs%k}lci_%KjCTVd7_HkN{xup-T`%6n znBPSjJvtVyxr&gdtzmn4uRH7(tGDcZ zL1`A*^_MfxW2+7)RQ;bf5QV2Q*Mye!0k3Q(rnWv2vtNOax{8A!%ktt-`p;iZL9Urf z7Cj30jZYD?PDzx`fHpg1q5k7Ty!M9Xbmsbo&Zwghli;CjK(h(7pa5T2A?sJ=Po*!U z)pX&bB%v0E<7~frZn@FH7_lylHAd^aK~{ngf~U1V$^lnNPlwF8W}6`;7ltFRfAXK2 z@X5F?Y6xSTqkY7Mn@^arv?E^O^Za9Uzu138?D%wx6YD+sWb93L zLW);mJTZ1vhMgc7(g(ACLBetlqT|xl+tXYA#@m;|fk5ld?Bxz8Q-+_6;3>Fvg?`TE zvCD83DqP*H6|l+HUY9W#_oDU%>J&RWi|euGO&1;?&rDqd-ADN%@(K~jg-&6-s9cRZ z25>yC?hBU^rLJ~vVududbL3AHj~LUR>Y>M$E3z8Sg$-AzBQm70AiH_z@A7X|R7cx2 z@6ZAT3L=DcC|Np~qo_K-4R^Z+RA;2&{?cgwnn^?nCi zf!rKOwc-b*)h1gdbsX~9mpMl|Z@X2; z0j;FA0S7Te)CNrr?yC22YP6dJ4ES)gqTTd=nI3k*y{I-bc)6NlJ@9=ti_1l%WYq9} zjbjow!8v|i$)j8z-IFI~`OpigQosg_-xkW#CfX#*jV}S_b^r9H3Sy-u>ANdF!l(p5 zc+j;!T=2OXl5Srb8WNi-hm684JJ$?Qdi*MyqB5y|1xa|Dwf^>NZ8rbV#%Mg12cH%?TQ84 ziCJ3&fIJNOh-#PAYlrASl>@4%7^5F4x+w2R4h#qT`IPHuEWxtXKf{%HHn!Py=Rpz@ z60pD@jKGJ!;riU$&o8%m0Y)f>l}s^tMl@jg9Owa38YlD&K=cEOKNR@7A9(Z%oO}rp zi4s7r)@zWz#d_)ga3CUrW60oW*F=b+aa5;nkd)V=^?Ay*0J|Y+T6~Ux=UYixK-@hm z2jwNlx{0Z;u*ilyUUEtX7bT8`^h>qcv}l%v%UccM4P?&GQ_72~ z;R1%zK4U-6&qmlCn_7v3xgK}Au?P?!v8Fj zG{%5TC&0lvy7LaI9OF4X2zL|NcN_FtUw|kszl_=tkN+2Q(~vO2^rruC_qO$+ke8P^n}jThW6CcWo?X^o?A$Lf!>cUMu}Oic%#IIVdasB?|t0wL^IAA)#j z)oNWEmp~DAROF@0jjaSeoxmZKfemA+1G>wiMGH3RY2E;=_-DB3mVg0w zeuX6|PV{)^@C?#O*nHT9+Dd*NhvRHnh0y&05}#AAT5MMB2EO#G(%c?-lWCb>MX<9J z(w&65F+9I$Ow#esjozWl<20YNdi}7}c|@b5Emfmj>`g5n?_Y?e>^4?lY~!R0o}IM# zhXsm9?J!u`W2zw1rM&oPqLNjd&-SL`HC(h=KofCR_RUWLl^zq~CP7lP^b{@~#Bm%% zrhR9-obNBy3fgfnYC;C78 zdX2wE3kXj^k-yumHpWd9I^{jCtal3x&OUU$LxXNL_e#CMMgWd^(^PMA#inHPKx8=wy2gSG9<^w|M{Go3iz zmt&0XX!f$pqe%o6DRqI!T<|3H?PcRFr{lKDah~k(!2Ql>42?6G^nd;ZAV$vk@GSL- z=zAsT;U`PhcV1vXYnpVRvhbt%izdLAu+IfipZQY1|G!Y=sCd}r4ApBRk}H;w4ah0$ zh6t0Y)80@!9JbkEhgkiA$YxNH;>UV%4Z2-4`5U6_2jK^9PlE(b%ZvXhk-T;mLb z_yYxWVM+_=H~xPB*gz-0q>LVNUky0E!2%`&2#%f){gYpl|HD80L-MP?`m6dy;a-iu zU+LfosLxjxTn_M*;T2b0p*!|`dywz^UwrY!`XeAGoph4Ixn$sS0;k|c*Kgg94-bBE z=p`?CiN43+@L|V6yHu~(vOPi`5x!rZ5EO~DZwLm%?^Or&Qow0?B^p47N8$kJk1goU z5pXt>7lYP>iPkO+%y9Eg09ZsLX+rD-Y@MJ`rYkMSRjX%*IyeOs*wcp8epZ@*-(I?M zAU-Seq75I?{6^m|vXcF>rH7O4w);Q?Mx zxCPQdWk=!ww6Vb^>1}4p)_HRRVA-aMFY4V1XbHxgqXb(ZLhSnj1_9eddN2h`D~1&? zd}esAhxugo@BQBI=`ZduD`WfzE6^_L_crjY0p9%MmEb@7vp>^ad>jGK|8rlTUqbR# zLBDOrdGOO>e8+%KrP24L1b38a+=K3*SE#e}x3pO{1ZP_Fjb?eG?Umt6YWh%%vHQ)~#<8}(h2P1y}B$?lbDd)yviu9ljP z3XAy^Q7?!i#33a|-~g=Uv0zfeO*@4`3e+Nn#+!qfIboqY1WFu?JQHHZFs@&jt?&~@ zit%9dq?zq`rI%m(5bx>*+UP|&VjyVU5puEy~BvJRVWlv4+{-i8rq0|)K# zwhgi#c+!6A;MO8uL9ux{S}223r-xe(n%3a^`iZNr$)N66hR9;8{$m=e1%Xz$keu2FogU_9Do~TX7FmNVZlRM=XQ|K!4nb!1 znV^Rtw~j0^P_A=%YRK9SY6S?~0^ zUt?s$3h?GvkB}K}0V0cD!?2vw;^uEH1P_ZX%M*fI>y7eERUQ*A)jDhlnm+bw^vbta zAuI!LBo07jB>h(rNYCh;nLM#|wKpdqf)@OgUfr+o5UCo}oQXRD?ht5D0>J>UFtZOs zf!v_2^4`uE`Y?^GmsYU+W=YN4fV|Pj$35Asd;k02ub=Q@(`^{?qVL>7>~{}jfE=9q$O9H0}I2sv$c$%wmB9E(x(u1e} zLY_EP%te^1j&hP=-y&Z?ugNRJOg_x^emD!z{BRht!cbK|;=&=ON8$jo_M0&x6naXh zy~mbD5CKQa%@Jssft8}&37D{*rwUUrji;{-Ahp5PR6Ot<(%>vLqzdKAv{XhTqVpN_ z7k=Rvl9#>gW$8=|9OL4i{fFh7f$>W?ID&qRb)l}{d{}MpbYDL_aR7WIgf@~s*a3Z; zXqC%yDIg;+9}fyTp4Z2$l?s<;t35=`2j2TdP{wa5sS#WP)O6OSQx;hK(`hkv={e?U zeb3TBvYgWi)P*F>Z~}fg0ESX5b;UcF&^IN34zKUrZ z#{9b1y)NO?WAQ$OGs3{QHh;q#-k=Zh@RT3-^4ageN%-cZUjaex2!e}D2zzw}FLL>P3Z032iGO~7CN4e=3A9>?W30N_J8&IDZK zT$HuLFj#Pjnfdt`DUbA$?=|?o!L`?3yX@H?L)W&d5}78MqV?LE3=isxYzS_fmoEyp zWGN7I6nHw*2mMtT@Ida8cX$8~H5$PRz}jz}w3!_Ud7rge2^b7<<>&q)#7c!05@njr z_$ELp(3OC)6dW5|tIuFX#SEAOi(Ju%UC(kGjL~AiEbY0S*30$1=E#cKi@%oLOU-u^ji`?4u6AvLF%!ig4@_8f&5u_(CH^W@rh+G|2wsD5HPf7Qa&HHWv(%Q z@7j7xzWRvfcV-<#)upsE&48dIaR9WH&ZUNIHmr`o%^dSqJP}ju1Qe#G>`nwR1_nk^ zZ~_X`}x>szcc?rJ)zIOGFS+2xn*2NeEj1dPq_48 zWkJ154KZ!Ifi8`&>bjhm$v4Y~Lw3uC;J#Y5iq@u@yMWO{zqFeBm%>OKfOQ5__l}tu zcz242b1XQP;OS~W;0nUr-A#b$_<>9#L3FULAgoOH1W*n+e4yR%Js)%N^g?Bhvrr7Z z9}D4)vdsHUofr*Y3+DTJ?CfJuer-R>HZ7$tmiv5f-~%7{z*5M&(&Wb69g3-BSDJ+i zg6hh=Un(Cp-)!D4PYAXu)ecVxx=m#9LVO2Z)_I*a;9A(NH(8pMx?FW84~FZrdJa#^ z7NC`OLT85wF!d+e2lomC?$9b^=MIS&XTY^OkNf`o&;NXK@4ffx3pKHmSczT4aj~bs zI|IBK$)!NtujQoE{)z%RpFx-;lhAatQrR>=*YpRyGyN~rAnJ<#QtmiuyM~k)i38Bt zIdsmZYBy>+Dh2Y-q3N{q1~h|q`~VBW$nc9!ATSKP)S=rcT?qsh>0tYy6E8Q!yavH< zx$WshkMR)rrT`EB@QN`GV3hA2bZS>O80}?~@$GMayKeri)ON=0S54w1OV}glOXUf9 zWf+|Wlxi7zAC8!IRO!(;0PS#Qyw-U)YBPLy1esC0kOU9XoZ#}bCM;kEhrj%CgVI=; z^YYxn@t}>i=nz&)9x1S-=3Y8K1;%QC+iJkvK`bn((UfBByesgwx4lgtOQvl&t3k*y z?t$|&3r$TlM(H~Z7v>Z;1h>uBTg{gKyrAhch97ZMT=3=aX9A+)p(ID*0Aiz-krP`s zV|AnUo)w({a1)T=2Pcq&1wu1?H+V(VlJsSufH#ghO8^67cKxtC?#u7-z3W}?N-qmo zb|0+3odjMN=9_=~ILU`T^dU8TH~vAk3*+|?8=}LO9gAABA-G;XCysH)M(*`l9j?dA zuCXTuBXIz<#Kr5eOx6Ok&ZaQM6IfbieX#P_3E)5o9%p#K%^_q$W_Q=Udoec3_H(Qd z?WKnv1Y=|7=a+S~0Y19X?bhC)rGbG{AI}H;&ENcuUZU{jAfNdMUEi?z zsZPqNnv}{%&xm$5`KfW!PhwSg)n)V+q8uZP4)8~~lct6{rtva}621JCNq%nexH z34r7@im#Z0B?Pe(FiN;e2z6y#4XpKqihSU$emLiYKdi!i?Op>jq(2nXE6qy9b4S1f zH+-&(9|7U<-AgaMRFCt|KR^8(7k!}L?hstI9(x~=wFlD33xg+c33B`GwN@UI{3kcocU6nX%iz83%JZ=3xTdBCSVxMAv=}L@D{k zK0D+6Ism?EgED)coabtd{nCI3hWLml-zDT%dU=u%hkzsCX+pl|$Hzo?r-0=oL9YBit;Sb zaMg1?CCS!CQju4N>w9WzYN-tbT2KaDb=OvnzyZiFz-Wie7`6XlB|xh%(o_JzfeDg% zL^tPv7dZjNlao6EyG@|nDM|$hgo6+dutBU$R{lT@hl1NW2Vt6%n?!?F(KvsSB{=UNKcEJe%$2(_E0En#y)LDd;fZ_?< zgeH6hZW$bb1!5a$lduoCdw&6L4@N!Gb87DwSU4^FuUhp7JV|a$k5-W8GSbxqPP&N)@377=j*fD+8 zAy*`ihajl59?|ck=1Z0J^?810oSRy)CMCZ^057l{)7TsTxZ6J9F>IVZ^d->yimzQ~h9MoTMNYgjeC+;u zX_&n&h^b<*;l4gFoPd4A zOET^R!fL=)3(Wsk#vmHDtpos@6JUVJdGJdHA9#cNOfX&vxSWO`yUd4Q)9R`LbqyPW z<>O~$LvW&ApOrUM6v08^oX~ggJSQGlzu)RG;M} z->@hhHJenXF_a{@R#H*#WxqJjZ!NlpMBatIT1q$+1UMLH3I)g4VHIhAm%*gE~9vAs7>8<53RSefeg3bEopc=7skW#f{E7UFn z!Stc>4JHOM-W*gaL9hXaOdvRn@$9cM9OlW#WSDgfo%aw-&RN=XDr|n|1Jk}}c3Cb3 zYRSALha5)Y0Ibv27dKpW0(6#j4~7}~s_DoH(EqkO1i^rvmkSlqnM=r40z;PHn0*%s ztR^R$wpH~C9b(ex(y=9iot?($H+2MF#8`)Y$1*qtX0m)R1Iojeog0Y*@K)vxHq7AC z&0d`VL!(k*B|wl-a-NzMFeMD0aYx7|W(N6in*9%n8JIymgn~ZhC;f>HpYHj$E74^h za#NU|hkqK40}!jsI-Q&AwaYvv>`uT;*97601Bd|#0^)WFM$WP2+j2qeb?Q6 zDNA9b)fP-Yy(TM{Nlw6SCxPWq6?r5p0P7t6zzK+HN)Nr`q%c;EaRP0dgW+jE1S52g zZz5ISASW$U{s_%^BS}kJ3-ryCEEbk>hd>3(b-u7gh8`E|9_NQ6fkVcbit=ED zm>_!8OK?1mMe&GpF!EvIX@gTiFmo@%_}z(6Jj^gM2Vkb@*Z*`-Afd+{oq+V(_XV6v z*9r(YtOO8@-ytAvPJoo8Q>h9S^(dW-SW#r*5F~He@p&wPB})KzV$ux;yi-|3I`HI38~``(QctG|HE%I;^Lv0|CqOS#hrs?} zcvb+nppJuiUk%-a?A(k>h^3HKLKa-CI0sAn?C<#ucWEi-w>0W8)yk4U(3ebYw0UTh zU)Q5v6gx^9N2|&d7CP58A5^s|2#(|R1k#~JBXI!Mdh4ayrS#Ga+02}Bza((OSD0f3 zCtxrzxI-m4G{L#xK~RR#?;>&le3JQp*+2tS^V^YzALIeM5@|7mdaNAOH~@9rxfyYED29f)G+e z4pPX32NMe^ZR7+D7M4+h%9~Y==XB$EEP-wk&`Cj;k9jS1V@8* z-*h!s9#kCINWKOl^^C*;xbWNw&_RM5sKwHI`L{B67Dzubf_~BaL%U$}Jjyq&D@ksUn zY{%Y=mlc#>rvt(GF?V+c_Kknc*`SC)(_vpA#S1iwg%iN-rZiTUZ4e4rq|G1~H_m+E zp&W-pPXfj9?zM*)XB^yEJ*F(iad^>)9S5WLkir093!%VykX4wea3~#KG$IFJ9pH8# z{gOK!)=7*~@|-bbGhjK3vEw8R+srt20&sI0k6FuPcL=OpB@FU79F`JTl7m<4b8(sO z#8d15_+u{s=TO{<=p@q1YY2P+HAUphHTOnOw803&e#dQFt}HJk5sc_o&Ph*CpV zS0Luz=WRk2d2j;h2!%RR;p0_8SZsDcd6uL=Ucm~C>GNX!UV+U%=uJXvICPO2(yB>5 zkp-AU3mZP=!C5#jbYIf~cUY#Sa2#$Ji36~i*ynhJgcPisC}cBo0(940M0T6dVD301 z4i5?t#f}3!Ai(VKTCAj>bFEfOCMG8OAzAUbGcz+uv*~KK;+s3t2D-D)G}Cma)Ac;+ z>qgoZhvj}d+`3bgKDQWST+)7wLI5Li04hXX-(Z;bT%b7tx+tmO1Q5OrfG+r@1SeqP zVDfe-F?8^CCyQ@5DcW!PjdGnj!oLqhN z)k(EFl40*l<|`E$?IWlbieANehilx$@KHf%iiYoS=MnQY+ptRq%7+<7pb7J7`^{i| zCO5Ek)|^1z3+K%pOK*1qB3yy1kBE$22TRyfp9gry%U`d{%Zu^Wty`0eF1kpKe=*!5 z?Mlnj@7dYeWO{n~kmyf0`Nreh4X0mttV^DTM{2M=Z18n6{x*IxP7ZVa{kFGX{1G?+ z6P6jebWjbpe|%8`U(@n*GmG;Mi#FQK~a`;VyVo*YT!_C0QBQfU;YnboK%hE z#AKthw5SvY%%No->&$MmbUR#-By=-;%AoPX@|o^BKj6a-BXIys#nvfi^}G|HgLK^- zgw4{5%Zwf1Mjg)4N&qTw^^$;PfK{RqNq0Ju)DSDkFlGy{XB;0I5-=S~)ymYXo8e2B zeZ|?A;mfQa2d`6=KQ0(jdK3--XWG!O_^9xL!AqKN0}?P}Qn@qBA)&;_0?(X)q!+qF z07KQ~v0$ zciO%b9J>d)EBW9Az*T(Z0Co2}1g)~v6t@6Y8J|gH0Utwdku6z=F-NzcuBK;0NRU8AY{!E6ksZNDGXlI zrD1LYKEW-d1LE{lRmy)AOzS*+Ph+EB+Ssd(1^q%t^$=Ff`nj1uDpRGG@-&Y0H-*LJ zAuUk|tx}_;t7b>{f`P}wS^>lj6+>*z%S=vZRDdEYyn=cJUkRjTBHJjt7eikSbnDYt^oKZS^l%Gsk}cY`fRD8aApmo6$wKdcM%>1AK{O6M^ue{PhheXT@ zfWD1GZxX1KFpn}Dax7nM))5H$NkE_7GBp;%&*h8dQC^aUE4-Y>VLU4&z~tDgY6ynRd6AeheRG zeo0{F?*n8;j1vH2P{YuiB+KP$GBvRU7HuJ35wsQd2w2RC^vGt6wh|>EN;+NL)+(24 zVug0)?+WfQ&BjSaGvI;}J zgNnS~LGc^&Ik*x~{fTUD+nSTr0o0qbs)LlLM;F7V2d;3rxP#%RxYz|53Ah^8#B;5E zdnK7xN0|giv)lmA!`>Fw2G^T=_eetbP;deS?}-^QdEi*Omt{gxd?hQu1X=k+0wkaE z6q+Na&y&vs;#s{$xQ)YD0^KEmW3R~G(FyA=(CJDwM^0*-=vtc&8!yv6)89Cq>9p&1 zzzMu!+_2x!oT=}|0T3u2S{RlC@IKM-*8aJjdn6M1g?oW*k5sC4tKq8g7My@fz&2mZ z=u=qa1l+l3rE={iyN}7X>2s6yliMUIEgQ|IaP zII7GA!*>Pm6r29Z89ffVoWqL@--Sgv-v&?`(bfa?eY+(GnBe4=8{m0Z4j|X)nb{q? z=~0_BoU%YX+ONx2iq!E)vD_^^@7lB1byT6<#axDA)MAgN#+ z#u8|kfSZ`aBu|@uQftfPiAkLWx#*Ye0%VpLq35hq;plGA-yGfoh9BX}7ItHHPkjdt zfZ!}FH{kQ29e_8Mn4ogoGk^Ck*+`L14KZXbF@v@p`}i5_VEk&(c?Xd*czl9}$T37t zfR%uHQ^Bc}Cz7*|eqp05w{+sm0AC686NgI2(V->**H-BlI#MmwlE-cR!Dd->Csycg zX~dyr{Te=W!BN1&LRszr)Ms|>zw^-m=;V7GdD-zWHUYg+sl0aYxo59*YF{;(+#tSy{lw*@HUJFz6Kkjq?V3oa{RA&3%p5>?293 z?9cavP9XdsAl;}7U}ZuX%vcFXIu%%6pngdZOUNC8Ie}&}QQMGQa?W>%^=JuLTyI_0N);3t>K z^R1aj?m2Khn}7<5&|p2#FboGk19NiPbKu?sGxPg)$SZ;Tq+e1OV{d3ons=Y&QB{d$ z&82Y4zyqGeo~BG5ln6Lh-WjGi@)+m5{B7U*?B-cV|6o{s3{-|=W~)#FqCSNn>qXJp zCvJUN^V}nz)0&rO*U&9Fblr#IxbZau)zS;(^g<`1#3x~ z*J5(%T(3x6BIH9T7kqgLHhJlBzbJdF*Vk^__f2_Rpop8N9On)%39$6vzV5ND?>qjN z8%en$mRDDU<=(YvV~9mr?ulmv1^E_%U2|H#RcTdzFjcN^1dj!WiRvo`zNqClr%^dwep z1fuzFh7Uql8Vd9L;MHoCGi_aGdUEnQWkYf(3Ljrl@ z=Ogv4n*2zuyN+Pi}mTmbnSJJS~aeA_!0=VXBCM8vgAiOPg{RU>vd;*xN*v+r;72* z52pxXP+Hshhfsp+EOFo0`4~M%;FF8R@ZFIJygVCTxO zO7ilBRz31Y4YR|C18+2Kk&vQ@g|X_x04K{+$h%1~FISea!NMLI zHB^)+3c$hjnVllV^;pVs`4ldd1YXKR7|10+b?@8**M9EKw|!E+2*xJhj2tK?AEodG zi6jRX2E7O1EwetQ&Xmua960b`{q7sKOdNliicp^lR?#`l$YS|en3(%~X?o(w&t#2kSSL$3YmHd%3g4a)hOSf8eW!*r7V{>b~&R5L_Of~6pZ`^ zRkC~To*U-p59|*`@e07%NzvfKup9s_%=%{Dt8v@jYi>Ag?Zq?lR9=>_6fJ8ges{MqIez8Le~HJ*jcS-Y+-5wDXGcxAuLZ^x)jxC9VgvVO)gyxOl}$ zz%2%&m&bZrXKlE&b75R0i*T;G%h#+UKf&Om>Bw;-1(^{7jArd>%t9}H+IkMNbZ{bZt1T5 z-znWccU$Sv`oqbA)&X%@JgpFd_t5i)c&u}1P-42;sr#K^oy4MenIuz`ZuGz%y&Fz! zkmXXXH6cs<4Ye)Fwu$YnW7nPCI(q7i)^zPC`A|$*4Sy!g`sjiiKfL|$>+-a5l29uY z{J0IC_Sr1W2f}!n52&p?zyz;6H^1+`n|EGuQvkUGaN~#6$t5|MFboIaEemp0xoKx^ z-1nu2cGmCv_PXkZ)1@c+sc=UgdF%tp4xrwdXvtD=P8Dta#J1#u$zxiNnZ8`~p3Ndz zD=;_c;xM$-HFD$)AAE(eR(aZEYD?46QW%)RgB%^lcX(-@hj z9ju!;UhB~vIQk&@mGjb=3PRAb4s}!9)$l1S z$V>4#5oFb&e1lpwOB@D{}xD1JDJewFCb2sn*+SSKS zp89+syns2}xm(@FOCRxaUSTY(TzIRI^B}K% z(3R13=Rq6{UI!f-wXVomK0d(Bq_ z%yp-4`r!m}41$aoaHmy64{b?vd|=)NVmYdFjlH*D|G-DT8H!>D0E%}-l7kJyasVnrX)1B(qb+G}+JDuK9doyR zd42VwQ@IHrWM&39*uM3}O(h%@(ITdiz#_mc8;yQnHLNUT?=P_*)N|H{$^C!q#T|efKV(iS$-#wTIRIMdjrI=F zDeh^`U$yg}KYRS-+0R>3ncN_c1~uIN-gIN?e`H0k;4Gl$y_80` z0_f*X#25rUWb*NsX{4b)cpi?48F-kaw(j*2$09o+CaQy-U*4xLvTB>y+r(sG+fsB3 zs_+0Ad8xxM^$q0$ey6*rc>?wZ}ePr8^w~60jzrdEZBZ;5h5Y5E^ zyS?A>Rn|9V7e3n#`rDS(LXPTO{qoyCzWis$goOOS?vEV7LSVxp)!sla&j9GL>u-7) zpq*?EEHAw_{rBHlZY@vCQ;45He`%E2Ostnz>@+F`lfZWvy$Hp!;aJOZLv;lY(W+P`Op_%&)65g zxF|_UD=shnpj>{}~>*(KG1P zkAbYi2rU3J1!Z6y)rqD5oqclmZ;bD&e*9^v%L$PafE1O49$}La2oux?4|PDmNPJ9a zM_w1JjEEzd0T~^oyWz~xA?M{gW^c|G^nxz@vURPc{0?5umKS&SO#euSFPKpD4b`-5 z&jV2)rHg{G73faPeUZnxo$7#xcDxh*==|N83r-}|1hu_@xm zKRE0?0GbkVZ(utX0C?_0rP$3J|4YGZE0a@Cjep@&!^zmkL{8oUhW!b_PnCMp^?S!E zXNgX5KLOD@PC(u9*O|hJdD|95?>@;l88?>$w=>teH$HZ zLjnDRJp+(~U+SBu2i=Wq<>hHtx=(w-7I?lLZIiNs;gen-2?Jjr<`jg|(N8aL0q zHt~mlwXpKjX%WM({_q1JdBgvlGM@OIe)k?WKjN5@a zFNB#J&)!90hguF3&`dsbC8Y}k6VUgMI3r_tHd!be@D?V%P1a!F_+kAEW%RK~MiWe! z1|Vnq0#-(MleRKCn@IdjJ35KE>!W>5e)%eLDQUJ>>!+{3@E2#Uy?`(NV8=)DV-e8H zW5Ku|pxqGMQ`oKr0G+@naioy^DC;6{w)XmmN5_7pa$xY$XY@PV(swx%P?5L@NEmVu zCMG5_4$lu*C!Y&-pyg#u9WsTxn$l&6kYrvSNXx!$)b*?Z1pu1$KB-+LQYYP|X|EfG zTE3l(EPTpM4)8Zjd>58%e|)?$_SwEk{NR&mr$a|=`Na1Mn*8zx3`u@@@5Ea9%Ixd^ z`0BYYe^=Pp@tOSG{q5aCQY4Jn_h{qX%?-1UWrc2^EOAww&d z0&*ch38UE4X!N=9Fxez7Hu2$h%5xzOb>F9=I%t~su$Pn5B-UL&$MbDNyzq42+u@?1 zc4Z)$toe5Gb*MWX?;GpH#uj7&kPkXa@pVJzq6Btm6B7*figX}{?a4!sL3n+gK!%m3VXkMeoeKa$2e24r z97&^HZ+%cdb^epPKlk`ZviA|U2%Q3)!+!2JfC2|B5KAS?~>aMw-iOlBW6wSltO zDctpyj+tLz%eT!L>P>do3T=qMMu`s_nZ6YG6=Y1Zf(Foq#^`_+?7?5~KokggGl>sf ztAmQS1v|l((q+eoO%LSj&M&k!cK>#%I=}qW3E&#VJ1*0POxa{ol4z ze`!l$I~M@9X)&V4BbjMlsoz+=bpDC4U;E@}dHg{MGhURU0s&j?`=HE;hDUmc0qX&8 zG?`6|!mbQk0>K6`N3w+ua5Q#+Ar*%`X-B!Nif>#db-~&R zjgzO2>T4fyy6*s(WxQ-uzwjdu^_pu*wYGZw>>HC`d+ze=s~1I#r~X&~nEZV9-?6@m zOwVAu7XTmL&>z-`<#pW?wOVUt^62ObXlL5>P0r_tJ31l%P728F`b;Jr|0KsHKz^fk!( zju+LHCc)C=&+8~X-NAxQBhRx2ZNkuDH`AU;)(kx_r-r<4&K_9K-#bivu@`K(qFX@V z8|xOE#J76u$KKuG%g9S5jW6!@NMWbNTDfzkBU(Pl^~$^O^iu0PJ0V z?)-FB*u96lxd0UP&JW>0GEqNvqt==|cVy(Lql3w=2ZbRab&J5g_&ouRdVcaDNGZ>S zC)9lei|R@f@pNK3;UHUdx^Lt;8+{WUJ1gwCHqvAbw!8QRveW6}msv!-36&2zMRpMq z_?_p8t*$rt)=7NYg1yuxY;`6+nhfJ5!g|9Gr~J6)A1Dvjmam?D>(ZZm{p_`Gof6T$ zOMdu&w-x{lTM<}#a(VgG?Ddt&cOM`7%qNDEy&snt%e>^}fT&}Tf%J1{ex#GkO(yih z(zL$bWX{Q}t^{P=#7CmRut2~*h=qyod64Cyx<3BF28aTz+p^Nu@yjNw8j%n9q6z8S zN0qjAbMJCI4MJ^;f6m<8CbgY@ay9RR}DDSH8YFF@LFk{PdBLU)n!f-giWP zHUPTHU|#TS07TSx60oCNaf1)G^zcET?SQf47bGkb>9!MJY|%Di&T=94$-=S8$iR+C zFxukI+M(kHy|_K&Q>G30hr}<6jrR@m)Ipo>9!q+8M4rk)M}>gpp9Bx5{qmTv+>~Fp z-CR8VAOCsbD}Q-)?!PaK7_a&9yx)G?50CvAb~Ou=+YSA)0N8%pV4z%{X*^Xoq>>7@N|^buE&m-CM(wu_<53JYvYo(T4@2@nM7cbxX!MA^K=67FMSiXRldouZPyco~WSX0RjnS8qc+Z)MEv_?NSN_?0K zvfge{U1^+@c^##@ZLdRDcmT}40LBhz@f z>R<|Nl=!;%sFI|} z_+=l_%r!u~6CoiA!ABo?Ti+uS} zPgzd+tIMt0+|{`sz4ZEpul&vA&F`EMlSp*z_%``*4*&b8f5KOY7 z3eZ6{I-mu=(KL7<3Ix1StZ>5PA?Tp)ZGo1hr#JXIcq)tCUKT-_RXo*~#82eeer2WA zSejir`^L$s=l=S~lYjj0ON|d_MFq*vYke#NeALI}N4Gf)bVW#a4-o2p0zdTnTLBCw zURqj}W6&SNf=B#;ay3bYcKzo5KX~TZ@!$IV!NEsAJ(LWO%ez+E<#MTozqT$b951PL zJ-}iRIvomP$wL8&tlPpIUA@64nyd%hCBe{0knt^|u7`Jy{2;44m=h*XvFQxznsiRA zBPpn7la;h?SliF}l#wk+eDHMlY3QCl*lRVHM zUoepa-!~uXB5uo2&)41aX>d{=x&oh53bYfUIjcu|o5a++T*3#r^{Hyicl zt*P0iGw)2!y!ghstABS+RIt+{v61Mw-si!@=k-3qk86Io!9$Au0Y2>g;f(PDe(}IC zV-djF01loFAo1-1x-JqwGyLp>pWXL6Pd+yM*=P1w5C7~)vine_Tp5xD07m8NZ4G}9 zQWgWf0@Qhlxxog-jsm`Y=*G+w1Z5WqjHd&Z>?98~h^VbJ$t;gL5Zj0zWb(VdD5@?x z2q6!kk*?U+S-zbG2_~smh!HN0V(Np+37zsCHVlt4PNz6l&_@%LMG0khWOW<|Jo`8g zGV)Iptb%wrS0YI4x0g^nw5P-(T01E;5kRLuW{Oo}z z$3OqrV?$3pabWn!(cQ`ZhX>0ed#mL^d6ZaA+@gT#pbG+41mvU3Nhvrd+_^ox6^Gwu(oD^6Na=aigeHJS+k{BS7Nw;F*D~qYPPHJmfNARNg%F zz$b>DI{3)Qrw;F{KJu`)03mDlTS2tMNtj+K|bat%N@9Yht_ODQAb-w>%NVE#fJP3voFYj2##XW z^9oiE5&+PYPFd6`2mzsYEN;SQqCx}^?D(?kDaY}gjPuEww`C{el$<@}FZOac=XUbE z9gV#{CT`0g;8>H5z1e83HJ6(MjU{;v_xwt8?xwt&>-zl4`&XuGr>17=CvMzWc;}Yb z!Sy_Ma3(&JoI5-g1<-IH;c-w0N$=cDyC|go13$F==UC1o`k6l)Kyu?iqVp2LE&!xi z3>ysmvuywX158OoK~y|ccUN+7{4U=fzSzA7O4+K}QOFF-E?Pd_CxLw0t904rN<+A{d4=E**Shk38VEeEp`h z)@nA}vVji>8feQCx>ghA=31lKZa3SE_n7+ z^3TPt44t#tFLfMrx2!jK=;$uA`JBr$r2{P6OQW|{ZR0h(`G2?F9F;duPI}#Me0$L4 zT(Ta=g2i*5FXpp;5LvCmZFwP(t-*am>LsOju`ClRT54vN&Yq*8T-cc_RLrQ*dou5J zn0~%^UBkA=58`HSdBbkNuV(T}=k^lyxm%QMFEub8`B@YwyM<$Kw}aiooVlqF{xmL8 z>*e(0soLiwRoZdUcuKj+4bHubtsl)zuh((g`NH85^A3v$$qTu%sSJht=B$7JqqC9k ztKy9x&y#ar>oDJDj@?wcTOy(=aON(t#d{VXWGitmUvi-Gw)wQi>)PTU6J~v>ku2p7 zJ9jC$GF!QUH3@b&M=o<)7~umYEzy6gWs8lZtcqp*;d0>BG_y5p!Y|Z*);nq z7BjCCO8b^>^x^5>HPwFC_S!5(zgy>yycL_A_r*^3*6(||v(K1GR95ZFOMU;dXwH># zvm=IibP0l+XkKmbZNj literal 0 HcmV?d00001 diff --git a/src/features/app/utils/openAppIcons.ts b/src/features/app/utils/openAppIcons.ts index 836520b90..a0207b7d9 100644 --- a/src/features/app/utils/openAppIcons.ts +++ b/src/features/app/utils/openAppIcons.ts @@ -2,6 +2,7 @@ import cursorIcon from "../../../assets/app-icons/cursor.png"; import finderIcon from "../../../assets/app-icons/finder.png"; import antigravityIcon from "../../../assets/app-icons/antigravity.png"; import ghosttyIcon from "../../../assets/app-icons/ghostty.png"; +import phpstormIcon from "../../../assets/app-icons/phpstorm.png"; import vscodeIcon from "../../../assets/app-icons/vscode.png"; import zedIcon from "../../../assets/app-icons/zed.png"; import { isMacPlatform } from "../../../utils/platformPaths"; @@ -30,6 +31,8 @@ export function getKnownOpenAppIcon(id: string): string | null { return zedIcon; case "ghostty": return ghosttyIcon; + case "phpstorm": + return phpstormIcon; case "antigravity": return antigravityIcon; case "finder": From f6c9b1bc1b6a5d9c628553c7abadd04985dc69d0 Mon Sep 17 00:00:00 2001 From: Aaron Olin Date: Tue, 17 Mar 2026 16:28:28 -0500 Subject: [PATCH 03/17] fix: Running code lines should stretch 100% of the window content --- src/styles/messages.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/styles/messages.css b/src/styles/messages.css index fb14d4bc6..b3460827d 100644 --- a/src/styles/messages.css +++ b/src/styles/messages.css @@ -475,6 +475,8 @@ display: flex; flex-direction: column; gap: 6px; + width: 100%; + flex: 1 1 auto; min-width: 0; } @@ -485,6 +487,7 @@ gap: 8px; font-size: 12px; color: var(--text-stronger); + width: 100%; min-width: 0; } From 41d8bb3cd22bfadc2d38e33da6452d11fc5754ce Mon Sep 17 00:00:00 2001 From: Aaron Olin Date: Wed, 18 Mar 2026 00:53:18 -0500 Subject: [PATCH 04/17] Fix: Commands not stretching 100% width of content area and on wide monitors it gets cut off when there's extra space --- src/styles/messages.css | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/styles/messages.css b/src/styles/messages.css index b3460827d..69db3ae3e 100644 --- a/src/styles/messages.css +++ b/src/styles/messages.css @@ -1101,9 +1101,11 @@ .message .markdown-codeblock pre { margin: 0; - padding: 10px 12px 12px; + padding: 10px 12px 18px; white-space: pre; overflow-x: auto; + overflow-y: hidden; + scrollbar-gutter: stable both-edges; width: 100%; max-width: none; box-sizing: border-box; @@ -1115,12 +1117,14 @@ .message .markdown-codeblock-single { margin: 8px 0; - padding: 10px 12px 12px; + padding: 10px 12px 18px; border: 1px solid var(--border-stronger); border-radius: 10px; background: var(--surface-command); white-space: pre; overflow-x: auto; + overflow-y: hidden; + scrollbar-gutter: stable both-edges; width: 100%; max-width: none; box-sizing: border-box; From 387f0b9103be3013d2c961886277ae9adbbe2c99 Mon Sep 17 00:00:00 2001 From: Aaron Olin Date: Wed, 18 Mar 2026 13:27:20 -0500 Subject: [PATCH 05/17] Chore: Let's not overdo it on checking if the phpstorm binary exists and then enabling the IDE. We'll use the same pattern the author intended --- src-tauri/src/shared/settings_core.rs | 56 +--------------- src-tauri/src/shared/workspaces_core.rs | 2 +- src-tauri/src/shared/workspaces_core/io.rs | 74 +--------------------- src-tauri/src/types.rs | 18 +++++- 4 files changed, 20 insertions(+), 130 deletions(-) diff --git a/src-tauri/src/shared/settings_core.rs b/src-tauri/src/shared/settings_core.rs index ccfb14a1e..211fcf666 100644 --- a/src-tauri/src/shared/settings_core.rs +++ b/src-tauri/src/shared/settings_core.rs @@ -3,13 +3,9 @@ use std::path::PathBuf; use tokio::sync::Mutex; use crate::codex::config as codex_config; -use crate::types::OpenAppTarget; -use crate::shared::workspaces_core::optional_open_app_targets_core; use crate::storage::write_settings; use crate::types::AppSettings; -const OPTIONAL_OPEN_APP_TARGET_IDS: &[&str] = &["phpstorm"]; - fn normalize_personality(value: &str) -> Option<&'static str> { match value.trim() { "friendly" => Some("friendly"), @@ -40,7 +36,6 @@ pub(crate) async fn get_app_settings_core(app_settings: &Mutex) -> .unwrap_or("friendly") .to_string(); } - inject_optional_open_app_targets(&mut settings); settings } @@ -57,9 +52,7 @@ pub(crate) async fn update_app_settings_core( write_settings(settings_path, &settings)?; let mut current = app_settings.lock().await; *current = settings.clone(); - let mut response = settings; - inject_optional_open_app_targets(&mut response); - Ok(response) + Ok(settings) } pub(crate) fn get_codex_config_path_core() -> Result { @@ -71,50 +64,3 @@ pub(crate) fn get_codex_config_path_core() -> Result { .ok_or_else(|| "Unable to resolve CODEX_HOME".to_string()) }) } - -fn inject_optional_open_app_targets(settings: &mut AppSettings) { - let optional_targets = optional_open_app_targets_core(); - let mut available_optional_targets_by_id = optional_targets - .into_iter() - .map(|target| (target.id.clone(), target)) - .collect::>(); - let mut existing_targets = std::mem::take(&mut settings.open_app_targets); - existing_targets.retain(|target| { - !OPTIONAL_OPEN_APP_TARGET_IDS.contains(&target.id.as_str()) - || available_optional_targets_by_id.contains_key(&target.id) - }); - - let mut merged_targets = - Vec::with_capacity(existing_targets.len() + available_optional_targets_by_id.len()); - let mut inserted_optional = false; - - for target in existing_targets { - if OPTIONAL_OPEN_APP_TARGET_IDS.contains(&target.id.as_str()) { - available_optional_targets_by_id.remove(&target.id); - } - if !inserted_optional && target.kind == "finder" { - merged_targets.extend(available_optional_targets_by_id.into_values()); - available_optional_targets_by_id = std::collections::HashMap::new(); - inserted_optional = true; - } - merged_targets.push(target); - } - - if !inserted_optional { - merged_targets.extend(available_optional_targets_by_id.into_values()); - } - - settings.open_app_targets = merged_targets; - - if !settings - .open_app_targets - .iter() - .any(|target| target.id == settings.selected_open_app_id) - { - settings.selected_open_app_id = settings - .open_app_targets - .first() - .map(|target| target.id.clone()) - .unwrap_or_default(); - } -} diff --git a/src-tauri/src/shared/workspaces_core.rs b/src-tauri/src/shared/workspaces_core.rs index 451d515e0..90eb5f911 100644 --- a/src-tauri/src/shared/workspaces_core.rs +++ b/src-tauri/src/shared/workspaces_core.rs @@ -15,7 +15,7 @@ pub(crate) use git_orchestration::{apply_worktree_changes_core, run_git_command_ pub(crate) use helpers::{is_workspace_path_dir_core, list_workspaces_core}; pub(crate) use io::{ get_open_app_icon_core, list_workspace_files_core, open_workspace_in_core, - optional_open_app_targets_core, read_workspace_file_core, + read_workspace_file_core, }; pub(crate) use runtime_codex_args::{ set_workspace_runtime_codex_args_core, WorkspaceRuntimeCodexArgsResult, diff --git a/src-tauri/src/shared/workspaces_core/io.rs b/src-tauri/src/shared/workspaces_core/io.rs index 5b0151db8..1e12162b7 100644 --- a/src-tauri/src/shared/workspaces_core/io.rs +++ b/src-tauri/src/shared/workspaces_core/io.rs @@ -1,4 +1,3 @@ -use std::collections::HashMap; use std::env; use std::path::{Path, PathBuf}; @@ -107,76 +106,6 @@ fn normalize_app_identifier(app: &str) -> String { .join(" ") } -fn find_executable_in_path(program: &str) -> Option { - let trimmed = program.trim(); - if trimmed.is_empty() { - return None; - } - - let path = PathBuf::from(trimmed); - if path.is_file() { - return Some(path); - } - - let path_var = env::var_os("PATH")?; - for dir in env::split_paths(&path_var) { - let candidate = dir.join(trimmed); - if candidate.is_file() { - return Some(candidate); - } - } - - None -} - -#[cfg(target_os = "windows")] -fn first_available_command(candidates: &[&str]) -> Option { - candidates - .iter() - .copied() - .find(|candidate| resolve_windows_executable(candidate, None).is_some()) - .map(str::to_string) -} - -#[cfg(not(target_os = "windows"))] -fn first_available_command(candidates: &[&str]) -> Option { - candidates - .iter() - .copied() - .find(|candidate| find_executable_in_path(candidate).is_some()) - .map(str::to_string) -} - -#[cfg(target_os = "windows")] -fn phpstorm_command_candidates() -> &'static [&'static str] { - &["phpstorm64.exe", "phpstorm.bat", "phpstorm"] -} - -#[cfg(target_os = "macos")] -fn phpstorm_command_candidates() -> &'static [&'static str] { - &["phpstorm"] -} - -#[cfg(all(not(target_os = "windows"), not(target_os = "macos")))] -fn phpstorm_command_candidates() -> &'static [&'static str] { - &["phpstorm", "phpstorm.sh"] -} - -pub(crate) fn optional_open_app_targets_core() -> Vec { - let Some(command) = first_available_command(phpstorm_command_candidates()) else { - return Vec::new(); - }; - - vec![OpenAppTarget { - id: "phpstorm".to_string(), - label: "PHPStorm".to_string(), - kind: "command".to_string(), - app_name: None, - command: Some(command), - args: Vec::new(), - }] -} - fn build_launch_args( path: &str, args: &[String], @@ -300,8 +229,7 @@ pub(crate) async fn open_workspace_in_core( if let (Some(strategy), Some(cli_program)) = ( app_strategy, normalize_open_location(line, column) - .and_then(|_| app_cli_command(trimmed)) - .and_then(find_executable_in_path), + .and_then(|_| app_cli_command(trimmed)), ) { let launch_args = build_launch_args(&path, &args, line, column, Some(strategy)); let mut cmd = tokio_command(cli_program); diff --git a/src-tauri/src/types.rs b/src-tauri/src/types.rs index 6b595106b..5270a5783 100644 --- a/src-tauri/src/types.rs +++ b/src-tauri/src/types.rs @@ -1043,6 +1043,14 @@ fn default_open_app_targets() -> Vec { command: None, args: Vec::new(), }, + OpenAppTarget { + id: "phpstorm".to_string(), + label: "PHPStorm".to_string(), + kind: "command".to_string(), + app_name: None, + command: Some("phpstorm".to_string()), + args: Vec::new(), + }, OpenAppTarget { id: "finder".to_string(), label: "Finder".to_string(), @@ -1101,6 +1109,14 @@ fn default_open_app_targets() -> Vec { command: Some("antigravity".to_string()), args: Vec::new(), }, + OpenAppTarget { + id: "phpstorm".to_string(), + label: "PHPStorm".to_string(), + kind: "command".to_string(), + app_name: None, + command: Some("phpstorm".to_string()), + args: Vec::new(), + }, OpenAppTarget { id: "finder".to_string(), label: file_manager_label.to_string(), @@ -1364,7 +1380,7 @@ mod tests { "vscode" }; assert_eq!(settings.selected_open_app_id, expected_open_id); - assert_eq!(settings.open_app_targets.len(), 6); + assert_eq!(settings.open_app_targets.len(), 7); assert_eq!(settings.open_app_targets[0].id, "vscode"); } From c1b4a01d403aeb8905f289ed17314c0c089fab04 Mon Sep 17 00:00:00 2001 From: Aaron Olin Date: Wed, 18 Mar 2026 13:40:41 -0500 Subject: [PATCH 06/17] fix: add PHPStorm as a built-in IDE target and preserve macOS editor fallback --- src-tauri/src/shared/workspaces_core/io.rs | 25 +++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src-tauri/src/shared/workspaces_core/io.rs b/src-tauri/src/shared/workspaces_core/io.rs index 1e12162b7..ce0876c60 100644 --- a/src-tauri/src/shared/workspaces_core/io.rs +++ b/src-tauri/src/shared/workspaces_core/io.rs @@ -90,6 +90,28 @@ fn app_cli_command(app: &str) -> Option<&'static str> { None } +fn find_executable_in_path(program: &str) -> Option { + let trimmed = program.trim(); + if trimmed.is_empty() { + return None; + } + + let path = PathBuf::from(trimmed); + if path.is_file() { + return Some(path); + } + + let path_var = env::var_os("PATH")?; + for dir in env::split_paths(&path_var) { + let candidate = dir.join(trimmed); + if candidate.is_file() { + return Some(candidate); + } + } + + None +} + fn normalize_app_identifier(app: &str) -> String { app.trim() .chars() @@ -229,7 +251,8 @@ pub(crate) async fn open_workspace_in_core( if let (Some(strategy), Some(cli_program)) = ( app_strategy, normalize_open_location(line, column) - .and_then(|_| app_cli_command(trimmed)), + .and_then(|_| app_cli_command(trimmed)) + .and_then(find_executable_in_path), ) { let launch_args = build_launch_args(&path, &args, line, column, Some(strategy)); let mut cmd = tokio_command(cli_program); From 1d84967779e64ae4c196b979227d9cd29aa56496 Mon Sep 17 00:00:00 2001 From: Aaron Olin Date: Wed, 18 Mar 2026 13:42:43 -0500 Subject: [PATCH 07/17] fix: use the Windows PHPStorm launcher name in open app targets --- src-tauri/src/types.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src-tauri/src/types.rs b/src-tauri/src/types.rs index 5270a5783..1b3d2f8ae 100644 --- a/src-tauri/src/types.rs +++ b/src-tauri/src/types.rs @@ -1001,6 +1001,12 @@ fn default_workspace_groups() -> Vec { } fn default_open_app_targets() -> Vec { + let phpstorm_command = if cfg!(target_os = "windows") { + "phpstorm64.exe" + } else { + "phpstorm" + }; + if cfg!(target_os = "macos") { return vec![ OpenAppTarget { @@ -1048,7 +1054,7 @@ fn default_open_app_targets() -> Vec { label: "PHPStorm".to_string(), kind: "command".to_string(), app_name: None, - command: Some("phpstorm".to_string()), + command: Some(phpstorm_command.to_string()), args: Vec::new(), }, OpenAppTarget { @@ -1114,7 +1120,7 @@ fn default_open_app_targets() -> Vec { label: "PHPStorm".to_string(), kind: "command".to_string(), app_name: None, - command: Some("phpstorm".to_string()), + command: Some(phpstorm_command.to_string()), args: Vec::new(), }, OpenAppTarget { From 3f18662b89d0e967e9f608ce138b815abf57a5e9 Mon Sep 17 00:00:00 2001 From: Aaron Olin Date: Wed, 18 Mar 2026 13:50:57 -0500 Subject: [PATCH 08/17] fix: use the macOS app launcher fallback for PHPStorm --- src-tauri/src/shared/workspaces_core/io.rs | 6 ++++++ src-tauri/src/types.rs | 6 +++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src-tauri/src/shared/workspaces_core/io.rs b/src-tauri/src/shared/workspaces_core/io.rs index ce0876c60..36eb1bbd0 100644 --- a/src-tauri/src/shared/workspaces_core/io.rs +++ b/src-tauri/src/shared/workspaces_core/io.rs @@ -67,6 +67,9 @@ fn app_launch_strategy(app: &str) -> Option { if normalized.contains("visual studio code") || normalized.starts_with("cursor") { return Some(LineAwareLaunchStrategy::GotoFlag); } + if normalized == "phpstorm" { + return Some(LineAwareLaunchStrategy::JetBrainsLineColumnFlags); + } if normalized == "zed" || normalized.starts_with("zed ") { return Some(LineAwareLaunchStrategy::PathWithLineColumn); } @@ -84,6 +87,9 @@ fn app_cli_command(app: &str) -> Option<&'static str> { if normalized.starts_with("cursor") { return Some("cursor"); } + if normalized == "phpstorm" { + return Some("phpstorm"); + } if normalized == "zed" || normalized.starts_with("zed ") { return Some("zed"); } diff --git a/src-tauri/src/types.rs b/src-tauri/src/types.rs index 1b3d2f8ae..13c31d090 100644 --- a/src-tauri/src/types.rs +++ b/src-tauri/src/types.rs @@ -1052,9 +1052,9 @@ fn default_open_app_targets() -> Vec { OpenAppTarget { id: "phpstorm".to_string(), label: "PHPStorm".to_string(), - kind: "command".to_string(), - app_name: None, - command: Some(phpstorm_command.to_string()), + kind: "app".to_string(), + app_name: Some("PhpStorm".to_string()), + command: None, args: Vec::new(), }, OpenAppTarget { From 07320349d3639b0e9960c368d1e301a3d1c4d69e Mon Sep 17 00:00:00 2001 From: Aaron Olin Date: Wed, 18 Mar 2026 13:53:35 -0500 Subject: [PATCH 09/17] fix: migrate persisted open app targets to include PHPStorm --- src-tauri/src/storage.rs | 87 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/src-tauri/src/storage.rs b/src-tauri/src/storage.rs index c29b9058c..1faaddba2 100644 --- a/src-tauri/src/storage.rs +++ b/src-tauri/src/storage.rs @@ -31,11 +31,13 @@ pub(crate) fn read_settings(path: &PathBuf) -> Result { let data = std::fs::read_to_string(path).map_err(|e| e.to_string())?; let mut value: Value = serde_json::from_str(&data).map_err(|e| e.to_string())?; migrate_follow_up_message_behavior(&mut value); + migrate_open_app_targets(&mut value); match serde_json::from_value(value.clone()) { Ok(settings) => Ok(settings), Err(_) => { sanitize_remote_settings_for_tcp_only(&mut value); migrate_follow_up_message_behavior(&mut value); + migrate_open_app_targets(&mut value); serde_json::from_value(value).map_err(|e| e.to_string()) } } @@ -92,6 +94,47 @@ fn migrate_follow_up_message_behavior(value: &mut Value) { ); } +fn migrate_open_app_targets(value: &mut Value) { + let Value::Object(root) = value else { + return; + }; + let Some(Value::Array(existing_targets)) = root.get_mut("openAppTargets") else { + return; + }; + + let default_targets = match serde_json::to_value(AppSettings::default().open_app_targets) { + Ok(Value::Array(targets)) => targets, + _ => return, + }; + + let existing_ids = existing_targets + .iter() + .filter_map(|target| target.get("id").and_then(Value::as_str)) + .collect::>(); + + let missing_targets: Vec = default_targets + .into_iter() + .filter(|target| { + target + .get("id") + .and_then(Value::as_str) + .map(|id| !existing_ids.contains(id)) + .unwrap_or(false) + }) + .collect(); + + if missing_targets.is_empty() { + return; + } + + let insert_at = existing_targets + .iter() + .position(|target| target.get("id").and_then(Value::as_str) == Some("finder")) + .unwrap_or(existing_targets.len()); + + existing_targets.splice(insert_at..insert_at, missing_targets); +} + #[cfg(test)] mod tests { use super::{read_settings, read_workspaces, write_workspaces}; @@ -251,4 +294,48 @@ mod tests { let settings = read_settings(&path).expect("read settings"); assert_eq!(settings.follow_up_message_behavior, "queue"); } + + #[test] + fn read_settings_migrates_missing_open_app_targets() { + let temp_dir = std::env::temp_dir().join(format!("codex-monitor-test-{}", Uuid::new_v4())); + std::fs::create_dir_all(&temp_dir).expect("create temp dir"); + let path = temp_dir.join("settings.json"); + + std::fs::write( + &path, + r#"{ + "theme": "dark", + "selectedOpenAppId": "vscode", + "openAppTargets": [ + { "id": "vscode", "label": "VS Code", "kind": "command", "appName": null, "command": "code", "args": [] }, + { "id": "cursor", "label": "Cursor", "kind": "command", "appName": null, "command": "cursor", "args": [] }, + { "id": "zed", "label": "Zed", "kind": "command", "appName": null, "command": "zed", "args": [] }, + { "id": "ghostty", "label": "Ghostty", "kind": "command", "appName": null, "command": "ghostty", "args": [] }, + { "id": "antigravity", "label": "Antigravity", "kind": "command", "appName": null, "command": "antigravity", "args": [] }, + { "id": "finder", "label": "File Manager", "kind": "finder", "appName": null, "command": null, "args": [] } + ] +}"#, + ) + .expect("write settings"); + + let settings = read_settings(&path).expect("read settings"); + let ids: Vec<&str> = settings + .open_app_targets + .iter() + .map(|target| target.id.as_str()) + .collect(); + + assert_eq!( + ids, + vec![ + "vscode", + "cursor", + "zed", + "ghostty", + "antigravity", + "phpstorm", + "finder" + ] + ); + } } From 026ffc99bd4cb093e969a14f4719b16a873f4ed9 Mon Sep 17 00:00:00 2001 From: Aaron Olin Date: Wed, 18 Mar 2026 14:24:29 -0500 Subject: [PATCH 10/17] fix: only migrate the PHPStorm open app target --- src-tauri/src/storage.rs | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/src-tauri/src/storage.rs b/src-tauri/src/storage.rs index 1faaddba2..9fc22085a 100644 --- a/src-tauri/src/storage.rs +++ b/src-tauri/src/storage.rs @@ -102,37 +102,29 @@ fn migrate_open_app_targets(value: &mut Value) { return; }; - let default_targets = match serde_json::to_value(AppSettings::default().open_app_targets) { - Ok(Value::Array(targets)) => targets, - _ => return, - }; - - let existing_ids = existing_targets + let has_phpstorm = existing_targets .iter() - .filter_map(|target| target.get("id").and_then(Value::as_str)) - .collect::>(); - - let missing_targets: Vec = default_targets - .into_iter() - .filter(|target| { - target - .get("id") - .and_then(Value::as_str) - .map(|id| !existing_ids.contains(id)) - .unwrap_or(false) - }) - .collect(); - - if missing_targets.is_empty() { + .any(|target| target.get("id").and_then(Value::as_str) == Some("phpstorm")); + if has_phpstorm { return; } + let phpstorm_target = match serde_json::to_value(AppSettings::default().open_app_targets) { + Ok(Value::Array(targets)) => targets + .into_iter() + .find(|target| target.get("id").and_then(Value::as_str) == Some("phpstorm")), + _ => None, + }; + let Some(phpstorm_target) = phpstorm_target else { + return; + }; + let insert_at = existing_targets .iter() .position(|target| target.get("id").and_then(Value::as_str) == Some("finder")) .unwrap_or(existing_targets.len()); - existing_targets.splice(insert_at..insert_at, missing_targets); + existing_targets.insert(insert_at, phpstorm_target); } #[cfg(test)] From 97969f40cd5a25b7ebe27ffcf9a95c4136de6c41 Mon Sep 17 00:00:00 2001 From: Aaron Olin Date: Wed, 18 Mar 2026 14:25:22 -0500 Subject: [PATCH 11/17] fix: restore missing HashMap import in workspace IO --- src-tauri/src/shared/workspaces_core/io.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src-tauri/src/shared/workspaces_core/io.rs b/src-tauri/src/shared/workspaces_core/io.rs index 36eb1bbd0..a558c0101 100644 --- a/src-tauri/src/shared/workspaces_core/io.rs +++ b/src-tauri/src/shared/workspaces_core/io.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::env; use std::path::{Path, PathBuf}; @@ -6,7 +7,7 @@ use tokio::sync::Mutex; use crate::shared::process_core::tokio_command; #[cfg(target_os = "windows")] use crate::shared::process_core::{build_cmd_c_command, resolve_windows_executable}; -use crate::types::{OpenAppTarget, WorkspaceEntry}; +use crate::types::WorkspaceEntry; use crate::utils::normalize_windows_namespace_path; use super::helpers::resolve_workspace_root; From 5035fdeb31fbb8b20cee65020b65e908ba3bd039 Mon Sep 17 00:00:00 2001 From: Aaron Olin Date: Wed, 18 Mar 2026 14:37:45 -0500 Subject: [PATCH 12/17] fix: add PHPStorm to the frontend open app defaults --- src/features/app/constants.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/features/app/constants.ts b/src/features/app/constants.ts index dacc6d2a2..9d2282d9e 100644 --- a/src/features/app/constants.ts +++ b/src/features/app/constants.ts @@ -7,6 +7,7 @@ import { export const OPEN_APP_STORAGE_KEY = "open-workspace-app"; export const DEFAULT_OPEN_APP_ID = isWindowsPlatform() ? "finder" : "vscode"; +const PHPSTORM_COMMAND = isWindowsPlatform() ? "phpstorm64.exe" : "phpstorm"; export type OpenAppId = string; @@ -47,6 +48,13 @@ export const DEFAULT_OPEN_APP_TARGETS: OpenAppTarget[] = isMacPlatform() appName: "Antigravity", args: [], }, + { + id: "phpstorm", + label: "PHPStorm", + kind: "app", + appName: "PhpStorm", + args: [], + }, { id: "finder", label: fileManagerName(), @@ -90,6 +98,13 @@ export const DEFAULT_OPEN_APP_TARGETS: OpenAppTarget[] = isMacPlatform() command: "antigravity", args: [], }, + { + id: "phpstorm", + label: "PHPStorm", + kind: "command", + command: PHPSTORM_COMMAND, + args: [], + }, { id: "finder", label: fileManagerName(), From d372eed1e443e0accd8ac4c6f539c4dc6aa343a8 Mon Sep 17 00:00:00 2001 From: Aaron Olin Date: Wed, 18 Mar 2026 14:53:38 -0500 Subject: [PATCH 13/17] fix: normalize Windows namespace paths for PHPStorm launches --- src-tauri/src/shared/workspaces_core/io.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src-tauri/src/shared/workspaces_core/io.rs b/src-tauri/src/shared/workspaces_core/io.rs index a558c0101..42006d89c 100644 --- a/src-tauri/src/shared/workspaces_core/io.rs +++ b/src-tauri/src/shared/workspaces_core/io.rs @@ -152,13 +152,14 @@ fn build_launch_args( launch_args.push(located_path); } Some(LineAwareLaunchStrategy::JetBrainsLineColumnFlags) => { + let sanitized_path = normalize_windows_namespace_path(path); launch_args.push("--line".to_string()); launch_args.push(line.to_string()); if let Some(column) = column { launch_args.push("--column".to_string()); launch_args.push(column.to_string()); } - launch_args.push(path.to_string()); + launch_args.push(sanitized_path); } Some(LineAwareLaunchStrategy::PathWithLineColumn) => { let sanitized_path = normalize_windows_namespace_path(path); From 742857ca1fb1a18ebef9ed88a5e82d074aa3f0bb Mon Sep 17 00:00:00 2001 From: Aaron Olin Date: Wed, 18 Mar 2026 16:08:18 -0500 Subject: [PATCH 14/17] fix: use the Linux PHPStorm launcher in open app defaults --- src-tauri/src/types.rs | 2 ++ src/features/app/constants.ts | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src-tauri/src/types.rs b/src-tauri/src/types.rs index 13c31d090..1a89bcae0 100644 --- a/src-tauri/src/types.rs +++ b/src-tauri/src/types.rs @@ -1003,6 +1003,8 @@ fn default_workspace_groups() -> Vec { fn default_open_app_targets() -> Vec { let phpstorm_command = if cfg!(target_os = "windows") { "phpstorm64.exe" + } else if cfg!(target_os = "linux") { + "phpstorm.sh" } else { "phpstorm" }; diff --git a/src/features/app/constants.ts b/src/features/app/constants.ts index 9d2282d9e..ef52ae3f9 100644 --- a/src/features/app/constants.ts +++ b/src/features/app/constants.ts @@ -7,7 +7,11 @@ import { export const OPEN_APP_STORAGE_KEY = "open-workspace-app"; export const DEFAULT_OPEN_APP_ID = isWindowsPlatform() ? "finder" : "vscode"; -const PHPSTORM_COMMAND = isWindowsPlatform() ? "phpstorm64.exe" : "phpstorm"; +const PHPSTORM_COMMAND = isWindowsPlatform() + ? "phpstorm64.exe" + : isMacPlatform() + ? "phpstorm" + : "phpstorm.sh"; export type OpenAppId = string; From 363d2fac8dea11868882e4baaa4b2858a843e84b Mon Sep 17 00:00:00 2001 From: Aaron Olin Date: Wed, 18 Mar 2026 20:03:09 -0500 Subject: [PATCH 15/17] fix: preserve line-aware args in the macOS PHPStorm fallback --- src-tauri/src/shared/workspaces_core/io.rs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src-tauri/src/shared/workspaces_core/io.rs b/src-tauri/src/shared/workspaces_core/io.rs index 42006d89c..be7ca4cff 100644 --- a/src-tauri/src/shared/workspaces_core/io.rs +++ b/src-tauri/src/shared/workspaces_core/io.rs @@ -269,10 +269,24 @@ pub(crate) async fn open_workspace_in_core( .await .map_err(|error| format!("Failed to open app ({target_label}): {error}"))? } else { + let fallback_app_args = if matches!( + app_strategy, + Some(LineAwareLaunchStrategy::JetBrainsLineColumnFlags) + ) && normalize_open_location(line, column).is_some() + { + build_launch_args(&path, &args, line, column, app_strategy) + } else { + Vec::new() + }; let mut cmd = tokio_command("open"); - cmd.arg("-a").arg(trimmed).arg(&path); - if !args.is_empty() { - cmd.arg("--args").args(&args); + cmd.arg("-a").arg(trimmed); + if fallback_app_args.is_empty() { + cmd.arg(&path); + if !args.is_empty() { + cmd.arg("--args").args(&args); + } + } else { + cmd.arg("--args").args(&fallback_app_args); } cmd.output() .await From 44bae446bd354edb09a532b259c47cb5c84c597d Mon Sep 17 00:00:00 2001 From: Aaron Olin Date: Thu, 19 Mar 2026 00:53:17 -0500 Subject: [PATCH 16/17] fix: use a fresh open instance for the macOS PHPStorm fallback --- src-tauri/src/shared/workspaces_core/io.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src-tauri/src/shared/workspaces_core/io.rs b/src-tauri/src/shared/workspaces_core/io.rs index be7ca4cff..d1db03020 100644 --- a/src-tauri/src/shared/workspaces_core/io.rs +++ b/src-tauri/src/shared/workspaces_core/io.rs @@ -286,6 +286,7 @@ pub(crate) async fn open_workspace_in_core( cmd.arg("--args").args(&args); } } else { + cmd.arg("-n"); cmd.arg("--args").args(&fallback_app_args); } cmd.output() From 18b275517274a5560c88778d2d0ecaaef1dade2a Mon Sep 17 00:00:00 2001 From: Aaron Olin Date: Thu, 19 Mar 2026 00:55:55 -0500 Subject: [PATCH 17/17] fix: recognize PhpStorm app bundle names in macOS launch mapping --- src-tauri/src/shared/workspaces_core/io.rs | 23 ++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src-tauri/src/shared/workspaces_core/io.rs b/src-tauri/src/shared/workspaces_core/io.rs index d1db03020..dd3d03263 100644 --- a/src-tauri/src/shared/workspaces_core/io.rs +++ b/src-tauri/src/shared/workspaces_core/io.rs @@ -68,7 +68,7 @@ fn app_launch_strategy(app: &str) -> Option { if normalized.contains("visual studio code") || normalized.starts_with("cursor") { return Some(LineAwareLaunchStrategy::GotoFlag); } - if normalized == "phpstorm" { + if is_phpstorm_app_identifier(&normalized) { return Some(LineAwareLaunchStrategy::JetBrainsLineColumnFlags); } if normalized == "zed" || normalized.starts_with("zed ") { @@ -88,7 +88,7 @@ fn app_cli_command(app: &str) -> Option<&'static str> { if normalized.starts_with("cursor") { return Some("cursor"); } - if normalized == "phpstorm" { + if is_phpstorm_app_identifier(&normalized) { return Some("phpstorm"); } if normalized == "zed" || normalized.starts_with("zed ") { @@ -97,6 +97,12 @@ fn app_cli_command(app: &str) -> Option<&'static str> { None } +fn is_phpstorm_app_identifier(normalized: &str) -> bool { + normalized == "phpstorm" + || normalized == "phpstorm app" + || normalized.ends_with(" phpstorm app") +} + fn find_executable_in_path(program: &str) -> Option { let trimmed = program.trim(); if trimmed.is_empty() { @@ -379,6 +385,14 @@ mod tests { app_launch_strategy("Zed Preview"), Some(LineAwareLaunchStrategy::PathWithLineColumn) ); + assert_eq!( + app_launch_strategy("PhpStorm.app"), + Some(LineAwareLaunchStrategy::JetBrainsLineColumnFlags) + ); + assert_eq!( + app_launch_strategy("/Applications/PhpStorm.app"), + Some(LineAwareLaunchStrategy::JetBrainsLineColumnFlags) + ); assert_eq!(app_launch_strategy("Ghostty"), None); } @@ -394,6 +408,11 @@ mod tests { Some("code-insiders") ); assert_eq!(app_cli_command("Cursor"), Some("cursor")); + assert_eq!(app_cli_command("PhpStorm.app"), Some("phpstorm")); + assert_eq!( + app_cli_command("/Applications/PhpStorm.app"), + Some("phpstorm") + ); assert_eq!(app_cli_command("Zed Preview"), Some("zed")); assert_eq!(app_cli_command("Ghostty"), None); }