Compare commits

..

2230 commits

Author SHA1 Message Date
Aymeric Cucherousset
a49d1baf87
Fix french translations (#538) 2025-04-15 07:16:06 +02:00
Piotr (Peter) Mardziel
fe8d8fe383
translation fixes and disambiguations (#528)
Co-authored-by: Piotr Mardziel <piotrm@Piotrs-Laptop.local>
2025-04-15 07:14:31 +02:00
crschnick
ab71d178f3 Show script icons in browser 2025-03-25 14:59:11 +00:00
crschnick
9318a24120 Fix script hierarchy containing duplicates 2025-03-25 14:56:46 +00:00
crschnick
a69ecdc94c Select icon if it is an exact match 2025-03-25 14:03:23 +00:00
crschnick
937d59a27a Fix potential OOB 2025-03-24 15:06:36 +00:00
crschnick
f648d63f4b [release] 2025-03-23 08:10:15 +00:00
crschnick
881452ccd0 Don't focus window after load 2025-03-22 18:52:36 +00:00
crschnick
c7bde460af [stage] 2025-03-22 18:39:01 +00:00
crschnick
12fe24a695 Make desktop open more robust 2025-03-22 18:37:59 +00:00
crschnick
3c13cf5e2a Improve tab init fail handling 2025-03-22 14:53:26 +00:00
crschnick
bc0e14a332 Don't check nested event loop return value 2025-03-22 13:30:06 +00:00
crschnick
10dab33ed4 Rework list box sync again 2025-03-22 13:24:18 +00:00
crschnick
c67411815e Check for event loop enter issues 2025-03-22 13:00:32 +00:00
crschnick
74bedf630c Fix list box sync 2025-03-22 11:23:59 +00:00
crschnick
93413c3da1 Improve icon choice keyboard workflow 2025-03-22 10:15:30 +00:00
crschnick
b39c74b4ff Improve list box performance by synchronizing less 2025-03-21 16:53:10 +00:00
crschnick
474d201708 Improve download progress display 2025-03-21 16:52:45 +00:00
crschnick
5c6acc50e4 [stage] 2025-03-21 14:32:04 +00:00
crschnick
0c0d91b67d Add ability to launch custom terminal editors 2025-03-21 14:28:33 +00:00
crschnick
894b93e3c2 [stage] 2025-03-21 09:33:10 +00:00
crschnick
8c516c043c Improve ssh key chmod 2025-03-21 09:25:46 +00:00
crschnick
9dc368ae7b Delay list box order updates 2025-03-21 09:09:51 +00:00
crschnick
f5543beb71 [stage] 2025-03-20 19:42:10 +00:00
crschnick
8c51eac399 Guard against nested event loop exceptions 2025-03-20 12:15:12 +00:00
crschnick
d4a713f29b [stage] 2025-03-20 10:19:20 +00:00
crschnick
c738331acd Disable terminal logging when not supported 2025-03-20 10:18:49 +00:00
crschnick
25fc3ca0c1 Fix int field issues 2025-03-20 10:18:39 +00:00
crschnick
e1ebd24c04 Ssh identity fixes [stage] 2025-03-20 09:31:30 +00:00
crschnick
96fb6b579b Synchronize list box to prevent concurrent modifications 2025-03-19 19:47:26 +00:00
crschnick
a01756562d Improve some error messages 2025-03-19 19:46:31 +00:00
crschnick
ad0e628b60 Add missing git message for icons 2025-03-19 19:09:57 +00:00
crschnick
a7f33dd0d2 [stage] 2025-03-19 19:03:28 +00:00
crschnick
5b6dadedfe Handle LXD scan errors 2025-03-19 18:29:13 +00:00
loong
45ef2eda8c
Update translations_zh.properties (#498) 2025-03-19 19:27:29 +01:00
crschnick
b4d1a9e68b Hide download box buttons while downloading 2025-03-19 17:54:18 +00:00
crschnick
086237f965 Make install detection more robust 2025-03-19 17:53:45 +00:00
crschnick
0417ce635e Bump jsvg dependency to fix reported svg rendering issues 2025-03-19 11:57:56 +00:00
crschnick
fa02ee1bc2 Bump vnclib to fix color depth issues 2025-03-19 11:57:30 +00:00
crschnick
7e2663c6ea [release] 2025-03-18 11:13:22 +00:00
crschnick
39e6e66b16 Fix box refresh on scene change [release] 2025-03-18 10:39:27 +00:00
crschnick
7b9afaef08 List box reliability fixes [release] 2025-03-18 10:02:51 +00:00
crschnick
a130bcfa91 Ignore late list changes 2025-03-18 08:47:06 +00:00
crschnick
6084fb8d4d Revert to previous list box implementation 2025-03-18 08:46:43 +00:00
crschnick
72a5c67aab Fix double struc creation 2025-03-18 08:39:32 +00:00
crschnick
e0bdf3f52d Improve terminal order on Linux 2025-03-18 08:38:57 +00:00
crschnick
e4b01ccf0b List box memory usage improvements [stage] 2025-03-18 07:30:36 +00:00
crschnick
516cfced4a Warp terminal adjustments [stage] 2025-03-17 19:28:31 +00:00
crschnick
40e2015780 Fix right side browser being blocked 2025-03-17 19:28:21 +00:00
crschnick
fe632cd474 [stage] 2025-03-17 18:36:27 +00:00
crschnick
8ee9c25f5f GC adjustments 2025-03-17 18:34:22 +00:00
crschnick
e1d0642557 Warp linux fixes 2025-03-17 18:33:30 +00:00
crschnick
6f29cfd637 Add support for warp on Windows and Linux 2025-03-17 18:16:49 +00:00
crschnick
4f15a39280 More translation fixes 2025-03-17 15:12:35 +00:00
Ikko Eltociear Ashimine
10c8ba62e7
docs: update contact_ja.md (#493) 2025-03-17 16:08:03 +01:00
crschnick
28dd386752 [release] 2025-03-17 08:30:59 +00:00
crschnick
ca1559937b Update changelog 2025-03-17 08:15:08 +00:00
crschnick
62ef05f707 [release] 2025-03-17 08:09:54 +00:00
crschnick
e78a791c06 Fix stage theme change not triggering 2025-03-17 08:07:01 +00:00
crschnick
72f2decd75 Use more aggressive gc ratio [stage] 2025-03-17 06:50:28 +00:00
crschnick
d93eacdb9b Tweak some gc settings [stage] 2025-03-17 06:45:17 +00:00
crschnick
795a3dde7c Remove platform pause from list box 2025-03-17 06:43:44 +00:00
crschnick
4ecc55443a Fix stackoverflow for some elevation requests 2025-03-17 06:43:34 +00:00
crschnick
51c8fff9bd Fix potential NPE 2025-03-17 06:42:57 +00:00
crschnick
25e2ddd2a3 Fix various NullPointers 2025-03-16 16:17:50 +00:00
crschnick
5473474334 Fix NPE when doing undo in filter 2025-03-16 16:17:33 +00:00
crschnick
c94e270eee Fix some platform out of bounds errors 2025-03-16 14:01:06 +00:00
crschnick
8850a78a36 Update ListBoxViewComp.java 2025-03-16 13:58:02 +00:00
crschnick
bffe75a540 Add more logging for layout content loading [stage] 2025-03-16 10:13:36 +00:00
crschnick
484028f22f Fix incus/lxd launches for busybox [stage] 2025-03-16 09:15:31 +00:00
crschnick
b2ac3c1fba Only refresh frame on Windows [stage] 2025-03-16 08:39:53 +00:00
crschnick
12c414eecc Add more logging for app initialization [stage] 2025-03-16 06:59:38 +00:00
crschnick
c42b6d8439 Fix user feedback size limit 2025-03-16 06:58:10 +00:00
crschnick
05d93c68ee Fix fish shell session scripts being skipped [stage] 2025-03-15 14:13:58 +00:00
crschnick
ec3b95c11b Fix outdated security links 2025-03-15 14:13:35 +00:00
crschnick
e69da5d5b6 Don't animate transition in performance mode 2025-03-15 08:43:54 +00:00
crschnick
c265b6b87d [release] 2025-03-15 07:10:20 +00:00
crschnick
9844cadbdd Link sync docs instead of showing built-in dialog 2025-03-14 11:28:02 +00:00
crschnick
1c42605650 [stage] 2025-03-13 12:57:36 +00:00
crschnick
5b454cd8cd Fix NPE for FreeRDP 2025-03-13 12:55:25 +00:00
crschnick
d7cb5967c6 Some shell fixes 2025-03-13 12:44:29 +00:00
crschnick
a98425f100 Some shell fixes 2025-03-13 12:36:12 +00:00
crschnick
793ca373aa Properly delegate initialized call [stage] 2025-03-13 08:39:14 +00:00
crschnick
b0a7f9d17e [stage] 2025-03-13 08:33:07 +00:00
crschnick
55e7c65462 Rework connections category on macos to focus on fallback shell 2025-03-13 08:26:29 +00:00
crschnick
c57000acee Rework fallback shell 2025-03-13 08:26:04 +00:00
crschnick
ff5941249d Fix vscode remote detection on macos 2025-03-13 04:28:04 +00:00
crschnick
48450490c3 Fix observable binding being gced 2025-03-13 04:13:27 +00:00
crschnick
6cc46ed08f Support ctrl+backspace for password fields 2025-03-12 11:35:13 +00:00
crschnick
03d222e5f5 Improve os logo mapping with spaces 2025-03-11 09:05:04 +00:00
crschnick
b2efb8ddfa Add clear functionality for choice comps 2025-03-11 04:28:51 +00:00
crschnick
f5f14a441f [release] 2025-03-11 02:23:20 +00:00
crschnick
3b84e5e480 Various small fixes [stage] 2025-03-11 01:51:53 +00:00
crschnick
0af114ca8b Adjust icon size [stage] 2025-03-10 09:18:28 +00:00
crschnick
ca5a3a63c6 Various small fixes 2025-03-10 09:12:49 +00:00
crschnick
09a0323069 Update sidebar size [stage] 2025-03-10 04:30:29 +00:00
crschnick
69524fb140 Style fixes 2025-03-10 03:52:34 +00:00
crschnick
8dd23fde64 More list box visibility adjustments [stage] 2025-03-10 03:25:34 +00:00
crschnick
9ce9c31674 List box visibility fixes 2025-03-10 03:23:05 +00:00
crschnick
907b6b8033 Various fixes 2025-03-10 01:53:09 +00:00
crschnick
35eb46df84 [stage] 2025-03-09 12:07:58 +00:00
crschnick
287e0031a0 Small section fixes 2025-03-09 12:07:51 +00:00
crschnick
1c933cd34f [stage] 2025-03-09 10:52:31 +00:00
crschnick
5f0732f5ae Fix PTB update alert showing too early 2025-03-09 10:52:14 +00:00
crschnick
2643a47116 [stage] 2025-03-09 10:35:35 +00:00
crschnick
50565d28ed Fix user token check [stage] 2025-03-09 09:58:39 +00:00
crschnick
d66571b799 [stage] 2025-03-09 09:15:52 +00:00
crschnick
b77921549b Section rework 2025-03-09 09:13:55 +00:00
crschnick
39ee41e7b4 More performance adjustments 2025-03-09 05:40:30 +00:00
crschnick
ee418ed0ff Section performance fixes 2025-03-09 05:00:41 +00:00
crschnick
a898341011 Various performance fixes 2025-03-09 03:45:20 +00:00
crschnick
e4d29c441f Trim atlantafx css 2025-03-09 02:09:17 +00:00
crschnick
afaf206a42 More performance fixes 2025-03-09 01:17:19 +00:00
crschnick
572a0f0341 Various performance fixes 2025-03-09 00:56:56 +00:00
crschnick
ab5fa40828 Improved password manager test field 2025-03-09 00:24:38 +00:00
crschnick
78f6278e2b Improve store icon dialog 2025-03-09 00:18:34 +00:00
crschnick
98a7a9ae0e Style fixes 2025-03-09 00:07:56 +00:00
crschnick
04f17c6978 More svg rasterizer fixes 2025-03-08 23:48:53 +00:00
crschnick
9870a42744 Improve rasterizer output 2025-03-08 23:28:40 +00:00
crschnick
64069c7085 Improve macos frame border color 2025-03-08 23:20:22 +00:00
crschnick
95e5816e1b Style fixes 2025-03-07 15:40:00 +00:00
crschnick
da30fbd302 Make terminal launches more resistent [stage] 2025-03-06 17:32:36 +00:00
crschnick
97356b45d5 [stage] 2025-03-04 23:22:46 +00:00
crschnick
df8f3d1f8a [stage] 2025-03-04 16:23:49 +00:00
crschnick
be04a8f700 [release] 2025-03-04 11:53:22 +00:00
crschnick
2f7a2e1b19 Various fixes [stage] 2025-03-03 21:43:11 +00:00
crschnick
cefedcd0c8 Improve identity creation defaults 2025-03-03 20:44:44 +00:00
crschnick
0329a87ffd Fix new item show condition 2025-03-03 20:44:28 +00:00
crschnick
565f2f4cdd Fix provider refresh when no updates are made 2025-03-03 20:44:13 +00:00
crschnick
70c9152dd6 Fix browser modal overlay location 2025-03-03 20:44:01 +00:00
crschnick
4f30d5fd14 Fix children refresh merge not working [stage] 2025-03-03 16:08:17 +00:00
crschnick
43f4bfeb0a [stage] 2025-03-03 14:48:51 +00:00
crschnick
ee270b6cdc [stage] 2025-03-03 13:44:39 +00:00
crschnick
0bd1279399 Make max widths larger for several components 2025-03-03 13:39:03 +00:00
crschnick
a01dac767a Improve icon cache handling of blank images 2025-03-03 12:45:56 +00:00
crschnick
bbca5bafb3 SSH chmod fixes 2025-03-01 15:54:35 +00:00
crschnick
635a37c7ea Improve scale factor detection [stage] 2025-03-01 12:33:44 +00:00
crschnick
a38a8ed8a8 [stage] 2025-03-01 11:40:57 +00:00
crschnick
ee82a912fa Various small fixes 2025-03-01 11:40:51 +00:00
crschnick
6958a4979c Improve transfer stop resiliency 2025-02-28 22:01:14 +00:00
crschnick
3a90a2a67a Improve dark mode detection [stage] 2025-02-28 19:45:23 +00:00
crschnick
8cdeb54115 Theme improvements [stage] 2025-02-28 19:34:32 +00:00
crschnick
574399bf15 Improve theme logging 2025-02-28 18:40:21 +00:00
crschnick
671216a04e Fix logging statement [stage] 2025-02-28 17:32:59 +00:00
crschnick
6e541006c3 [stage] 2025-02-28 16:56:37 +00:00
crschnick
c53ac3eba5 Merge branch 'icons-constrast' 2025-02-28 16:55:30 +00:00
crschnick
34b576e9bb [stage] 2025-02-28 15:09:01 +00:00
crschnick
3df09a7eef Trace platform preferences changes [stage] 2025-02-28 14:38:24 +00:00
crschnick
8005d4b225 Fix NPE [release] 2025-02-27 08:50:25 +00:00
crschnick
c7a147883a Improve script compat description 2025-02-26 13:01:53 +00:00
crschnick
241954bdd9 Various fixes [stage] 2025-02-26 09:58:18 +00:00
crschnick
29ba973217 Small fixes [stage] 2025-02-25 07:16:10 +00:00
crschnick
3babf16a5b Small fixes [stage] 2025-02-25 05:44:50 +00:00
crschnick
d84e2fe0ce macos category display fixes 2025-02-25 04:59:33 +00:00
crschnick
996423c74e Add more macos editors 2025-02-25 04:55:50 +00:00
crschnick
09da9c59cb Add windsurf linux 2025-02-24 18:08:31 +00:00
crschnick
da6c0600e5 Add windows ai editors 2025-02-24 16:54:11 +00:00
crschnick
98c1090917 Tailscale translation fixes 2025-02-24 16:13:44 +00:00
crschnick
73a2beceac Translation fixes 2025-02-24 16:10:19 +00:00
Cesaryuan
69e959408e
Update Chinese translations for Tailscale terms (#449) 2025-02-24 17:08:27 +01:00
crschnick
04aab314da Launch feature polish [stage] 2025-02-24 14:58:21 +00:00
crschnick
d6002fa5fc Launch improvements 2025-02-24 14:25:18 +00:00
crschnick
8f653880ba Properly implement launch command 2025-02-24 13:43:49 +00:00
crschnick
cdfd7dc67d Update README.md 2025-02-24 06:51:00 +00:00
crschnick
28c039d359 Fix terminal restart secret progress being wrong 2025-02-23 16:35:02 +00:00
crschnick
af18da6ead Fix entry state not showing when first added 2025-02-23 14:16:55 +00:00
crschnick
28391e5847 Fix identity updates for empty values 2025-02-23 14:16:39 +00:00
crschnick
41ecefd090 Fix podman state display 2025-02-23 14:16:30 +00:00
crschnick
f2ea4cc4d7 Add support for cosmic-term 2025-02-23 07:58:39 +00:00
crschnick
dea1b0ff47 Recognize hetzner box 2025-02-23 07:58:22 +00:00
crschnick
2508fc9ca0 Small fixes [release] 2025-02-18 17:02:31 +00:00
crschnick
bb69e47cb0 Small fixes [stage] 2025-02-18 01:04:15 +00:00
crschnick
3b2ba7777c Small fixes [stage] 2025-02-17 23:47:56 +00:00
crschnick
f2ba052266 Move caret on selection 2025-02-17 23:27:41 +00:00
crschnick
22a9666abb Small fixes [stage] 2025-02-17 22:14:27 +00:00
crschnick
c59446f68d Remmina fixes [stage] 2025-02-17 21:39:18 +00:00
crschnick
7eab8cb670 Remmina fixes 2025-02-17 21:13:15 +00:00
crschnick
4725d28885 Remmina fixes 2025-02-17 21:08:42 +00:00
crschnick
d6e977e7a5 Remmina fixes 2025-02-17 20:45:54 +00:00
crschnick
3edf7cc17c [stage] 2025-02-17 18:48:37 +00:00
crschnick
a7ad65c0a4 rdp fixes 2025-02-17 18:39:28 +00:00
crschnick
41a652e03d rdp fixes 2025-02-17 18:35:31 +00:00
crschnick
c758743912 Add freerdp 2025-02-17 18:26:48 +00:00
crschnick
61cbdc6e6a [stage] 2025-02-17 18:03:54 +00:00
crschnick
46c49d164f Small fixes 2025-02-17 18:03:45 +00:00
crschnick
1d1ceb07f4 [stage] 2025-02-17 03:25:00 +00:00
crschnick
5f020c8fe5 [stage] 2025-02-17 03:16:17 +00:00
crschnick
0511acc80a Remmina fixes 2025-02-17 03:12:24 +00:00
crschnick
2a5f59f327 Remmina fixes 2025-02-17 03:08:57 +00:00
crschnick
56ce66d1e9 Remmina fix 2025-02-17 02:06:05 +00:00
crschnick
77fbb24817 Remmina fixes 2025-02-17 01:58:50 +00:00
crschnick
98a0abf4ef Remmina fixes 2025-02-17 01:56:43 +00:00
crschnick
a1ea4605ab Some remmina config changes 2025-02-17 01:26:57 +00:00
crschnick
493e041fb9 Use rdp name as file name 2025-02-17 00:26:19 +00:00
crschnick
4638885be8 Translation fixes 2025-02-17 00:26:00 +00:00
crschnick
c7e00238d9 [stage] 2025-02-16 23:02:41 +00:00
crschnick
2197cbc909 Small fixes 2025-02-16 22:47:16 +00:00
crschnick
020c6d9d4b Change webtop window size 2025-02-16 19:45:50 +00:00
crschnick
4a115fc411 Small fixes [stage] 2025-02-16 18:54:54 +00:00
crschnick
f6502c664c Improve category style 2025-02-16 02:37:58 +00:00
crschnick
eb67516209 Webtop fixes 2025-02-16 01:27:13 +00:00
crschnick
0a96061b1b Fix NPE for unavailable identities 2025-02-14 21:52:42 +00:00
crschnick
5896ea0288 Small fixes for translations 2025-02-14 21:52:18 +00:00
crschnick
d8155769d9 [release] 2025-02-13 12:37:42 +00:00
crschnick
d76a799969 Small fixes and translation updates 2025-02-13 12:00:43 +00:00
crschnick
9e4a205d9a [release] 2025-02-12 12:39:27 +00:00
crschnick
cb937200bc Fix shell dialect NPE 2025-02-12 10:40:34 +00:00
crschnick
9b613b57ed Fix window content alignment 2025-02-11 16:57:04 +00:00
crschnick
770cf6d1f3 Fix icons race condition 2025-02-11 16:56:50 +00:00
crschnick
dae161f0ec Update links 2025-02-11 15:43:38 +00:00
crschnick
d5b36619fc [release] 2025-02-11 11:30:30 +00:00
crschnick
b494c69ded Squash merge branch 15-release into master 2025-02-11 11:17:48 +00:00
bannert
c7171ce204
fix: update documentation URLs to reflect new structure (master) (#439) 2025-02-11 12:13:31 +01:00
crschnick
99383f23c0 Use proper linguist key for translations 2025-01-27 16:54:25 +00:00
crschnick
5f99ea41fe Ignore translations for language detection 2025-01-27 16:49:52 +00:00
Chenx Dust
db4ca6c403
Fix hyper link in README.md (#428) 2025-01-27 01:21:33 +01:00
crschnick
1db6c4edc4 Small fixes [release] 2025-01-22 13:14:29 +00:00
crschnick
6a2a69f8f6 Small fixes 2025-01-22 00:33:00 +00:00
crschnick
ddfef15cb4 Add support for waveterm [stage] 2025-01-21 21:01:03 +00:00
crschnick
5f299090d4 Close dialogs on exit 2025-01-21 21:00:32 +00:00
crschnick
6e6e16330c Fix all-same icon color 2025-01-21 21:00:21 +00:00
crschnick
9932482556 List incus support 2025-01-21 18:25:54 +00:00
crschnick
453ce21ded Small fixes [stage] 2025-01-21 14:16:53 +00:00
crschnick
bae90df7dc Add custom service open action 2025-01-21 14:00:26 +00:00
crschnick
5877d741b0 Improve handling of errors in some places 2025-01-21 00:04:22 +00:00
crschnick
833a163e71 Fix broken equals check 2025-01-20 21:55:11 +00:00
crschnick
f7f9c46931 Fix incompatible version error message 2025-01-18 22:16:23 +00:00
crschnick
3704c9f737 Some gradle build fixes 2025-01-18 22:10:06 +00:00
crschnick
5c0594817e Fix identity key sync [release] 2025-01-18 15:35:29 +00:00
crschnick
7cd1f75fcc [release] 2025-01-18 12:40:34 +00:00
crschnick
d9cfe07e53 Small save improvements 2025-01-18 12:17:42 +00:00
crschnick
63ba8d830c Improve entry save for concurrent changes [stage] 2025-01-18 11:08:01 +00:00
crschnick
1b9e60c6ad Update changelog 2025-01-18 10:38:08 +00:00
crschnick
cc8b9255cc Wait on terminal exit error to allow handling of it 2025-01-18 10:38:01 +00:00
crschnick
26804e9813 Don't check selected category for null 2025-01-18 09:43:43 +00:00
crschnick
eb9e0888ca [stage] 2025-01-17 22:22:48 +00:00
crschnick
2a0f5f75f0 Modal overlay close fixes 2025-01-17 22:07:59 +00:00
crschnick
0cb10b4a0b Adjust save thread spawning [stage] 2025-01-17 21:48:34 +00:00
crschnick
0fac04a13f [stage] 2025-01-17 21:43:33 +00:00
crschnick
5888801bcf Various category fixes 2025-01-17 21:37:37 +00:00
crschnick
50c7bc3181 Small fixes 2025-01-17 20:16:41 +00:00
crschnick
8518e051de Fix early window startup errors 2025-01-17 14:31:38 +00:00
crschnick
b8e358ed89 Synchronize list selector 2025-01-17 09:47:39 +00:00
crschnick
2a8a10c4d8 Allow json comments 2025-01-17 09:47:22 +00:00
crschnick
30f9cf15aa Don't return api error if created category already exists 2025-01-16 17:28:43 +00:00
crschnick
bd4ed83e8c Identity selection improvements 2025-01-16 17:15:54 +00:00
crschnick
b578faccc9 [release] 2025-01-16 07:50:28 +00:00
crschnick
45f6545fc8 Squash merge branch 14-release into master 2025-01-16 07:29:55 +00:00
crschnick
48c9f96c03 Clarify window backdrop enum 2024-12-14 04:07:37 +00:00
crschnick
ed24ad9138 Reformat 2024-12-13 09:05:00 +00:00
crschnick
d584a2f74d [release] 2024-12-13 06:23:09 +00:00
crschnick
9f18f7b48e Shield mouse release events as well 2024-12-13 05:56:20 +00:00
crschnick
f98c6e47c0 Click shield improvements 2024-12-13 05:05:51 +00:00
crschnick
60a007b754 Click shield rework 2024-12-13 04:37:02 +00:00
crschnick
dff204809d Improve click shield 2024-12-13 04:22:58 +00:00
crschnick
b2427fb956 [stage] 2024-12-12 21:32:42 +00:00
crschnick
a34edcd497 Fix potential NPEs 2024-12-12 20:33:47 +00:00
crschnick
d07a2e677d [stage] 2024-12-12 18:28:52 +00:00
crschnick
b17e57ef5f Rework window click behaviour on macOS 2024-12-12 18:26:12 +00:00
crschnick
e1941f83da Improve double click handling for connections 2024-12-12 16:53:07 +00:00
crschnick
db5f30cfff Update changelog 2024-12-12 12:52:49 +00:00
crschnick
12867d1fa3 Manually rebase remote 2024-12-12 12:42:25 +00:00
Quezaquo
fe145d3a26
Update of some lang FR files (#391)
* update: app strings file FR lang

* update: app strings file FR lang

* update: base strings file FR lang

* update: proc strings file FR lang

* update: forgot the custom keyword in app strings file lang FR
2024-12-12 13:36:23 +01:00
crschnick
cc8b74be9d [stage] 2024-12-11 06:05:57 +00:00
crschnick
3d9f5da20d Attempt to fix kitty launch issue 2024-12-11 05:56:43 +00:00
crschnick
df21e005c9 Disable category expand button when it's not needed 2024-12-11 02:21:35 +00:00
crschnick
c7158a9b94 [stage] 2024-12-11 02:10:08 +00:00
crschnick
252b7ca52e [release] 2024-12-05 16:17:15 +00:00
crschnick
aff06ee726 [release] 2024-12-05 14:56:44 +00:00
crschnick
9e8744a1b2 macOS recognition fix 2024-12-05 14:42:01 +00:00
crschnick
6739219e55 macOS detection fixes 2024-12-05 14:15:06 +00:00
crschnick
345ab32c75 Update changelog 2024-12-04 17:07:22 +00:00
crschnick
82253ef4cc [release] 2024-12-04 16:48:29 +00:00
crschnick
8661018619 Merge 13.4-release into master [release] 2024-12-04 13:49:32 +00:00
crschnick
4e88ed2fff [release] 2024-11-26 16:46:26 +00:00
crschnick
22f14ba6d0 [release] 2024-11-26 15:34:52 +00:00
crschnick
4884205df0 Fix wt tab behaviour 2024-11-26 15:29:05 +00:00
crschnick
09f1157ae8 Small fixes [release] 2024-11-26 12:54:22 +00:00
crschnick
68e7abd352 Fixes [release] 2024-11-26 10:13:05 +00:00
crschnick
75c624d3c0 [release] 2024-11-26 09:43:48 +00:00
crschnick
95a0a97909 [release] 2024-11-26 09:41:32 +00:00
crschnick
550b8402b5 Reformat 2024-11-25 16:17:25 +00:00
crschnick
64dc77f4f5 [stage] 2024-11-25 15:29:07 +00:00
crschnick
933bfb9ae2 Fix split tab layout on resize 2024-11-25 15:17:52 +00:00
crschnick
dc9b40acda Tabs sync fixes 2024-11-25 14:03:09 +00:00
crschnick
315d411c1b Small fixes 2024-11-25 13:27:34 +00:00
crschnick
af411fa0de [stage] 2024-11-24 15:35:31 +00:00
crschnick
b18f44eaf5 Rework state and license checks 2024-11-24 14:29:08 +00:00
crschnick
51288823e6 [release] 2024-11-23 11:49:13 +00:00
crschnick
f5640797f0 Reformat 2024-11-23 11:41:36 +00:00
crschnick
04f39cd0a7 Various fixes 2024-11-23 11:35:32 +00:00
crschnick
7f85600b13 Various fixes 2024-11-22 16:42:03 +00:00
crschnick
372dd155f6 [stage] 2024-11-22 11:29:54 +00:00
crschnick
e9be54f015 Support mouse buttons for navigation 2024-11-21 16:28:17 +00:00
crschnick
66b6fae017 Rework shell license check 2024-11-21 11:23:36 +00:00
crschnick
3211896045 Remove link refs 2024-11-21 11:21:26 +00:00
crschnick
9055b56c3d Fix updater [release] 2024-11-20 10:04:29 +00:00
crschnick
8541643fc9 [release] 2024-11-20 09:23:00 +00:00
crschnick
ecdf5119b0 Reformat 2024-11-20 09:22:20 +00:00
crschnick
9b20fe7e8e Improve homebrew check 2024-11-20 09:19:24 +00:00
crschnick
b5a030e615 [stage] 2024-11-20 09:12:52 +00:00
crschnick
c51bb1723e Small fixes 2024-11-20 09:12:39 +00:00
crschnick
bc7d601747 Coreutils fixes 2024-11-20 08:46:25 +00:00
crschnick
7c14e91749 Small fixes 2024-11-20 08:36:09 +00:00
crschnick
9f08b82e7d Improve installation error handling 2024-11-19 16:13:50 +00:00
crschnick
9403ebf204 [stage] 2024-11-19 15:28:09 +00:00
crschnick
7975a735ef Merge branch 'allusers' 2024-11-19 15:04:20 +00:00
crschnick
4693213589 Update readme 2024-11-19 13:02:25 +00:00
crschnick
520b4d4882 Bump deps 2024-11-19 11:24:00 +00:00
crschnick
65a4cc424b Fine tune updater 2024-11-19 11:23:41 +00:00
crschnick
3be6f70272 Improvements for proxmox connections 2024-11-19 10:15:11 +00:00
crschnick
b1d8ec2de9 Update changelog 2024-11-18 15:59:23 +00:00
crschnick
bb0e049835 Improve tab pins 2024-11-18 15:52:30 +00:00
crschnick
82e6b7a035 Add download action 2024-11-18 12:29:54 +00:00
crschnick
3df5a1f697 Various resilience fixes 2024-11-18 11:36:38 +00:00
crschnick
e76371518f Fix NPE [release] 2024-11-16 08:27:04 +00:00
crschnick
57ebe195cf [release] 2024-11-16 07:16:29 +00:00
crschnick
5868fcfd33 Squash merge xpipe 13 feature branches into master 2024-11-16 07:13:24 +00:00
crschnick
6dabe53011 [release] 2024-11-02 01:30:27 +00:00
crschnick
7229f98051 Don't time out on non shown error handler 2024-10-21 18:25:11 +00:00
crschnick
323ca02a43 [release] 2024-10-19 12:46:56 +00:00
crschnick
029fd78580 [release] 2024-10-19 11:54:39 +00:00
crschnick
5e88e84185 Don't run unnecessary tasks 2024-10-18 16:05:56 +00:00
crschnick
4f3e7d2028 [stage] 2024-10-18 15:16:17 +00:00
crschnick
6d748b24e6 Properly work around windows terminal elevation issues 2024-10-18 15:11:58 +00:00
crschnick
aae09c255f [release] 2024-10-17 15:32:57 +00:00
crschnick
01bac9d5fa [release] 2024-10-17 11:31:36 +00:00
crschnick
5f2cce508d [release] 2024-10-17 10:53:27 +00:00
crschnick
4a00ce19ae [release] 2024-10-17 10:11:49 +00:00
crschnick
f0fe0d7a6e Reformat 2024-10-17 09:41:41 +00:00
crschnick
c084a55d15 Logging fixes 2024-10-17 08:21:57 +00:00
crschnick
0c85ba0425 Only open browser after session is started 2024-10-17 08:21:30 +00:00
crschnick
1dd08fce8d Improve report contact field 2024-10-17 08:21:07 +00:00
crschnick
8a964cadad Logging fixes 2024-10-16 14:31:43 +00:00
crschnick
63bef7161a Script adjustments for bsd 2024-10-16 14:13:59 +00:00
crschnick
e80cf7e4b0 Improve windows terminal missing error message 2024-10-16 14:13:52 +00:00
crschnick
411e9c17b6 Update selfhst images 2024-10-16 09:50:22 +00:00
crschnick
2596177ad7 [stage] 2024-10-15 05:46:26 +00:00
crschnick
3f61701132 Expect first shell failure 2024-10-14 05:14:21 +00:00
crschnick
fe7dd6aa71 Make logs a pro feature 2024-10-14 05:14:12 +00:00
crschnick
5346ef2ca5 [stage] 2024-10-13 10:47:00 +00:00
crschnick
5e2ee60ce5 [stage] 2024-10-13 09:13:30 +00:00
crschnick
0b9c62b285 Fix some logging issues 2024-10-13 09:09:25 +00:00
crschnick
51d434c3e0 Document vm guest identities better 2024-10-13 09:09:15 +00:00
crschnick
4b297f1ce8 Fix script command 2024-10-13 08:42:20 +00:00
crschnick
e894390124 Reformat 2024-10-13 08:35:13 +00:00
crschnick
d4ad1d2e82 Add logging 2024-10-13 08:31:37 +00:00
crschnick
bd06633931 Fixes for potential stack overflow 2024-10-13 05:40:45 +00:00
crschnick
62cb424040 Don't set up desktop integration if platform doesn't work 2024-10-13 04:24:26 +00:00
crschnick
9dc5cf8048 Fix for blank paths in file browser 2024-10-13 04:24:10 +00:00
crschnick
593e0eb7b7 [release] 2024-10-11 06:01:13 +00:00
crschnick
a7a3b22dcd Fix no such file exception 2024-10-11 06:00:59 +00:00
crschnick
89885c11b8 Show empty script action in browser 2024-10-10 15:07:20 +00:00
crschnick
2680066c7f Small file transfer fixes [stage] 2024-10-10 06:51:47 +00:00
crschnick
2191ccbfcc [stage] 2024-10-10 05:18:58 +00:00
crschnick
b7d67ba9c5 Small fixes [stage] 2024-10-10 04:10:49 +00:00
crschnick
fd3c6f034c Add keeper password manager template 2024-10-10 04:09:05 +00:00
crschnick
99999eeaec Add way to duplicate files 2024-10-10 04:08:36 +00:00
crschnick
60e0b124db [stage] 2024-10-09 12:30:13 +00:00
crschnick
b35b63708d Add new rename file conflict action 2024-10-09 12:15:27 +00:00
crschnick
7f334c21e9 Fix progress being wrong for files < 1kb 2024-10-09 10:45:33 +00:00
crschnick
aa62161b8f Improve some error messages 2024-10-09 10:27:08 +00:00
crschnick
2db320e1bd FIx macOS fallback shell triggering too often 2024-10-09 10:26:56 +00:00
crschnick
c2427da60d Fix platform preferences exception not being caught 2024-10-09 10:26:27 +00:00
crschnick
a77f1b5fb6 Check if file has been deleted during transfer 2024-10-09 10:26:11 +00:00
crschnick
e7c7886cba Remove drop shadows for tooltips in performance mode 2024-10-09 06:30:16 +00:00
crschnick
10ae05f887 Fix update read key syntax 2024-10-09 06:29:58 +00:00
crschnick
bfd583e325 Report system fallback shell 2024-10-09 06:29:36 +00:00
crschnick
531d019b54 Fix issue with invalid storage dir 2024-10-08 00:38:05 +00:00
crschnick
41786d99cd Fix NPE 2024-10-08 00:22:39 +00:00
crschnick
89b245a27c Update readme 2024-10-07 01:06:59 +00:00
crschnick
30212c6a87 [release] 2024-10-06 23:39:04 +00:00
crschnick
99d867965b Various small fixes 2024-10-06 23:38:50 +00:00
crschnick
b7b3acf168 [stage] 2024-10-06 03:08:28 +00:00
crschnick
6d0bb39786 bsd tar fixes 2024-10-06 03:06:53 +00:00
crschnick
da1944ef3d Fix untar dir missing 2024-10-06 02:53:13 +00:00
crschnick
ae93c49689 Tar fixes 2024-10-06 02:15:47 +00:00
crschnick
830745adbe Rework compress actions 2024-10-06 00:38:26 +00:00
crschnick
f13eac1c75 Small browser fixes 2024-10-06 00:15:08 +00:00
crschnick
8c6333beb9 Add compress action 2024-10-05 02:53:13 +00:00
crschnick
cb413ee358 Rework dist scripts 2024-10-05 02:52:20 +00:00
crschnick
a565c795cf Fix category move updates 2024-10-04 16:53:55 +00:00
crschnick
d7527b40e6 [stage] 2024-10-04 00:40:25 +00:00
crschnick
c72ed034d1 Small fixes 2024-10-04 00:21:38 +00:00
crschnick
be464120fa Category display fixes 2024-10-03 20:51:02 +00:00
crschnick
d0b62d4cbd Update 12.1_incremental.md 2024-10-02 19:00:39 +00:00
crschnick
9d635e1224 [stage] 2024-10-02 18:55:30 +00:00
crschnick
48523690b7 File choice fixes 2024-10-02 18:54:33 +00:00
crschnick
1924bd1644 Fix parallelism carrier thread count 2024-10-02 18:12:42 +00:00
crschnick
3c808810a1 [stage] 2024-10-02 14:35:31 +00:00
crschnick
a97fd10e9c Small fixes 2024-10-02 14:35:03 +00:00
crschnick
09d7d6d0e7 Add port setting for vms 2024-10-02 14:16:32 +00:00
crschnick
0283c6508f Add chooser for git synced key files 2024-10-02 12:06:55 +00:00
crschnick
cac46dc026 Prevent potential NPEs 2024-10-01 16:35:30 +00:00
crschnick
671270ce40 Fix NPE 2024-10-01 16:13:21 +00:00
crschnick
dbea577662 Merge branch icons into master [release] 2024-10-01 10:29:31 +00:00
crschnick
19b341d848 Improve default terminal order 2024-09-15 07:22:27 +00:00
crschnick
c430d9857c Fix vscode it not opening in containers 2024-09-15 07:22:11 +00:00
crschnick
b8a2d80b60 Fix zed translation 2024-09-15 07:21:55 +00:00
crschnick
7b601b8917 Fix downloads move not replacing files 2024-09-14 20:16:22 +00:00
crschnick
02153ad23e [release] 2024-09-13 18:55:49 +00:00
crschnick
5c074615db Data store formatting fixes 2024-09-13 18:55:13 +00:00
crschnick
ccb8888df2 Fix some exceptions 2024-09-13 18:30:10 +00:00
crschnick
feb8cf9165 Don't sleep after macos appearance setting 2024-09-13 03:49:58 +00:00
crschnick
ca1a8e9132 Make window close async 2024-09-13 03:48:43 +00:00
crschnick
6a1efd1981 [stage] 2024-09-12 21:07:32 +00:00
crschnick
aa89cf450e Small fixes 2024-09-12 21:06:23 +00:00
crschnick
1f56eb7e35 Use shell replacement for scripts 2024-09-12 21:06:09 +00:00
crschnick
94b5673565 Fix transfer move^ 2024-09-12 21:05:38 +00:00
crschnick
5a0e12aa13 [stage] 2024-09-11 22:37:15 +00:00
crschnick
5db185168d Translation fixes 2024-09-11 22:32:23 +00:00
crschnick
474606bcb3 Add script quick edit action 2024-09-11 22:32:12 +00:00
crschnick
ba9ead724f Show copy id action when api is enabled 2024-09-11 22:31:45 +00:00
crschnick
c38962ef2e Fix downloads move being unreliable 2024-09-11 22:31:28 +00:00
crschnick
c5f99af866 Powershell fixes 2024-09-10 23:58:42 +00:00
crschnick
eec51705d1 [stage] 2024-09-10 17:31:03 +00:00
crschnick
7d8f9facac [stage] 2024-09-10 00:45:56 +00:00
crschnick
9092c7a509 Update api links 2024-09-10 00:45:41 +00:00
crschnick
83b5b7b2de Powershell fixes 2024-09-10 00:45:11 +00:00
crschnick
29a12cb215 Bump gradle 2024-09-10 00:44:41 +00:00
crschnick
ac9b0ea467 Toggle style fixes 2024-09-09 14:09:42 +00:00
crschnick
db6349201f [stage] 2024-09-08 22:48:44 +00:00
crschnick
1627f30a62 Small fixes 2024-09-08 22:48:25 +00:00
crschnick
f1c5337355 Shell environmentally factoring and default option implementation 2024-09-08 22:07:53 +00:00
crschnick
19086e57d8 Update translations 2024-09-08 15:56:02 +00:00
crschnick
8c60eea4a7 Small browser open fixes 2024-09-08 15:54:20 +00:00
crschnick
439fc4dd03 [release] 2024-09-07 14:04:37 +00:00
crschnick
6602cf26a5 Fixes 2024-09-07 12:38:37 +00:00
crschnick
a3674a12b5 [stage] 2024-09-06 14:33:16 +00:00
crschnick
40597f5527 Various store and ordering fixes 2024-09-06 12:41:26 +00:00
crschnick
a8e8c13f18 Add enable api setting 2024-09-06 12:40:32 +00:00
crschnick
44ba765e30 vnc fixes [stage] 2024-09-05 15:03:37 +00:00
crschnick
44c59d7547 [stage] 2024-09-05 14:13:03 +00:00
crschnick
0daec42954 Some fixes for VNC 2024-09-05 14:12:53 +00:00
crschnick
fa2a23ab36 [stage] 2024-09-04 17:43:26 +00:00
crschnick
849b99002a Various fixes 2024-09-04 17:43:11 +00:00
crschnick
08f0001b9d Various fixed and additions 2024-09-04 08:03:39 +00:00
crschnick
440664e56f Various fixes 2024-09-03 08:30:48 +00:00
crschnick
0a8e87e26a [stage] 2024-09-02 19:33:35 +00:00
crschnick
b8ee5f2349 Fixes 2024-09-02 19:33:25 +00:00
crschnick
1f8aab240d Fixes [stage] 2024-09-02 03:49:20 +00:00
crschnick
64bb401a36 Fixes [stage] 2024-09-02 02:24:33 +00:00
crschnick
9c435c55f4 Always check for store show 2024-08-31 11:33:11 +00:00
crschnick
c7d38843da Fix NPE 2024-08-31 08:21:56 +00:00
crschnick
b8a981eb1f Check for startup when receiving shutdown notice 2024-08-31 08:21:36 +00:00
crschnick
26762d4d80 Ignore invalid users/groups file 2024-08-31 08:21:01 +00:00
crschnick
89e0b74606 Fix readme links 2024-08-30 15:56:05 +00:00
crschnick
0c1f63d3c9 Jump to only filter result left 2024-08-30 02:49:20 +00:00
crschnick
9529dceb5c [release] 2024-08-29 07:42:54 +00:00
crschnick
a4a7a1ee86 Fix linux term fallback 2024-08-29 07:12:58 +00:00
crschnick
aa552f5837 Fixes 2024-08-29 04:51:33 +00:00
crschnick
ac2c97301f Fixes 2024-08-29 04:23:45 +00:00
crschnick
2d1e5298a9 Termius fixes 2024-08-29 03:12:17 +00:00
crschnick
13c9644ff5 Term fixes 2024-08-29 02:54:07 +00:00
crschnick
82b501d4cc Fix term fallback 2024-08-29 02:09:34 +00:00
crschnick
ec258a1eaa Fix for sh shells 2024-08-29 01:23:39 +00:00
crschnick
4f2ccea254 Terminal fixes 2024-08-29 00:30:16 +00:00
crschnick
7d1b02bb2f [stage] 2024-08-28 23:49:11 +00:00
crschnick
89eba44c9d Fix alert size 2024-08-28 23:41:56 +00:00
crschnick
3dab6a3d18 Reset scroll 2024-08-28 23:41:33 +00:00
crschnick
6cc0771974 Fix intro show condition 2024-08-28 23:11:16 +00:00
crschnick
3a700643f0 Fix owner info NPEs 2024-08-28 23:10:30 +00:00
crschnick
2831e8372a Fix ssh bridge issues 2024-08-28 23:09:28 +00:00
crschnick
6cb9e1e553 [stage] 2024-08-27 23:46:43 +00:00
crschnick
473fcfe843 Fix git sync issues 2024-08-27 23:38:24 +00:00
crschnick
c06e6032c9 Fix OOB 2024-08-27 23:37:57 +00:00
crschnick
e20a59c15f Add disable encryption check 2024-08-27 23:37:23 +00:00
crschnick
a49077cc09 macOS fixes 2024-08-27 15:30:57 +00:00
crschnick
14867ba09c [release] 2024-08-27 14:05:49 +00:00
crschnick
c1b5fbfa5f Various fixes 2024-08-27 13:23:41 +00:00
crschnick
542e535e42 Small fixes 2024-08-26 21:52:40 +00:00
crschnick
44c2f5db87 [stage] 2024-08-26 18:56:58 +00:00
crschnick
38f18df272 Various fixes 2024-08-26 18:56:45 +00:00
crschnick
54f47a5a1f [stage] 2024-08-25 23:27:16 +00:00
crschnick
030faaa601 Various fixes 2024-08-25 23:27:00 +00:00
crschnick
6c21529789 Rework license names 2024-08-25 19:56:05 +00:00
crschnick
7aa92b437f Bump jfx dev version 2024-08-25 12:47:01 +00:00
crschnick
6044cd3ead Rename share to sync 2024-08-25 12:46:47 +00:00
crschnick
41d419f19d mobaxterm fixes 2024-08-24 21:58:10 +00:00
crschnick
63dba2d33c [stage] 2024-08-24 18:46:53 +00:00
crschnick
8ad12ff713 Various fixes 2024-08-24 18:46:43 +00:00
crschnick
27e6fc10e0 Reformat 2024-08-24 12:42:06 +00:00
crschnick
d6cb3bf2bd Various fixes 2024-08-24 12:40:00 +00:00
crschnick
4adb18249b [stage] 2024-08-23 19:32:07 +00:00
crschnick
7992ef7a64 Parse legacy colors 2024-08-23 19:31:07 +00:00
crschnick
3ca8db6ed4 Text field fixes 2024-08-23 19:30:56 +00:00
crschnick
be684d7b72 Rework categories 2024-08-22 17:34:36 +00:00
crschnick
a65a0bd1b0 Fix api os deserialization 2024-08-21 21:55:16 +00:00
crschnick
d92b870908 Make categories expendable 2024-08-21 15:20:58 +00:00
crschnick
417115c527 [stage] 2024-08-21 13:51:05 +00:00
crschnick
ae2a2d10eb Various fixes [stage] 2024-08-21 13:04:55 +00:00
crschnick
c9a07dd061 Various small fixes [stage] 2024-08-19 16:29:21 +00:00
crschnick
52eb584c9c [stage] 2024-08-19 13:05:35 +00:00
crschnick
26ef089c44 Small fixes 2024-08-19 13:05:26 +00:00
crschnick
a08dba4d06 Various fixes 2024-08-19 11:22:37 +00:00
crschnick
4f1ddc6634 Update changelog 2024-08-18 15:12:26 +00:00
crschnick
7e3ac0cf2c Small fixes [stage] 2024-08-18 11:24:14 +00:00
crschnick
b5471e52d1 Browser fixes 2024-08-18 10:23:44 +00:00
crschnick
ef5427f046 Show owner information for files 2024-08-17 14:27:59 +00:00
crschnick
1ebc60cb71 Reformat 2024-08-16 10:57:17 +00:00
crschnick
6a946dbc5a ssh bridge fixes 2024-08-16 10:24:45 +00:00
crschnick
749f3e706e Fix build 2024-08-16 10:23:49 +00:00
crschnick
4d1eadf712 Readd window pos fix 2024-08-16 10:23:13 +00:00
crschnick
ab7e2f2220 Bump versions [stage] 2024-08-15 15:22:14 +00:00
crschnick
4ac19f24e1 [stage] 2024-08-15 10:35:50 +00:00
crschnick
cb7ecd26af Fix possible browser freeze 2024-08-15 10:34:58 +00:00
crschnick
ee78d152c5 Fix double open 2024-08-15 10:34:44 +00:00
crschnick
79a18260aa Fix translation 2024-08-15 10:27:52 +00:00
crschnick
d9bc91120e [stage] 2024-08-15 09:46:45 +00:00
crschnick
5cda797d5a Bold macos fixes 2024-08-15 09:36:16 +00:00
crschnick
9f597b1b06 Small fixes 2024-08-15 09:30:48 +00:00
crschnick
654fd3ae45 [stage] 2024-08-14 15:31:11 +00:00
crschnick
eb178350dd Small fixes 2024-08-14 15:31:03 +00:00
crschnick
0ff69602e9 [stage] 2024-08-14 14:40:38 +00:00
crschnick
ac221b3b85 Various fixes 2024-08-14 12:29:14 +00:00
crschnick
51121d2301 Rework data encryption for raw data 2024-08-13 17:04:08 +00:00
crschnick
da42eb578f More terminal launcher rework 2024-08-13 14:48:56 +00:00
crschnick
20206b6263 Implement basic ssh bridge 2024-08-12 17:49:55 +00:00
crschnick
41f71d45a7 Rework launchable stores 2024-08-12 07:10:46 +00:00
crschnick
9b7cca8589 Small fixes 2024-08-11 09:22:51 +00:00
crschnick
0ad8df23b6 Small fixes [stage] 2024-08-11 08:40:32 +00:00
crschnick
af71e3015d More teleport fixes 2024-08-11 08:21:25 +00:00
crschnick
7dc98b42ca [stage] 2024-08-10 17:28:24 +00:00
crschnick
cd0df90dd4 Rework state style 2024-08-10 17:26:14 +00:00
crschnick
ced43f728b Add teleport support [stage] 2024-08-10 14:52:15 +00:00
crschnick
e00ff07775 Format more aws hostnames 2024-08-10 14:51:16 +00:00
crschnick
860ae0ee60 Fix NPE 2024-08-10 14:11:19 +00:00
crschnick
fbd1f1c5ce Small fixes [stage] 2024-08-10 11:36:49 +00:00
crschnick
de03207d90 Fixes [stage] 2024-08-10 10:50:18 +00:00
crschnick
1caa6cad6b More shell rework 2024-08-10 09:38:55 +00:00
crschnick
99971caba5 Various fixes 2024-08-09 15:38:20 +00:00
crschnick
9860b0c10f Merge branch no-stderr into master 2024-08-09 07:38:47 +00:00
crschnick
65b2be5709 Merge branch serial into master 2024-08-09 07:28:43 +00:00
crschnick
a26917b500 Small reliability fixes 2024-08-04 02:53:55 +00:00
crschnick
675fb6972a Fixes for bsd 2024-08-03 08:19:33 +00:00
crschnick
081dced632 Handle bsd better 2024-08-03 07:56:34 +00:00
crschnick
1140267809 Update changelog 2024-07-30 00:15:09 +00:00
crschnick
3996d330a6 Fix possible NPE 2024-07-30 00:14:51 +00:00
crschnick
ebedd332bb Fix DirectoryNotEmptyException on downloads move 2024-07-29 21:47:24 +00:00
crschnick
65f51b8c2e Script hierarchy fixes 2024-07-29 11:48:11 +00:00
crschnick
35704db4c5 [stage] 2024-07-28 20:27:48 +00:00
crschnick
b2a988ac06 Script fixes 2024-07-28 20:27:37 +00:00
crschnick
4640e10ac0 Enable zgc 2024-07-28 19:48:33 +00:00
crschnick
544dc85e6f Script rework 2024-07-28 19:48:24 +00:00
crschnick
0014bf6165 Script improvements 2024-07-27 21:48:48 +00:00
crschnick
ccb018a522 Style fixes 2024-07-27 10:55:04 +00:00
crschnick
0ee5ee2d88 [release] 2024-07-26 17:01:02 +00:00
crschnick
f580f39e86 Input order fixes [release] 2024-07-26 16:58:53 +00:00
crschnick
3357c73aea Input fixes [release] 2024-07-26 16:25:04 +00:00
crschnick
f0a91e09c5 [release] 2024-07-26 16:02:58 +00:00
crschnick
bdc0b1352a [stage] 2024-07-26 15:04:10 +00:00
crschnick
f04322d30d [stage] 2024-07-26 14:19:30 +00:00
crschnick
97d9e7e6a1 Input fixes 2024-07-26 13:49:36 +00:00
crschnick
8ea0f98ae9 Handled potential native errors better 2024-07-26 13:18:45 +00:00
crschnick
4a84c07c56 Fix double click setting 2024-07-26 08:46:16 +00:00
crschnick
bfee4d8edc [release] 2024-07-26 06:37:18 +00:00
crschnick
aaa0c199fd Final fixes [release] 2024-07-26 05:14:13 +00:00
crschnick
5e1b5fa0d2 [stage] 2024-07-25 15:13:49 +00:00
crschnick
6fda1170b5 Disable tray for linux 2024-07-25 14:55:03 +00:00
crschnick
bbbd8f353a [stage] 2024-07-25 10:22:24 +00:00
crschnick
0426d83bef Input fixes 2024-07-25 09:03:18 +00:00
crschnick
b0b7c7e859 [stage] 2024-07-25 07:03:42 +00:00
crschnick
a4b7d42f83 Fix typed selection 2024-07-25 07:01:50 +00:00
crschnick
17069267da Reformat 2024-07-25 07:01:40 +00:00
crschnick
54f27a274f Fix first character being ignored on typed selection [stage] 2024-07-25 05:39:04 +00:00
crschnick
f11b76fdff [stage] 2024-07-25 04:21:10 +00:00
crschnick
7f62c94881 Improve browser chooser history 2024-07-25 04:20:00 +00:00
crschnick
68cf68ea70 Fix macos display name for preview versions 2024-07-25 03:44:00 +00:00
crschnick
da15533220 Apply window maximize state 2024-07-25 03:43:37 +00:00
crschnick
f15391a1a7 Browser fixes 2024-07-25 03:43:18 +00:00
crschnick
dc93536be9 Add rosetta check 2024-07-24 11:18:13 +00:00
crschnick
2ca2eefb29 Rename display name 2024-07-24 11:04:21 +00:00
crschnick
d8d62c0eff Rework updater 2024-07-24 11:03:51 +00:00
crschnick
2f4d72c63f Improve display names 2024-07-24 03:52:53 +00:00
crschnick
d6b1d78e6e Add macos native wait 2024-07-24 03:21:24 +00:00
crschnick
37de0f30c6 [stage] 2024-07-24 01:28:13 +00:00
crschnick
af77a09bd7 File browser busy fixes 2024-07-24 01:28:05 +00:00
crschnick
382b07f751 Update README 2024-07-23 12:26:12 +00:00
crschnick
4f1e6ea5f1 Fix orphaned connections on load 2024-07-23 11:45:33 +00:00
crschnick
167d1c1da2 Sync download change listeners 2024-07-23 06:18:59 +00:00
crschnick
490bd7953b Bump deps 2024-07-23 06:18:46 +00:00
crschnick
d4da792e49 Style fixes 2024-07-23 01:26:45 +00:00
crschnick
3e183a22f4 Style fix 2024-07-23 01:01:56 +00:00
crschnick
3bf851e457 Style fixes 2024-07-23 00:55:47 +00:00
crschnick
3c722ffcd1 Style fixes 2024-07-23 00:14:49 +00:00
crschnick
e5dbee813c Fix NPE [stage] 2024-07-22 17:35:17 +00:00
crschnick
d223c2af75 Vnc auth fixes [stage] 2024-07-22 09:55:28 +00:00
crschnick
e7611caf78 [stage] 2024-07-22 04:12:21 +00:00
crschnick
e2957a1c67 Small fixes 2024-07-22 03:50:03 +00:00
crschnick
eef991c031 Small fixes 2024-07-22 03:48:03 +00:00
crschnick
227559a7df macos dist fixes 2024-07-22 03:08:45 +00:00
crschnick
97ff085e15 Linux style fixes 2024-07-22 02:54:01 +00:00
crschnick
65ba63aac2 [stage] 2024-07-22 02:06:13 +00:00
crschnick
12e2679692 Rework service description 2024-07-22 01:42:42 +00:00
crschnick
5b01540c98 Build fixes 2024-07-22 01:42:35 +00:00
crschnick
bec339b7b3 Bump deps 2024-07-21 22:47:10 +00:00
crschnick
981c51e405 File browser fixes 2024-07-21 04:31:33 +00:00
crschnick
5748bb4bfc Add foot terminal translation 2024-07-21 02:25:14 +00:00
crschnick
15fc94a15e [stage] 2024-07-21 02:19:42 +00:00
crschnick
fbf307d0bd Various fixes 2024-07-21 02:19:19 +00:00
crschnick
031ff66387 Add more browser tab actions 2024-07-21 00:16:02 +00:00
crschnick
1061d407e5 Browser fixes 2024-07-20 16:04:04 +00:00
crschnick
68e29efd1a Service open fixes 2024-07-20 13:10:27 +00:00
crschnick
6887f9b4ca Build fix [stage] 2024-07-19 17:45:53 +00:00
crschnick
2a2ffb408a [stage] 2024-07-19 17:17:37 +00:00
crschnick
914a541ed8 Bump vnc lib 2024-07-19 17:17:19 +00:00
crschnick
99b16a59e6 Fix sha256sums containing mapping file 2024-07-19 17:17:03 +00:00
crschnick
bc5ed101ed Style fixes 2024-07-19 17:16:39 +00:00
crschnick
4d7fcbbef0 Fix browser keys not being captured 2024-07-19 17:16:29 +00:00
crschnick
74d1e5fb7e Build fixes [stage] 2024-07-18 19:40:12 +00:00
crschnick
1c791160c7 Fix possible provider NPE 2024-07-18 18:47:30 +00:00
crschnick
9f27f0837d [stage] 2024-07-18 16:55:37 +00:00
crschnick
45c3ba17d5 Reliability fixes 2024-07-18 16:55:20 +00:00
crschnick
708c2d0c3f Style fixes 2024-07-18 16:34:07 +00:00
crschnick
0632875c7d Style fixes 2024-07-18 15:10:32 +00:00
crschnick
64f58d37a4 Merge branch mac-window into master 2024-07-18 13:47:21 +00:00
crschnick
0e62af8817 [stage] 2024-07-17 22:41:08 +00:00
crschnick
c6b304f68a Add changelogs 2024-07-17 22:39:17 +00:00
crschnick
e9d9152d23 Style fixes 2024-07-17 22:36:08 +00:00
crschnick
58533aba5b Fixes for constrained powershell 2024-07-17 12:45:21 +00:00
crschnick
f3f63aa0f7 Maven publish fixes 2024-07-17 09:24:42 +00:00
crschnick
adf98c5a8c SIgning fixes 2024-07-16 12:23:22 +00:00
crschnick
d4ae4abe61 Small fixes 2024-07-16 11:39:14 +00:00
crschnick
44d33d32f4 Fix file updates being checked against wrong timestamp (#300) 2024-07-16 09:58:45 +00:00
crschnick
f9008aa736 [stage] 2024-07-15 22:42:27 +00:00
crschnick
e1f84d4a6b Rework ui size 2024-07-15 20:58:04 +00:00
crschnick
43524d8101 Expect more exceptions 2024-07-15 19:01:43 +00:00
crschnick
2d07615f5e Rework information string for services 2024-07-15 19:01:06 +00:00
crschnick
d67e2c813d Various fixes 2024-07-14 19:02:27 +00:00
crschnick
26eff90c2b [release] 2024-07-14 10:24:37 +00:00
crschnick
37879f034b Prevent empty drag 2024-07-14 10:08:12 +00:00
crschnick
99adb0866f Rework unlock alert 2024-07-14 08:19:17 +00:00
crschnick
21b7e063aa Small fixes 2024-07-14 08:19:00 +00:00
crschnick
972f106cb0 Restrict auth file permission 2024-07-14 08:17:53 +00:00
crschnick
47bfbcbce5 Various fixes 2024-07-13 13:09:43 +00:00
crschnick
a59a2275f3 [stage] 2024-07-13 10:24:17 +00:00
crschnick
4abe8e81d4 Small fixes 2024-07-13 10:24:07 +00:00
crschnick
3f10abdea8 [stage] 2024-07-13 08:07:33 +00:00
crschnick
73e61f48bd Move ptb auth file 2024-07-13 06:56:22 +00:00
crschnick
ceee5d56b1 Filter beacon responses 2024-07-13 06:44:05 +00:00
crschnick
0527dbee39 [stage] 2024-07-12 13:51:59 +00:00
crschnick
67762a976b Browser timeout fixes 2024-07-12 13:51:33 +00:00
crschnick
18b6fa0338 [stage] 2024-07-12 09:26:41 +00:00
crschnick
a1d564a947 Fix file bridge issues on very fast writes 2024-07-12 09:16:09 +00:00
crschnick
6541a84972 Check for occupied beacon 2024-07-11 05:39:05 +00:00
crschnick
ac07f21fd4 Add support for zed 2024-07-10 23:57:56 +00:00
crschnick
53c36cd4c2 [stage] 2024-07-10 04:43:47 +00:00
crschnick
4a2d8cc9b0 Add typed selection model 2024-07-10 04:43:30 +00:00
crschnick
fdbfcb6ddc File browser improvements 2024-07-10 04:20:55 +00:00
crschnick
1893464432 Multi user fixes 2024-07-10 01:00:56 +00:00
crschnick
26c911c057 Rework file downloads 2024-07-09 14:04:27 +00:00
crschnick
4c64eb5ae5 [stage] 2024-07-09 14:03:56 +00:00
crschnick
9f232aa7d1 Check file transfer input before writing 2024-07-09 09:06:46 +00:00
crschnick
43a512bb30 Remove slack 2024-07-09 02:51:48 +00:00
crschnick
70ba263ec4 Small fixes 2024-07-08 09:14:42 +00:00
crschnick
935e28190c [stage] 2024-07-07 02:28:28 +00:00
crschnick
9cf9340aca Prefs fixes 2024-07-07 02:27:20 +00:00
crschnick
8090c09e0c Various fixes 2024-07-07 01:39:37 +00:00
crschnick
8e8b9c5330 [stage] 2024-07-06 17:42:55 +00:00
crschnick
ddac4cd402 Various fixes 2024-07-06 17:28:42 +00:00
crschnick
1af837b02e [stage] 2024-07-05 22:20:43 +00:00
crschnick
9b20a5019f Various fixes 2024-07-05 22:20:26 +00:00
crschnick
235f90ad86 [stage] 2024-07-05 19:43:57 +00:00
crschnick
5e8ddb881d Style fixes 2024-07-05 19:43:40 +00:00
crschnick
f5edcb69d3 Various fixes 2024-07-05 15:31:52 +00:00
crschnick
bbe3f09837 Rework transparency style 2024-07-05 13:00:16 +00:00
crschnick
2fb2ce35a0 Rework storage error handling 2024-07-04 20:14:18 +00:00
crschnick
8ac507a3b5 Update sidebar style 2024-07-04 20:13:37 +00:00
crschnick
c1b2086e90 Rework filter field 2024-07-04 20:13:20 +00:00
crschnick
3ef26d0b0b Small fixes 2024-07-04 20:11:38 +00:00
crschnick
cc88a0594c Update version spec 2024-07-04 13:27:18 +00:00
crschnick
c83b627307 api rework [stage] 2024-07-04 12:08:58 +00:00
crschnick
83a616916e [stage] 2024-07-03 23:51:40 +00:00
crschnick
547b335410 Api updates 2024-07-03 23:51:18 +00:00
crschnick
1172e0a949 Update readme 2024-07-03 16:59:28 +00:00
crschnick
08722f3699 [release] 2024-07-02 13:16:41 +00:00
crschnick
beb2fff315 Improve storage loading errors 2024-07-02 13:06:01 +00:00
crschnick
ca4048570a Only use upload painter on windows 2024-07-02 13:05:43 +00:00
crschnick
ddefa34167 [stage] 2024-07-02 11:19:40 +00:00
crschnick
0c5617f9a0 [stage] 2024-07-02 11:07:51 +00:00
crschnick
7960d9c6ae Use uploading painter 2024-07-02 11:06:45 +00:00
crschnick
a75a8e1cd7 Postgres fixes 2024-07-02 11:05:33 +00:00
crschnick
d2b509e32d [release] [noannounce] 2024-07-01 16:07:17 +00:00
crschnick
3b4aed5821 [release] [noannounce] 2024-07-01 15:29:05 +00:00
crschnick
30d28b7a19 Check service group parent type 2024-07-01 15:10:07 +00:00
crschnick
0329ee614c Bump jreleaser 2024-07-01 15:09:32 +00:00
crschnick
f6a112510c Rework launcher error handling 2024-07-01 15:09:20 +00:00
crschnick
e26240ac1f Make store wrapper more resilient 2024-07-01 15:08:01 +00:00
crschnick
0620fe1d2a Fix stage style blank screen issue 2024-07-01 15:07:33 +00:00
crschnick
21b6d71bf7 [release] [noannounce] 2024-06-30 16:41:00 +00:00
crschnick
8df1cf3ca0 [stage] 2024-06-30 15:46:35 +00:00
crschnick
b5618f08a4 [stage] 2024-06-30 15:33:01 +00:00
crschnick
ce5b943380 Fix NPE 2024-06-30 15:29:43 +00:00
crschnick
fe81900429 [stage] 2024-06-30 15:24:12 +00:00
crschnick
eac19b73ae [stage] 2024-06-30 15:20:41 +00:00
crschnick
32c3f42aaa Brew fixes 2024-06-30 14:56:52 +00:00
crschnick
d2aa28cfe4 Fix NPE 2024-06-30 12:56:51 +00:00
crschnick
30f750d8f1 Kitty fixes 2024-06-30 12:56:38 +00:00
crschnick
61c3547497 Sorting fixes 2024-06-30 11:12:33 +00:00
crschnick
fa90137e39 Merge branch 'order-improvements' 2024-06-30 11:12:21 +00:00
crschnick
c986030e8e Bump javafx 2024-06-30 10:12:15 +00:00
crschnick
a18d94fb8d Expect custom script failures 2024-06-30 10:04:59 +00:00
crschnick
abbb8a0fcd Fix file list resize 2024-06-30 09:52:55 +00:00
crschnick
4b502aef74 Fix NPE properly [release] [noannounce] 2024-06-29 14:02:13 +00:00
crschnick
e6a7afd78d [release] [noannounce] 2024-06-29 14:00:52 +00:00
crschnick
e6b6d9b325 Command read rework 2024-06-29 13:35:38 +00:00
crschnick
f6c06b38c3 Rename fixes 2024-06-29 12:59:41 +00:00
crschnick
fd848e26c1 Fix some key not working when renaming files 2024-06-29 10:04:30 +00:00
crschnick
da6caf0c78 Fix NPEs 2024-06-29 09:46:14 +00:00
crschnick
d10397495e Final fixes [release] [noannounce] 2024-06-29 06:50:01 +00:00
crschnick
5901a9ac5d [stage] 2024-06-28 18:44:48 +00:00
crschnick
d6d5c8162f Small fixes 2024-06-28 18:44:40 +00:00
crschnick
d2c56c0acd [stage] 2024-06-28 13:12:10 +00:00
crschnick
5ac45dce41 Various fixes before release 2024-06-28 09:49:15 +00:00
crschnick
bc6a25972d Various fixes 2024-06-28 04:00:09 +00:00
crschnick
aa91e263ca Various fixes 2024-06-27 15:15:43 +00:00
crschnick
5ca15b2af6 Small fixes [stage] 2024-06-27 09:34:53 +00:00
crschnick
2df431ae79 Cleanup 2024-06-27 06:35:29 +00:00
crschnick
1ff39b2182 Small fixes [stage] 2024-06-27 06:28:23 +00:00
crschnick
99056e924c Various fixes 2024-06-27 05:03:38 +00:00
crschnick
9d4903e665 Various fixes [stage] 2024-06-26 12:16:06 +00:00
crschnick
7f0d9746d1 Various fixes [stage] 2024-06-26 06:39:47 +00:00
crschnick
5f01430592 Various fixes 2024-06-26 02:45:16 +00:00
crschnick
95b716293f Style improvements 2024-06-25 09:20:00 +00:00
crschnick
dd7c48883f [stage] 2024-06-25 08:00:19 +00:00
crschnick
f430210519 [stage] 2024-06-25 07:56:40 +00:00
crschnick
06a7ef27b5 Reformat 2024-06-25 07:56:30 +00:00
crschnick
dacd24a8e5 api fixes 2024-06-25 07:55:16 +00:00
crschnick
7dc13a9a26 ps session fixes 2024-06-25 05:26:30 +00:00
crschnick
c3334c18b5 [stage] 2024-06-25 03:01:01 +00:00
crschnick
0de7d36c4f Various fixes 2024-06-25 03:00:24 +00:00
crschnick
2d636de52a Small fixes 2024-06-24 02:20:21 +00:00
crschnick
36379174fa [stage] 2024-06-23 23:26:03 +00:00
crschnick
dbdcd590ad Style fixes 2024-06-23 23:21:25 +00:00
crschnick
140040468a Style fixes 2024-06-23 23:08:26 +00:00
crschnick
eeeef57b8f Reformat 2024-06-23 22:40:50 +00:00
crschnick
d1b506415c Shortcut fixes 2024-06-23 22:39:23 +00:00
crschnick
65fbe13113 Small fixes 2024-06-23 21:57:27 +00:00
crschnick
382532efb3 Style fixes 2024-06-23 08:26:27 +00:00
crschnick
9e62de47da Style fixes 2024-06-23 07:47:25 +00:00
crschnick
6cc7fa180c Browser UI rework 2024-06-23 05:38:44 +00:00
crschnick
93eb1075ac Build fixes 2024-06-22 01:46:15 +00:00
crschnick
f465e19edc [stage] 2024-06-21 23:26:32 +00:00
crschnick
1e8699c4fb Various fixes 2024-06-21 23:26:24 +00:00
crschnick
de3179f968 [stage] 2024-06-21 18:22:33 +00:00
crschnick
4f3f2bbfd2 Various fixes 2024-06-21 18:20:35 +00:00
crschnick
2d67443f22 Style fixes 2024-06-21 01:43:06 +00:00
crschnick
5e90f169b1 Small fixes 2024-06-21 01:07:48 +00:00
crschnick
33302fb75d [stage] 2024-06-20 21:28:16 +00:00
crschnick
02b979de6b Blob rework 2024-06-20 21:25:28 +00:00
crschnick
0587dea4ac Window fixes 2024-06-20 18:33:51 +00:00
crschnick
05bb34eef5 Style fixes [stage] 2024-06-20 02:14:57 +00:00
crschnick
1611ed2743 Small fixes 2024-06-20 01:23:57 +00:00
crschnick
986c3299de Style fixes 2024-06-19 22:00:52 +00:00
crschnick
4c21c6a8c2 [stage] 2024-06-19 20:37:32 +00:00
crschnick
4d43cc5fd0 Merge branch native-window into master 2024-06-19 20:26:19 +00:00
crschnick
914f724577 Small fixes 2024-06-19 16:00:45 +00:00
crschnick
1f7174db86 Fixes for the data dir, remove store ids, update api doc [stage] 2024-06-18 22:26:16 +00:00
crschnick
734fac9af6 Add double click option 2024-06-18 20:23:40 +00:00
crschnick
20093becf3 Api fixes, order fixes [stage] 2024-06-18 17:32:10 +00:00
crschnick
adb621dab4 Add interactive check and rework api [stage] 2024-06-18 00:20:45 +00:00
crschnick
c5608bd23c api rework [stage] 2024-06-17 17:51:36 +00:00
crschnick
33577ca7c1 More beacon fixes 2024-06-17 12:28:37 +00:00
crschnick
2810dc4372 Apply api changes 2024-06-17 11:38:30 +00:00
crschnick
2b684c9e5e api fixes 2024-06-17 11:36:55 +00:00
crschnick
1702d716cd Fix openapi inheritance 2024-06-17 00:51:23 +00:00
crschnick
af4dd0602a [stage] 2024-06-16 22:47:48 +00:00
crschnick
be6ed54afd Fix threading for beacon 2024-06-16 22:16:41 +00:00
crschnick
c2e3caa63d Quote openapi response codes 2024-06-16 15:01:57 +00:00
crschnick
f370c5f6cf Fixes [stage] 2024-06-16 14:47:31 +00:00
crschnick
9090c128e9 Fixes [stage] 2024-06-15 19:18:36 +00:00
crschnick
4bf33d7e59 Fixes [stage] 2024-06-15 16:27:37 +00:00
crschnick
f675650701 Fixes [stage] 2024-06-15 15:39:20 +00:00
crschnick
7b6d4d4dbf Fixes [stage] 2024-06-15 13:52:48 +00:00
crschnick
368612b70f Fix install script for macos 2024-06-15 13:16:51 +00:00
crschnick
8f49c35aca Reformat [stage] 2024-06-15 11:10:39 +00:00
crschnick
5ce9538633 Various fixes 2024-06-15 10:41:14 +00:00
crschnick
4426eb2424 Merge branch release-10.0 into master 2024-06-15 10:40:03 +00:00
crschnick
bc9b962be9 Ordering fixes 2024-05-30 10:08:27 +00:00
crschnick
ae02fb3791 Hook up update observables 2024-05-30 07:50:21 +00:00
crschnick
ba7c83a1e8 Rework list bindings 2024-05-29 14:25:45 +00:00
crschnick
e14b38b31f Rework states 2024-05-29 05:41:29 +00:00
crschnick
6f6b4a76d4 Implement basic ordering 2024-05-28 12:05:22 +00:00
crschnick
f3557bb715 Rework store visibility updates 2024-05-28 06:10:00 +00:00
crschnick
e6b70ff60c Prepare for docker contexts 2024-05-28 01:58:33 +00:00
crschnick
7fb54d4fe1 Bump vnc lib 2024-05-27 23:44:15 +00:00
crschnick
da94bf250a Small fixes 2024-05-27 21:34:08 +00:00
crschnick
03cd25106c Vault secret rewrite fixes 2024-05-27 21:33:58 +00:00
crschnick
9ad3e1975e Test fixes 2024-05-27 21:15:19 +00:00
crschnick
a5309f3614 Fix possibility of selecting own children as hosts 2024-05-27 16:52:33 +00:00
crschnick
9c565de56b Fix expand button being enabled with no children shown 2024-05-27 16:52:15 +00:00
crschnick
d62047d56b macOS start/restart fixes 2024-05-26 20:43:55 +00:00
crschnick
ec5342e50f Don't clear legacy directories anymore 2024-05-26 20:33:40 +00:00
crschnick
73524d471f Dist fixes 2024-05-26 20:33:30 +00:00
crschnick
4d9e47b821 Fix launcher input not triggering on startup 2024-05-26 20:32:58 +00:00
crschnick
8e8f10676a [stage] 2024-05-25 20:33:11 +00:00
crschnick
ad7b001f1a Various fixes 2024-05-25 20:33:00 +00:00
crschnick
05c7028e1e Rework registry handling 2024-05-25 19:30:21 +00:00
crschnick
4ea51c1cf4 Various fixes 2024-05-25 16:19:54 +00:00
crschnick
80e487d0aa Small fixes [stage] 2024-05-24 18:34:03 +00:00
crschnick
a7d825be67 File transfer fixes 2024-05-24 17:54:54 +00:00
crschnick
d41b3017f6 Property fixes 2024-05-23 23:44:51 +00:00
crschnick
54f74c140f Test fixes 2024-05-23 23:41:20 +00:00
crschnick
c502e0f1b1 Reorder preferred terminals 2024-05-23 19:47:12 +00:00
crschnick
ed5e3631c8 Build fixes [stage] 2024-05-23 19:01:18 +00:00
crschnick
8383c9f082 [stage] 2024-05-23 18:09:21 +00:00
crschnick
5d45d9c25b Small fixes 2024-05-22 06:31:04 +00:00
crschnick
56087fec1c Markdown fixes 2024-05-21 15:15:01 +00:00
crschnick
f19aed4e0e Improve update alert 2024-05-20 23:45:31 +00:00
crschnick
202dd0a876 Add simple rdp connections and fix some lang tags 2024-05-20 22:22:20 +00:00
crschnick
07caa29262 Improve file transfer resilience 2024-05-20 10:31:29 +00:00
crschnick
7c6906d86e Finish new item action on enter 2024-05-20 07:18:08 +00:00
crschnick
89776bb6da Storage fixes 2024-05-20 07:10:39 +00:00
crschnick
4ed6f05fbd Notes fixes 2024-05-20 05:08:45 +00:00
crschnick
28bbaaf53e Notes improvements 2024-05-20 03:34:33 +00:00
crschnick
7de6a51d73 Add connection notes 2024-05-20 01:36:56 +00:00
crschnick
ef22578d71 [release] 2024-05-18 13:33:36 +00:00
crschnick
79d5dc370a Add installer message 2024-05-18 13:02:39 +00:00
crschnick
90e1b94944 [stage] 2024-05-17 21:37:42 +00:00
crschnick
675c2e9d1e [stage] 2024-05-17 18:46:31 +00:00
crschnick
4a351f8598 Improve handling of directories when permissions are missing 2024-05-17 18:44:49 +00:00
crschnick
a1575f4042 Fix NPE in temp check 2024-05-17 15:47:14 +00:00
crschnick
8bc5ea0688 Final window center adjustments 2024-05-17 14:59:44 +00:00
crschnick
b63f899a86 More window center fixes 2024-05-17 14:45:51 +00:00
crschnick
ff9305da3c [stage] 2024-05-17 13:15:56 +00:00
crschnick
c05d945694 Fix window y centering 2024-05-17 12:46:53 +00:00
crschnick
374a98d22d More window centering fixes 2024-05-17 12:37:36 +00:00
crschnick
54e1e6369c Rework window centering again 2024-05-17 12:23:18 +00:00
crschnick
59ce98d9db Window centering fixes 2024-05-17 10:57:59 +00:00
crschnick
6345be78a7 Focus finder on details open 2024-05-16 16:10:02 +00:00
crschnick
856322e052 Fixes for subshell elevation 2024-05-16 13:56:41 +00:00
crschnick
b36ca8f557 Fix error handler with empty details 2024-05-16 11:14:32 +00:00
crschnick
b0491bbd66 Make password manager command a text area 2024-05-16 09:59:49 +00:00
crschnick
bdce4576d4 Add support for windows terminal canary 2024-05-16 08:26:27 +00:00
crschnick
76207cb7cf Git readme improvements 2024-05-15 17:56:27 +00:00
crschnick
c73b7de9a6 Fix NPE 2024-05-15 09:19:16 +00:00
crschnick
f4f3f21a70 Synchronize fs close 2024-05-15 09:01:04 +00:00
crschnick
6dd0e6d255 Window centering improvements 2024-05-14 08:53:12 +00:00
crschnick
d77925540e Prevent flickering when opening the same system in chooser 2024-05-14 08:23:51 +00:00
crschnick
d155dcdeb2 Fix NPE 2024-05-14 08:23:36 +00:00
crschnick
a09a3238cf Fix OOB 2024-05-14 08:23:22 +00:00
crschnick
69dd83e2e4 Delay window centering 2024-05-13 17:23:22 +00:00
crschnick
fd282267de Attempt to center window 2024-05-13 16:42:17 +00:00
crschnick
8d60945488 [stage] 2024-05-13 15:26:09 +00:00
crschnick
14268d2324 Add index based command builder element add 2024-05-13 15:18:25 +00:00
crschnick
a2896aba76 [release] 2024-05-12 10:24:52 +00:00
crschnick
3e76404758 Small fixes 2024-05-12 10:24:37 +00:00
crschnick
2e7fae6bde Small lock change fixes 2024-05-12 08:29:32 +00:00
crschnick
e32ee4da2f Fix secrets not being properly rewritten when passphrase is removed 2024-05-12 08:11:47 +00:00
crschnick
4fc95833e5 Handle storage deletion errors better 2024-05-12 07:18:29 +00:00
crschnick
3ef04b17c9 Small fixes 2024-05-12 07:08:36 +00:00
crschnick
e63d044313 Small fixes 2024-05-11 17:56:55 +00:00
crschnick
100820b439 Small fixes 2024-05-11 17:43:07 +00:00
crschnick
2d38d62428 Browser style fixes 2024-05-11 13:17:36 +00:00
crschnick
37eb17ef0d Bump jackson 2024-05-11 10:00:47 +00:00
crschnick
770a9a0263 Small fixes 2024-05-11 10:00:36 +00:00
crschnick
fe5686c05f Various browser fixes 2024-05-11 07:06:52 +00:00
Frederick Ambo
2a52591f54
DA lang - 9.2 check (#263)
* DA lang - 9.2 check

* It's not an "arch"

* No arch here.

* vault grammar
2024-05-10 17:08:02 +02:00
crschnick
0d989431ae Build fixes 2024-05-10 13:15:21 +00:00
crschnick
911c85a004 Rework dev environment setup 2024-05-10 12:22:15 +00:00
crschnick
0c06fe99f7 [stage] 2024-05-10 09:49:23 +00:00
crschnick
72be31435f Small bug fixes 2024-05-10 09:49:07 +00:00
crschnick
620c30382f Fix scripts not applying 2024-05-10 08:15:01 +00:00
crschnick
7507f664df Various fixes 2024-05-10 05:41:08 +00:00
crschnick
3363ad9f41 Update file host on file choice if needed 2024-05-10 04:44:11 +00:00
crschnick
63d9cce78e Only set wmclass on linux 2024-05-09 15:00:21 +00:00
crschnick
05884660d7 Set wmclass name 2024-05-09 14:51:57 +00:00
crschnick
234048109f Fix file browser dragboard mime type for macos 2024-05-09 09:56:11 +00:00
crschnick
6571f17e3b [stage] 2024-05-09 08:06:13 +00:00
crschnick
933b3f6837 Clean up shortcut handling 2024-05-08 12:34:28 +00:00
crschnick
26823e4728 Browser shortcut rework 2024-05-08 09:56:52 +00:00
crschnick
9e95c6b5b4 Fix storage not saving when exiting while saving 2024-05-07 10:51:12 +00:00
crschnick
9d4c4fe97d Merge branch browser-model into master 2024-05-07 07:31:09 +00:00
crschnick
b98ac6b4ea Fix NPE 2024-05-07 05:03:31 +00:00
crschnick
7309763d42 Make webview fail resilient 2024-05-07 05:00:20 +00:00
crschnick
9ba4c3115a Add ability for custom javafx dir 2024-05-07 04:35:18 +00:00
crschnick
33e75fec2a Rework file icon loading 2024-05-06 00:45:43 +00:00
crschnick
e4f5738fa5 Rework error actions 2024-05-06 00:14:14 +00:00
crschnick
1cabd5b93d Check for desktop support 2024-05-05 02:28:53 +00:00
crschnick
cb9145dd37 [stage] 2024-05-04 21:30:27 +00:00
crschnick
ab13f17fe7 Platform sync fixes 2024-05-04 21:28:37 +00:00
crschnick
8051412e9f [release] 2024-05-03 16:36:17 +00:00
crschnick
650398b541 Reformat 2024-05-03 16:17:57 +00:00
crschnick
2d367aadb9 Rebase 2024-05-03 16:12:57 +00:00
Christopher Schnick
7d0db085c0
Merge pull request #245 from tacaly/master
DA lang context fix "hibernation option"
2024-05-03 18:10:28 +02:00
Frederick Ambo
db4dc20c70
Merge branch 'xpipe-io:master' into master 2024-05-03 18:06:21 +02:00
crschnick
48e9ece152 Various fixes 2024-05-03 15:46:47 +00:00
Frederick Ambo
12a2e85535 DA lang context "hibernation option"
Added the correct text context. It will prob. be changed with time, but for now it's WAY better than "hvælving" = Arch.
2024-05-03 02:35:12 +02:00
crschnick
fbf9902b58 Remove bug report button 2024-05-02 23:39:52 +00:00
crschnick
5d6c8b95bc Fix property characters 2024-05-02 23:18:41 +00:00
crschnick
dcbbd211fe Build fixes 2024-05-02 23:12:37 +00:00
crschnick
60207dd24f Small fixes [stage] 2024-05-02 20:44:34 +00:00
crschnick
3bc1dc6cad Small fixes 2024-05-02 20:19:14 +00:00
crschnick
366a6e74e7 Add lock on hibernation option 2024-05-02 16:04:24 +00:00
crschnick
9ad98e4638 Fix desktop environment issues with null 2024-05-02 15:13:13 +00:00
crschnick
e5fdec3a5a [stage] 2024-05-01 19:12:36 +00:00
crschnick
e5c2079264 Fix NPEs 2024-05-01 19:00:00 +00:00
crschnick
568d1c2e6f Fix popover double show 2024-05-01 17:33:21 +00:00
crschnick
fd629c62bc Merge branch 'package-ptb' 2024-05-01 17:33:11 +00:00
crschnick
6408390535 Reformat 2024-05-01 17:16:19 +00:00
crschnick
c7f6bcf7d7 VNC improvements 2024-05-01 17:01:36 +00:00
crschnick
7176f4dd0a [stage] 2024-04-29 23:06:37 +00:00
crschnick
6fc48a7d74 Rework macos app launches 2024-04-29 22:49:30 +00:00
crschnick
dc50b0b155 Prepare next version 2024-04-29 20:23:19 +00:00
crschnick
733df4c005 Rework elevation function 2024-04-29 19:38:22 +00:00
crschnick
9c3eaa479c Various fixes 2024-04-28 21:09:40 +00:00
crschnick
bed38d425f Fix build [release] [noannounce] 2024-04-27 18:41:07 +00:00
crschnick
3d0dd67389 [release] [noannounce] 2024-04-27 18:21:45 +00:00
crschnick
b112d23163 Fix NPE with tabby 2024-04-27 18:12:52 +00:00
crschnick
82003734b5 Lang fixes 2024-04-27 18:03:08 +00:00
crschnick
b3f1f77bf2 Lang fixes 2024-04-27 14:23:37 +00:00
crschnick
e079a7feb6 Build fixes 2024-04-27 13:28:06 +00:00
crschnick
c99e4c2749 [release] 2024-04-27 12:31:07 +00:00
crschnick
9291d46370 [release] 2024-04-27 11:59:52 +00:00
crschnick
1e867d150c [release] 2024-04-27 11:15:41 +00:00
crschnick
304cad9e4b Fixes 2024-04-27 10:44:16 +00:00
crschnick
b14a00be7d [stage] 2024-04-25 18:55:49 +00:00
crschnick
09ffad1ec6 Various fixes 2024-04-25 18:55:41 +00:00
crschnick
45e2e9a95c Fixes 2024-04-25 10:50:08 +00:00
crschnick
88f3be6525 Style fixes 2024-04-25 08:37:15 +00:00
crschnick
8199800a4b Improve local development setup 2024-04-25 07:24:23 +00:00
crschnick
c8613350ff Logo adjustments 2024-04-25 07:24:03 +00:00
crschnick
8346ea90ba Lang adjustments 2024-04-25 07:21:55 +00:00
Christopher Schnick
9ce3e0b888
Merge pull request #233 from tolgaulas/patch-1
fixes on tr translatıon
2024-04-25 09:06:38 +02:00
crschnick
36b1b73626 Build fixes 2024-04-24 17:31:48 +00:00
Christopher Schnick
da1b9c5c1a
Merge pull request #238 from tacaly/master
Danish 🇩🇰 lang corrections
2024-04-24 19:16:36 +02:00
Tacaly
1f8e411c1e Merge branch 'master' of https://github.com/tacaly/xpipe 2024-04-24 16:59:12 +02:00
Tacaly
f7cedf54da da Rework finish key 2024-04-24 16:52:19 +02:00
Tolga Ulas
67059394f6
Merge branch 'xpipe-io:master' into patch-1 2024-04-24 17:52:03 +03:00
crschnick
bc3a4fcf15 Rework finish key 2024-04-24 14:47:00 +00:00
Tacaly
55007cfc52 DA lang fix (user feedback)
Quick fix in context form
2024-04-24 16:13:37 +02:00
Tacaly
9b69f534eb DA lang fix
No "valgfri" in a user software context.
2024-04-24 16:11:29 +02:00
crschnick
35841c403d Cleanup 2024-04-24 14:02:07 +00:00
Tacaly
6edf216005 da hotfix 2024-04-24 15:50:00 +02:00
Tacaly
984f565ed7 DA lang fix round 2
Missed the "app" folder.
2024-04-24 15:42:50 +02:00
Tacaly
4aea1694ff DA lang fix -GUI QA 2024-04-24 15:31:34 +02:00
crschnick
adc277971b [stage] 2024-04-24 13:05:54 +00:00
crschnick
04bca4295d Various fixes 2024-04-24 12:42:35 +00:00
crschnick
a974e7aac2 [stage] 2024-04-23 16:59:20 +00:00
crschnick
627a8141cd Init file sourcing changes 2024-04-23 16:57:11 +00:00
crschnick
24600a98be Fixes 2024-04-23 15:53:52 +00:00
crschnick
125d1ddbaf Style fixes 2024-04-23 09:27:30 +00:00
crschnick
a02d40cab6 Fixes 2024-04-23 08:42:17 +00:00
crschnick
188e41e1ba Fixes 2024-04-22 17:38:33 +00:00
crschnick
363f79f8ad [stage] 2024-04-22 11:57:13 +00:00
crschnick
401ab34689 Various fixes 2024-04-22 11:56:59 +00:00
crschnick
4ca3d2c52c Cleanup 2024-04-22 06:01:20 +00:00
crschnick
8564b2e820 Translation fixes 2024-04-22 05:15:37 +00:00
crschnick
8a822b1f4c Add danish translations 2024-04-22 04:43:02 +00:00
crschnick
10d1382327 More init script rework 2024-04-21 12:56:12 +00:00
crschnick
a87cbb2344 [stage] 2024-04-21 10:41:45 +00:00
crschnick
230729d9ab Fixes 2024-04-21 10:37:50 +00:00
crschnick
1ddc58d254 Fix browser shell locks 2024-04-21 09:17:58 +00:00
crschnick
46de09ccd3 Add more terminal websites 2024-04-21 05:13:56 +00:00
crschnick
eb873f2225 Fixes 2024-04-21 04:48:44 +00:00
crschnick
409b24ff8a Fixes 2024-04-21 04:29:19 +00:00
crschnick
a03f8d7e84 Terminal init command rework 2024-04-21 04:05:23 +00:00
crschnick
0a3488324d Rework init commands for warp 2024-04-21 03:22:23 +00:00
crschnick
73bc99b3c6 Save on category share 2024-04-21 01:28:57 +00:00
crschnick
51f7e3f4f5 Fixes [stage] 2024-04-20 10:25:02 +00:00
crschnick
ccb4e06809 [stage] 2024-04-20 07:40:06 +00:00
crschnick
e5c1ebed1d Fixes 2024-04-20 05:46:59 +00:00
crschnick
bae68e7ea8 Various fixes 2024-04-20 02:33:15 +00:00
crschnick
e04c63d36f Session control fixes 2024-04-19 10:05:05 +00:00
crschnick
385068a3de Fixes 2024-04-19 07:52:59 +00:00
Tolga Ulas
d9110b871c
Update translations_tr.properties 2024-04-19 08:50:08 +03:00
Tolga Ulas
5fbb1703ed
mınor fıx on tr translatıon 2024-04-19 08:35:06 +03:00
crschnick
60413b7de8 Fixes 2024-04-19 03:15:57 +00:00
crschnick
5d339d417e [stage] 2024-04-19 01:35:04 +00:00
crschnick
5dacb65e1a Fixes 2024-04-19 01:34:49 +00:00
crschnick
363cb80f88 Fixes 2024-04-18 23:03:47 +00:00
crschnick
b43a3d6489 Rasterize images 2024-04-18 22:29:08 +00:00
crschnick
7bb5c2a441 Various fixes 2024-04-18 22:20:32 +00:00
crschnick
b298d3cb81 Reformat 2024-04-18 21:37:28 +00:00
crschnick
4b78b5e132 Small fixes 2024-04-18 09:54:24 +00:00
crschnick
c191f675e0 More fixes 2024-04-18 06:23:19 +00:00
crschnick
059b12b654 Various fixes 2024-04-18 04:38:56 +00:00
crschnick
ba83a9c23a More desktop rework 2024-04-17 08:07:40 +00:00
crschnick
5e80c1a4a1 State rework 2024-04-16 13:47:29 +00:00
crschnick
dd3ccf37d3 Desktop rework 2024-04-16 08:58:48 +00:00
crschnick
d9e23b9ebf Various fixes 2024-04-15 04:40:50 +00:00
crschnick
7bce1e8f3b [stage] 2024-04-14 03:45:30 +00:00
crschnick
f39ec1451b Fixes 2024-04-14 03:34:52 +00:00
crschnick
6c0b3cb58c Various fixes 2024-04-14 00:58:11 +00:00
crschnick
4c26c84a70 Rework ssh host key check 2024-04-13 21:07:38 +00:00
crschnick
f585969233 Rework file icon loading 2024-04-13 21:05:28 +00:00
crschnick
8a60b29674 Improve platform notice 2024-04-13 21:05:15 +00:00
crschnick
922633fb6e Fix NPE 2024-04-13 21:05:04 +00:00
crschnick
8eac06c213 [stage] 2024-04-13 17:26:44 +00:00
crschnick
bc1afe3f36 [stage] 2024-04-13 16:44:29 +00:00
crschnick
2a6e993d6e Fix broken links 2024-04-13 16:38:44 +00:00
crschnick
eb3794462f [stage] 2024-04-13 16:26:46 +00:00
crschnick
cbc5ad473a Merge branch vnc into master 2024-04-13 16:23:09 +00:00
crschnick
6bd105b1de Remove unused dependency 2024-04-03 06:02:26 +00:00
crschnick
c86e65dea4 More readme fixes 2024-04-03 05:36:02 +00:00
crschnick
78a7255bb8 More readme fixes 2024-04-03 05:26:04 +00:00
crschnick
8357641eed More readme fixes 2024-04-03 05:24:21 +00:00
crschnick
1457d003d2 More readme fixes 2024-04-03 05:20:06 +00:00
crschnick
42c149a1ff Update readme 2024-04-03 05:02:38 +00:00
crschnick
93786dc76c Improve window title 2024-04-03 04:08:49 +00:00
crschnick
6f612b82f9 Logo adjustments 2024-04-03 03:55:31 +00:00
crschnick
ac36f7043a macos fixes 2024-04-02 07:07:13 +00:00
crschnick
fa7b6d66bd Update macos logos 2024-04-02 06:52:16 +00:00
crschnick
138dd5a56d Update logo 2024-04-02 06:31:48 +00:00
crschnick
91e1a37cdb Rework terminal integrations 2024-04-02 03:41:08 +00:00
crschnick
3293f27e6f Update old links 2024-04-02 02:29:56 +00:00
crschnick
3ebaf17c0d Rework kitty macos support 2024-04-01 08:06:14 +00:00
crschnick
fe1665eace Fix kitty arguments 2024-04-01 07:27:55 +00:00
crschnick
d3c173dbcd Merge branch better-commands 2024-04-01 07:25:06 +00:00
crschnick
10ef67a6ec Fix context menu actions 2024-04-01 06:36:47 +00:00
crschnick
beb467af5a Kitty fixes 2024-04-01 05:12:44 +00:00
crschnick
a8f07c3e0f Rework file paths and kitty support 2024-04-01 03:43:22 +00:00
crschnick
c228e6ba30 More keyboard navigation fixes 2024-03-30 19:34:13 +00:00
crschnick
3f01bb0028 Rework keyboard usage 2024-03-30 00:24:50 +00:00
crschnick
9ad5b6f7f5 Browser quick access improvements 2024-03-29 18:32:00 +00:00
crschnick
061dbe1cf3 Improve help command for linux/macos 2024-03-29 13:51:53 +00:00
crschnick
e9b0a19509 Properly init direct terminal launcher for updates 2024-03-28 15:52:07 +00:00
crschnick
d8b9571de8 [release] 2024-03-28 13:14:12 +00:00
crschnick
65607aac53 Quick access fixes 2024-03-28 11:36:41 +00:00
crschnick
822cab45e7 Update changelog [stage] 2024-03-27 18:19:42 +00:00
crschnick
d3f4bd3044 Reformat 2024-03-27 18:19:03 +00:00
crschnick
c475353c22 Small browser fixes 2024-03-27 17:36:27 +00:00
crschnick
6510c3f6b9 Fix wrong error messages 2024-03-27 17:35:31 +00:00
crschnick
e3c4d9a9ec More sid fixes 2024-03-27 15:29:50 +00:00
crschnick
eb60590a50 Navbar img fixes 2024-03-27 15:21:15 +00:00
crschnick
3cebf750fb Rework sid check again 2024-03-27 14:55:10 +00:00
crschnick
7e8d31dd3c Rework setsid usage 2024-03-27 13:28:13 +00:00
crschnick
baee77d47f More quick access fixes 2024-03-27 12:44:35 +00:00
crschnick
76379b5a5d Improve progress display for GB+ 2024-03-27 11:41:50 +00:00
crschnick
92faa26bbc More quick access fixes 2024-03-26 22:58:03 +00:00
crschnick
b1a3caad46 Quick access fixes 2024-03-26 19:38:31 +00:00
crschnick
86ae1271a4 Rework image handling for browser 2024-03-26 19:38:24 +00:00
crschnick
07472cad9a Add browser quick access 2024-03-26 13:53:51 +00:00
crschnick
7784757393 [stage] 2024-03-26 09:37:11 +00:00
crschnick
7a537f1a60 Add file path class 2024-03-25 11:41:44 +00:00
crschnick
9154a28aca Cleanup 2024-03-25 10:55:48 +00:00
crschnick
6e5f131658 Fix unnecessary elevation confirmation 2024-03-25 10:51:33 +00:00
crschnick
db94d2d74b Rework jna handling and add sid check 2024-03-25 08:15:31 +00:00
crschnick
e90c6f36d3 Overlay style fixes [release] 2024-03-24 07:19:54 +00:00
crschnick
ce8c97dce6 [release] 2024-03-24 06:41:02 +00:00
crschnick
4fdf89b77d Handle askpass without platform 2024-03-24 05:53:45 +00:00
crschnick
d5442e858d Catch more temp dir errors 2024-03-24 05:53:34 +00:00
crschnick
a44594ef5f Temp fixes 2024-03-24 03:32:39 +00:00
crschnick
57352b566f Small fixes 2024-03-24 02:41:46 +00:00
crschnick
096666e345 Improve error overlay styling 2024-03-23 15:14:37 +00:00
crschnick
3848fb9d93 Update changelog [stage] 2024-03-23 13:57:00 +00:00
crschnick
a07a18d4f0 Improve font check 2024-03-23 13:31:22 +00:00
crschnick
0535a5444f Move font check 2024-03-23 13:18:48 +00:00
crschnick
0b8fbfabe0 Improve font loading check 2024-03-23 13:11:05 +00:00
crschnick
7391310dce Improve font loading checks 2024-03-23 12:12:23 +00:00
crschnick
147e68efe5 Reformat 2024-03-23 06:53:08 +00:00
crschnick
ea7114178f Rework working directory handling 2024-03-23 06:31:41 +00:00
crschnick
346855826e Bump javafx jmods 2024-03-23 04:05:10 +00:00
crschnick
9c79f245e1 Dont' use fallback working dir 2024-03-23 03:37:31 +00:00
crschnick
24b6efed96 Fix shell check fallback not working correctly 2024-03-23 02:46:52 +00:00
crschnick
11bebd059a Update changelog [stage] 2024-03-22 09:52:12 +00:00
crschnick
529563aa24 Fix combo box popup jumping when switching themes 2024-03-22 09:25:00 +00:00
crschnick
e345b1eaf2 Fix children always being included in parent category 2024-03-22 09:24:43 +00:00
crschnick
67dafeabf7 Fix date ordering 2024-03-22 09:03:01 +00:00
crschnick
474b529050 Fix dead link 2024-03-22 09:02:28 +00:00
crschnick
3b6ad291a8 Refactor checks 2024-03-22 06:03:30 +00:00
crschnick
f84c06dd8c Improve shell check 2024-03-22 05:42:09 +00:00
crschnick
709d075893 Improve terminal colors 2024-03-22 05:21:58 +00:00
crschnick
b10b7737a5 Add ovh bastion support 2024-03-22 05:21:39 +00:00
crschnick
4bbdd9315c Fix skip connection validation still looking for child connections 2024-03-22 02:37:11 +00:00
crschnick
bdc1444519 Fix browser nav bar showing when directory was initially set 2024-03-22 02:36:41 +00:00
crschnick
3dd7829b23 Bump slf4j 2024-03-22 02:15:51 +00:00
crschnick
cad35a6876 Bump versions [stage] 2024-03-21 06:14:50 +00:00
crschnick
e4d9545fe8 Use dependabot for gh actions as well 2024-03-21 05:48:26 +00:00
crschnick
9c90ec9336 Bump versions [stage] 2024-03-21 05:28:19 +00:00
crschnick
a329969590 [stage] 2024-03-21 04:49:12 +00:00
crschnick
bced7bdc33 Merge branch 'gpg' 2024-03-21 04:48:05 +00:00
crschnick
e1002cf60f Update changelog 2024-03-21 04:23:16 +00:00
crschnick
11d6997752 Refactor windows terminal classes 2024-03-21 03:57:53 +00:00
crschnick
cde12be5c8 Improve working directory handling if it does not exist 2024-03-21 03:57:23 +00:00
crschnick
50defba23d Improve shell temporary directory creation 2024-03-21 03:55:54 +00:00
crschnick
863b0f8783 Improve fallback shell check on non-windows systems 2024-03-21 00:47:02 +00:00
crschnick
1fb68f8ca1 Handle more platform initialization errors 2024-03-17 10:07:23 +00:00
crschnick
3a47755c73 Check for font loading issues more extensively 2024-03-15 11:58:33 +00:00
crschnick
55441b737f [release] 2024-03-14 09:50:08 +00:00
crschnick
e89fb97d53 Flush logs on error 2024-03-14 09:34:53 +00:00
crschnick
c6cca15a9a Handle file list limit exit better 2024-03-13 17:50:37 +00:00
crschnick
b9406a4904 Update changelog 2024-03-13 15:10:52 +00:00
crschnick
31f926f815 Fix terminal launch errors not showing 2024-03-13 12:09:57 +00:00
crschnick
c3d7d69476 Update changelog 2024-03-13 11:59:55 +00:00
crschnick
95b0398490 Update changelog 2024-03-13 10:24:02 +00:00
crschnick
19251dea9f Improve askpass focus handling 2024-03-13 10:23:54 +00:00
crschnick
5af9ec6fde Fix secret query advance being off 2024-03-13 10:23:10 +00:00
crschnick
92adaf47c3 Update changelog 2024-03-13 09:41:54 +00:00
crschnick
89d5d06b22 Improve askpass handling 2024-03-13 09:41:33 +00:00
crschnick
f17b3c5ec5 Remove useless runnable check 2024-03-13 07:46:27 +00:00
crschnick
21a47de1fa More powershell fixes 2024-03-13 07:32:08 +00:00
crschnick
0149137abe More powershell fixes 2024-03-13 07:23:13 +00:00
crschnick
32515720fc macOS fixes 2024-03-13 07:04:54 +00:00
crschnick
4320040c9a More elevation fixes 2024-03-13 06:21:08 +00:00
crschnick
6f4712934e Update changelog 2024-03-12 17:56:37 +00:00
crschnick
0b0e92e9da Make store creation more resilient when parent is deleted 2024-03-12 15:27:30 +00:00
crschnick
a4faa7c04c Fix children not properly refreshing across different categories for #207 2024-03-12 13:08:54 +00:00
crschnick
c2ac2481e4 Rework shell exit listener to close file browser tabs 2024-03-12 10:19:29 +00:00
crschnick
770c9eb309 Properly trim file path everywhere in chooser 2024-03-12 08:30:58 +00:00
crschnick
1a29d14a79 Fix possible NPE 2024-03-11 12:08:10 +00:00
crschnick
cd0c8a1649 [release] [noaanounce] 2024-03-11 08:16:02 +00:00
crschnick
bff56ed58c Move charsetter classes 2024-03-11 08:15:43 +00:00
crschnick
c35fb24bf4 Improve data store choice display 2024-03-11 07:04:29 +00:00
crschnick
74abedcb6f Add changelog 2024-03-11 07:04:05 +00:00
crschnick
6ad4f990d1 Simplify warp launcher 2024-03-10 10:18:25 +00:00
crschnick
1e7c2f9c93 Skip scan if validation has been skipped 2024-03-10 09:47:53 +00:00
crschnick
fe885d7e80 Tunnel lifecycle fixes 2024-03-10 09:25:52 +00:00
crschnick
64ee40afb0 Improve winget support 2024-03-09 03:56:54 +00:00
crschnick
fbfe4bdf4b Fix old license URLs 2024-03-09 02:16:55 +00:00
crschnick
ccea37e0e0 Rework ssh host key trust handling 2024-03-09 02:02:00 +00:00
crschnick
74d6ff7fa3 Update readme 2024-03-07 23:16:37 +00:00
crschnick
bb4e844da9 Cleanup 2024-03-07 22:41:29 +00:00
crschnick
965dfe04e8 Remove api module 2024-03-07 22:26:23 +00:00
crschnick
1c43bece80 Include changelog for legacy versions [release] 2024-03-07 19:34:55 +00:00
crschnick
e70a04d588 [release] 2024-03-07 19:33:41 +00:00
crschnick
aeb921f161 Improve pwsh launch 2024-03-07 19:33:17 +00:00
crschnick
9ac9128712 Terminal launch fixes 2024-03-07 19:13:22 +00:00
crschnick
1b5d2b7741 Styling fixes 2024-03-07 18:41:50 +00:00
crschnick
84ca2545f0 Fix macos contextmenu style 2024-03-07 17:06:05 +00:00
crschnick
0de5d9da4e Improve fallback shell terminal handling 2024-03-07 01:51:57 +00:00
crschnick
84700d2a27 Fix certutil check 2024-03-07 01:50:52 +00:00
crschnick
fe2334e936 Prefer cmd over powershell for default terminal 2024-03-06 19:58:56 +00:00
crschnick
3a9d06c745 Fix possibility of invalid selection 2024-03-06 19:58:49 +00:00
crschnick
c4f0797960 Add quick access button 2024-03-06 18:38:15 +00:00
crschnick
9abd19483f Shell error handling fixes 2024-03-05 21:50:24 +00:00
crschnick
73efb183e7 Fix install script sometimes uninstalling PTB version 2024-03-04 20:09:12 +00:00
crschnick
ee3f5babe6 More status bar fixes [release] [noannounce] 2024-03-04 18:48:54 +00:00
crschnick
a557fcbe73 Fix status bar binding 2024-03-04 18:42:32 +00:00
crschnick
d54fa908f8 [release] 2024-03-04 18:05:07 +00:00
crschnick
84378c18d8 Fix relative path computation 2024-03-04 18:03:30 +00:00
crschnick
7821d0e779 Reformat 2024-03-04 17:47:13 +00:00
crschnick
a7c046520c Merge branch transfer-performance into master 2024-03-04 17:23:30 +00:00
crschnick
bda50dba72 Fix progress being wrong in some cases 2024-03-02 13:23:40 +00:00
crschnick
d60fff969b Fix possible NPE 2024-03-02 11:35:20 +00:00
crschnick
f1742ce37d Update changelog 2024-03-02 11:25:33 +00:00
crschnick
8483130b09 Don't start local powershell 2024-03-02 11:25:16 +00:00
crschnick
7117b4f6f2 Create canonical version information 2024-03-02 11:24:54 +00:00
crschnick
6df1f70a19 Don't cast button 2024-03-02 09:09:56 +00:00
crschnick
106132ef88 Link to help page for double prompt 2024-03-02 09:01:55 +00:00
crschnick
97d7f4f30f Update changelog 2024-03-01 18:56:50 +00:00
crschnick
bb0f6fbeeb Improve section display 2024-03-01 16:59:54 +00:00
crschnick
30ebddc904 Fix concurrency issues in secret manager 2024-03-01 08:52:23 +00:00
crschnick
86df8b8803 Rework scan to restart shell 2024-03-01 08:52:10 +00:00
crschnick
b1753e589e Fix prefs NPEs 2024-03-01 08:52:00 +00:00
crschnick
8591fe8562 Bump clink 2024-03-01 07:12:46 +00:00
crschnick
d0497fcba2 Improve file watch error message 2024-03-01 07:12:35 +00:00
crschnick
2cda040274 Improve transfer pane icon 2024-02-29 15:38:20 +00:00
crschnick
a9e00e8bc9 Avoid querying the local shell directly 2024-02-29 09:49:30 +00:00
crschnick
3df16c22a4 Expect more errors and rename 2024-02-29 09:49:23 +00:00
crschnick
9f55deb1e2 Expect socket exceptions now 2024-02-29 09:40:28 +00:00
crschnick
4ff0d59dba Improve changelogs 2024-02-29 09:38:53 +00:00
crschnick
27fb7370be Fix windows updater being broken 2024-02-29 09:09:50 +00:00
crschnick
e7fc20b40d Trim application search output 2024-02-29 07:05:36 +00:00
crschnick
bef976914f Bump flexmark 2024-02-28 15:14:13 +00:00
crschnick
405cdf53e5 Fix local powershell handling 2024-02-28 15:13:27 +00:00
crschnick
5ef3e1d508 Rework windows updater 2024-02-28 14:13:30 +00:00
crschnick
fb8e990083 Better handle markdown io errors 2024-02-28 14:13:07 +00:00
crschnick
f42434c60e Don't show logs clean error when another instance is running 2024-02-28 14:12:51 +00:00
crschnick
37dae87a2f Add support to search for custom store attributes 2024-02-28 14:12:35 +00:00
crschnick
ac1fcf821d Rework windows updater to fix issues 2024-02-28 12:30:22 +00:00
crschnick
b847a6fb9f Check for askpass platform support 2024-02-28 11:44:41 +00:00
crschnick
99cd652536 [release] [noannounce] 2024-02-28 10:22:02 +00:00
crschnick
dbfcfe73f6 [release] 2024-02-28 10:19:45 +00:00
crschnick
fbc56f0b81 Fix update issues 2024-02-28 09:34:35 +00:00
crschnick
e56c159e0e Disable context menu for markdown comps 2024-02-28 08:53:41 +00:00
crschnick
d86925c1a3 [release] [noannounce] 2024-02-28 08:43:54 +00:00
crschnick
f7cf7284c7 Fix vault preferences not respecting custom storage directory 2024-02-28 08:18:45 +00:00
crschnick
d2d0df9789 Remove leftover merge files 2024-02-28 07:52:21 +00:00
crschnick
3e7fbe89ac Merge branch prefs into master
The changes have been squashed as the commit history and messages were not very carefully crafted. There isn't that much value in preserving random commit messages.

Also due to diverging branches, rebasing or merging it was difficult.
2024-02-28 07:36:31 +00:00
crschnick
ce45ff9ec6 Improve side window modality 2024-01-30 10:54:01 +00:00
crschnick
404a6e1948 Add helper to only read stderr 2024-01-30 10:49:08 +00:00
crschnick
8a7669e832 Remove unused gtk fix 2024-01-26 17:41:37 +00:00
crschnick
60111ddd21 vscodium fixes 2024-01-26 12:33:23 +00:00
crschnick
315a58675e Add vscodium support 2024-01-26 12:15:26 +00:00
crschnick
a11ad4d486 Show failure messages when launching external applications 2024-01-26 12:15:15 +00:00
crschnick
715d36336c Confirm git share of file 2024-01-26 07:56:09 +00:00
crschnick
2dead13d4d Improve shell check display 2024-01-24 06:08:23 +00:00
crschnick
1b7181a744 Add ability to easily add files to git vault data 2024-01-24 05:49:47 +00:00
crschnick
e76a5f2e94 Fix ssh config NPE 2024-01-24 04:32:27 +00:00
crschnick
f660c20188 Make av registry check more robust 2024-01-24 04:27:07 +00:00
crschnick
a4eef3fdf7 Allow for cluster creation 2024-01-23 04:31:51 +00:00
crschnick
afc34a37b5 Rework fixed child handling for k8s improvements 2024-01-23 02:54:21 +00:00
crschnick
53926a2830 Fix save race condition 2024-01-22 12:33:26 +00:00
crschnick
2310515a4c Fix file system exception edit problems 2024-01-22 09:49:12 +00:00
crschnick
5ae35ecfe9 Fix missing default linux icon 2024-01-22 03:43:50 +00:00
crschnick
f61bef8bc0 Include release date in update changelog 2024-01-20 21:45:17 +00:00
crschnick
809999394d [release] 2024-01-20 19:06:57 +00:00
crschnick
ebc1f33a56 Improve alert styling 2024-01-20 18:14:21 +00:00
crschnick
11859d3d10 Be more defensive for github api dates 2024-01-20 17:38:04 +00:00
crschnick
3f197337c6 [stage] 2024-01-20 04:22:48 +00:00
crschnick
3b04427f3c Update changelog 2024-01-20 03:52:39 +00:00
crschnick
df1a80ed42 Rework alert styling 2024-01-20 01:14:36 +00:00
crschnick
79ef71a1d3 Fix store validity refresh on simple update 2024-01-20 00:42:08 +00:00
crschnick
2abd95e3e5 Fix typo 2024-01-20 00:34:59 +00:00
crschnick
ad19bc742b Include release date in changelog 2024-01-19 21:18:24 +00:00
crschnick
00a36628e9 Don't minimize detached windows applications 2024-01-19 21:16:59 +00:00
crschnick
a1ec6cb422 Allow for finer control of write logging 2024-01-19 17:25:18 +00:00
crschnick
ac865b4a92 [stage] 2024-01-19 01:00:49 +00:00
crschnick
65cec08224 Always wait when performing action after exit 2024-01-19 00:22:29 +00:00
crschnick
41965a521b Update readme for downloads 2024-01-18 22:12:37 +00:00
crschnick
acca222a06 Check whether logs dir exists before cleaning 2024-01-18 20:54:32 +00:00
crschnick
f33db9ca49 Force canonical versions for releases 2024-01-18 20:30:29 +00:00
crschnick
a08d130314 [release] 2024-01-18 20:21:06 +00:00
crschnick
406e63cbf0 Wait on update exit 2024-01-18 20:14:03 +00:00
crschnick
25301c1ac1 [release] 2024-01-18 19:52:02 +00:00
crschnick
46cd57db18 Small fixes 2024-01-18 17:15:35 +00:00
crschnick
f467d59f49 Add windows terminal preview support 2024-01-18 17:15:26 +00:00
crschnick
d9f1a5a769 Fix wezterm macos issues 2024-01-18 15:17:56 +00:00
crschnick
298cd28872 Add wezterm support 2024-01-18 02:14:36 +00:00
crschnick
2565bb250f [stage] 2024-01-17 20:59:18 +00:00
crschnick
aa7c04d5ba Small browser appearance improvements 2024-01-17 20:58:33 +00:00
crschnick
ff2e5e67c4 Rework local store sharing 2024-01-17 18:07:43 +00:00
crschnick
f1966c0e39 Allow for sharing of connections with data files 2024-01-17 17:31:18 +00:00
crschnick
ab9059881c Rename store creation comp and apply state on entry update 2024-01-17 17:06:10 +00:00
crschnick
37ed575266 Rework ssh file path handling 2024-01-17 16:35:27 +00:00
crschnick
de6c71fc5d Update changelog 2024-01-16 21:19:08 +00:00
crschnick
916fd98773 Update build instructions [stage] 2024-01-16 16:40:34 +00:00
crschnick
2ff1d57c21 Update changelog 2024-01-16 13:07:41 +00:00
crschnick
bd2acc6574 Various small fixes 2024-01-16 13:05:21 +00:00
crschnick
dd54dccec7 [stage] 2024-01-15 21:58:39 +00:00
crschnick
995dcca50c Bundle javafx jmods 2024-01-15 21:05:14 +00:00
crschnick
3e07e890ea Rework debug mode 2024-01-15 17:43:03 +00:00
crschnick
057398c317 Include previous changelog 2024-01-15 15:16:53 +00:00
crschnick
47125b4615 Update changelog 2024-01-15 15:15:53 +00:00
crschnick
eef19677ab Fix possible NPEs 2024-01-15 15:11:35 +00:00
crschnick
45d3d40c6f Probably return error on cli open command 2024-01-11 21:12:28 +00:00
crschnick
68b4cb2fcc Update readme formatting 2024-01-11 19:54:09 +00:00
crschnick
3e81d0a2d3 Update readme 2024-01-11 19:51:17 +00:00
crschnick
edf22a0815 Fix some install options 2024-01-11 18:19:17 +00:00
crschnick
c58ad29309 Update linux installer script 2024-01-11 17:42:29 +00:00
crschnick
1b1d388941 Fix gradle build 2024-01-11 15:25:38 +00:00
crschnick
974da59fc8 Make window clamping more robust for invalid values 2024-01-10 15:58:26 +00:00
crschnick
54dff11353 Revert to jfx 20 to fix performance regression in 21 2024-01-10 05:11:32 +00:00
crschnick
2c5d3b326d Rework svg image handling 2024-01-10 04:26:49 +00:00
crschnick
140b734142 Recognize rbash 2024-01-10 01:43:45 +00:00
crschnick
faf1b6b4c7 Rework shell control to be more robust in cases it exits unexpectedly 2024-01-09 23:17:18 +00:00
crschnick
a6cb7b1a30 Don't return null for unknown os name 2024-01-09 04:39:02 +00:00
crschnick
522c2c1664 Rework image sizes and caches 2024-01-09 04:20:24 +00:00
crschnick
e4d3eb2a3d Fix connection choice filter focus 2024-01-09 04:20:13 +00:00
crschnick
44945546f7 Focus filter in connection choice popover 2024-01-09 02:30:32 +00:00
crschnick
720e5edfdc Rework text area 2024-01-09 01:34:17 +00:00
crschnick
f454e6d905 Allow for better contextual file reference handling 2024-01-09 01:33:38 +00:00
crschnick
277f7f8c04 Fix logs attribute 2024-01-09 01:33:21 +00:00
crschnick
bfef4e1426 Fix possible NPE 2024-01-09 01:33:00 +00:00
crschnick
0f9cae0681 Rework comboboxes 2024-01-06 16:54:57 +00:00
crschnick
aee7b65bce Fix nord selected color contrast in browser 2024-01-06 10:50:27 +00:00
crschnick
720716b1c6 Rework connection creation menu 2024-01-06 10:40:50 +00:00
crschnick
fb103e03df Shorten nixos description 2024-01-05 17:11:16 +00:00
crschnick
950294fff1 nixos update 2024-01-05 17:10:31 +00:00
crschnick
0742c0e2d2 Fix exception in edge case for binding lists 2024-01-05 15:06:25 +00:00
crschnick
8e8186507a [release] 2024-01-05 10:09:40 +00:00
crschnick
4ee5c18960 Add min size comp methods 2024-01-05 08:01:46 +00:00
crschnick
7521f08512 Make theme detection error resistant 2024-01-05 08:01:35 +00:00
crschnick
16de992c29 Fix mini section size with one element 2024-01-05 08:00:59 +00:00
crschnick
fcba5d1c54 Fix NPE if no terminal was set 2024-01-04 09:11:34 +00:00
crschnick
6c9fddebe6 Rework checksums some more 2024-01-03 18:56:33 +00:00
crschnick
0070a08109 Fix error handling of invalid commandline options 2024-01-03 18:15:06 +00:00
crschnick
06544d31e1 [stage] 2024-01-03 16:48:31 +00:00
crschnick
c59b6bb1ff Fix browser restore not sleeping at right time 2024-01-03 16:48:17 +00:00
crschnick
c3cbe022b9 Add pfsense shell dialect 2024-01-03 16:21:04 +00:00
crschnick
362cb12e20 Fix exceptions on provider switch 2024-01-03 15:23:13 +00:00
crschnick
d8b2c44489 Rework custom terminal description and add tilix 2024-01-03 14:23:03 +00:00
crschnick
e357c96c5d Small fixes 2024-01-03 14:22:43 +00:00
crschnick
44cae41799 Wait only 10s to declare a directory as recent 2024-01-03 14:22:29 +00:00
crschnick
0754e79970 Run restore action staggered 2024-01-03 14:22:18 +00:00
crschnick
ab8b6d449e Rework checksums 2024-01-03 09:15:53 +00:00
crschnick
0fef5b0b9a Remove font check 2024-01-03 07:06:35 +00:00
crschnick
8d080bee1e Rework bundled fonts handling 2024-01-03 06:31:13 +00:00
crschnick
784bff8031 [stage] 2024-01-02 16:01:03 +00:00
crschnick
5aa1c35eb1 Make window init error handling more robust 2024-01-02 15:26:53 +00:00
crschnick
4542225c3f Fix changelog spacing 2024-01-02 14:50:50 +00:00
crschnick
f398366246 Allow dumb replacement shell to depend on parent 2024-01-02 13:20:13 +00:00
crschnick
6cc2fd85a3 [stage] 2024-01-02 13:07:06 +00:00
crschnick
1e0edf610e Hide bsd sh 2024-01-02 13:06:42 +00:00
crschnick
8506047f6b Force javafx to look for embedded fonts 2024-01-02 09:58:26 +00:00
crschnick
17917de665 Locate correct fonts in development mode 2024-01-02 06:59:04 +00:00
crschnick
57c199e6d7 [stage] 2024-01-01 16:17:56 +00:00
crschnick
9fa35aff21 Remove unnecessary window position update 2024-01-01 16:08:28 +00:00
crschnick
8a684caf1b Require macos applications to be in some applications directory 2024-01-01 15:57:35 +00:00
crschnick
88086dc322 Fix browser state serialization 2024-01-01 15:04:15 +00:00
crschnick
6063e3b092 Small UI improvements 2024-01-01 14:26:10 +00:00
crschnick
ae52ab7e53 Rework shell dialect names for bsd sh support 2024-01-01 14:24:26 +00:00
crschnick
f67e9ad247 Rework browser welcome screen 2024-01-01 10:04:06 +00:00
crschnick
2bc508d448 Small linux fixes 2024-01-01 10:03:49 +00:00
crschnick
5f7a37279f [stage] 2023-12-31 13:42:20 +00:00
crschnick
1a9d1647e0 Clean up font size 2023-12-31 08:51:54 +00:00
crschnick
e9c9bb8a61 Provide logical font mapping as fallback 2023-12-31 05:53:39 +00:00
crschnick
4aa53bbd1d Update gradle wrapper jar to 8.5 2023-12-31 05:46:56 +00:00
crschnick
d619f3aacb Make gradle wrapper executable 2023-12-31 05:11:24 +00:00
crschnick
38e1b929d6 Bundle fonts to not rely on fontconfig 2023-12-31 05:05:03 +00:00
crschnick
e3b451b600 Fix connection summary generation for #146 2023-12-30 14:20:23 +00:00
crschnick
24c5b31a04 Add paragraph utility method for process output exceptions 2023-12-30 11:35:04 +00:00
crschnick
ec6ef44451 [stage] 2023-12-30 09:38:59 +00:00
crschnick
ca84b90dbd Rework file browser open and it improve ssh config support 2023-12-30 09:02:28 +00:00
crschnick
815787c8a9 Fix copy paste error 2023-12-30 07:09:22 +00:00
crschnick
aee414f27d Fix name alignment 2023-12-30 06:37:47 +00:00
crschnick
16071c0e75 Cleanup shell control 2023-12-30 02:20:21 +00:00
crschnick
1757684c79 Fix correct NPE 2023-12-30 02:18:02 +00:00
crschnick
bb1aa5c245 Allow for multiple connections with same name and fix NPE 2023-12-30 02:17:13 +00:00
crschnick
fb3b768352 Fix NPE 2023-12-30 02:16:56 +00:00
crschnick
43f2decca9 Add self test to verify that local shell is working 2023-12-29 09:06:37 +00:00
crschnick
82ada6da4a [stage] 2023-12-29 03:43:04 +00:00
crschnick
f0deb25d31 Improve performance mode gains by removing more styling 2023-12-29 03:24:22 +00:00
crschnick
7970402411 Use better layout for long connection names in various widths 2023-12-29 02:53:57 +00:00
crschnick
5f729095d8 Allow for fast connection renaming 2023-12-29 01:24:25 +00:00
crschnick
8ab8aaf100 Fix NPE 2023-12-29 01:12:11 +00:00
crschnick
dd87d07e1f Fix connection category target computation 2023-12-29 00:40:17 +00:00
crschnick
a533de0300 [release] 2023-12-28 22:14:52 +00:00
crschnick
34ff540386 Disable fast terminal startup for now 2023-12-28 21:56:07 +00:00
crschnick
75d6b1ed4f Rework pro context menu entries 2023-12-28 07:38:37 +00:00
crschnick
a81ea216aa [stage] 2023-12-28 05:01:20 +00:00
crschnick
d0d55a3a94 Make fast terminal startup a setting 2023-12-28 04:57:33 +00:00
crschnick
f2c5561eac Improve script init failure message 2023-12-28 00:57:06 +00:00
crschnick
f689e6a2ea Fix typo 2023-12-28 00:24:59 +00:00
crschnick
1c8a7a98c6 Print development command output 2023-12-28 00:24:52 +00:00
crschnick
722d239fda Make insights generation more robust 2023-12-28 00:24:21 +00:00
crschnick
d0d8e96e5d Update changelog 2023-12-28 00:24:10 +00:00
crschnick
e9c1622850 [stage] 2023-12-27 04:26:16 +00:00
crschnick
6e3b044fdc Update av alert 2023-12-27 04:23:11 +00:00
crschnick
404cd744b2 Check tray support everytime 2023-12-27 00:15:16 +00:00
crschnick
86e15cb6f2 Update AV alerts 2023-12-26 21:41:38 +00:00
crschnick
06186295e9 Use more conservative screen size fallback 2023-12-25 22:48:11 +00:00
crschnick
829deddc1d Add malwarebytes notice 2023-12-25 20:09:43 +00:00
crschnick
86758f06b7 Add garuda logo 2023-12-25 15:50:53 +00:00
crschnick
f980fdc45a Fix shutdown race condition 2023-12-25 15:40:31 +00:00
crschnick
b212d32d40 Add zorin logo 2023-12-25 15:09:47 +00:00
crschnick
e8ba39f383 [stage] 2023-12-24 18:24:06 +00:00
crschnick
4fecc1e58f Check entry status while scanning 2023-12-24 18:17:14 +00:00
crschnick
853e8a2b24 Update changelog 2023-12-24 17:01:26 +00:00
crschnick
dba1feb81f Add optional string command execution 2023-12-24 17:00:38 +00:00
crschnick
7bf4b6f4db Add close shortcut to other secondary windows 2023-12-24 15:49:55 +00:00
crschnick
cbcafb12e3 Add ability to clone connections for #124 2023-12-24 13:49:15 +00:00
crschnick
8c8dd99ad2 Rework choice pane handling for #125 2023-12-24 13:22:53 +00:00
crschnick
ea882bcc43 Fix possible null pointers 2023-12-23 22:51:48 +00:00
crschnick
f752f8b60f Verify screen bounds 2023-12-23 20:47:40 +00:00
crschnick
07964f4bef Prevent empty dragboards 2023-12-23 20:19:49 +00:00
crschnick
0eaa928441 Improve URL open error messages 2023-12-23 19:38:47 +00:00
crschnick
78f302befc Use own method to open hyperlinks 2023-12-23 19:30:53 +00:00
crschnick
17c8913bfc Fix possible NPE [stage] 2023-12-23 19:20:32 +00:00
crschnick
ff4587aae9 Improve font loading error message 2023-12-23 19:12:56 +00:00
crschnick
eeb0de4a4e Rework licensed features to include preview check 2023-12-23 12:31:40 +00:00
crschnick
741edbec0d Don't fail when we can't resolve our own executable 2023-12-22 23:32:18 +00:00
crschnick
c790870ae6 Rework insights popup 2023-12-22 23:31:48 +00:00
Christopher Schnick
752e04b955
Merge pull request #111 from muescha/patch-1
Update preferences_en.properties - fix typo
2023-12-22 16:45:55 +01:00
crschnick
debe83d2f4 Widen acceptable store exceptions 2023-12-22 13:50:18 +00:00
crschnick
af8904ea7a Add setting to debug local shell 2023-12-22 12:20:35 +00:00
crschnick
e8566ea137 Verify font loading 2023-12-22 12:20:20 +00:00
crschnick
2a8bd7b2a0 Improve some file browser action names 2023-12-22 11:49:25 +00:00
crschnick
da71e1172c Ignore some expression exceptions 2023-12-22 11:20:46 +00:00
crschnick
a609ccbde2 Clean up tray 2023-12-22 11:20:31 +00:00
Muescha
21b3dcd921
Update preferences_en.properties - fix typo 2023-12-21 23:10:52 +01:00
crschnick
9b9fed3ec6 Asahi Linux fixes 2023-12-21 16:20:55 +00:00
crschnick
270ef73cc1 Improve loading spinner handling 2023-12-21 13:42:27 +00:00
crschnick
7d03e22af3 Handle data lock race condition 2023-12-21 10:51:56 +00:00
crschnick
8e1665ba27 Properly define webview user data directory 2023-12-21 10:50:05 +00:00
crschnick
09146d1241 Fix NPE 2023-12-20 10:47:54 +00:00
crschnick
5549f04541 [stage] 2023-12-20 10:06:52 +00:00
crschnick
1ad11a38a1 Fixes for powershell remote sessions 2023-12-20 09:48:02 +00:00
crschnick
aa71a72f58 Ssh key file fixes 2023-12-19 15:31:06 +00:00
crschnick
09a419f628 Always use login shells 2023-12-19 15:29:56 +00:00
crschnick
4daa183dad Rework shell terminal command handling and clipboards 2023-12-19 14:55:50 +00:00
crschnick
2af59af190 Rework terminal launching, path caching, and state management 2023-12-19 06:50:15 +00:00
crschnick
4fda66e7db Handle pro features in menu 2023-12-18 06:14:55 +00:00
crschnick
32468d1d18 Focus askpass text 2023-12-18 04:37:10 +00:00
crschnick
4760b4a443 Rework browser actions for vscode action 2023-12-18 04:24:51 +00:00
crschnick
b668028547 [release] 2023-12-16 03:46:42 +00:00
crschnick
7f8df4f10a [stage] 2023-12-15 07:33:38 +00:00
crschnick
09b5a0ad3a Update changelog 2023-12-15 07:16:46 +00:00
crschnick
27a3aedc00 Improve git storage handling 2023-12-15 05:45:36 +00:00
crschnick
cda3f0207e More environment variable fixes 2023-12-15 03:18:16 +00:00
crschnick
215b78b75e Fix environment variable handling in command builder 2023-12-15 02:57:54 +00:00
crschnick
3afbb2ed0a Fix ssh git integration 2023-12-14 03:59:20 +00:00
crschnick
102b69f261 Update changelog 2023-12-13 00:19:35 +00:00
crschnick
843e36de0c Add color to mini sections 2023-12-12 19:03:26 +00:00
crschnick
30d7cfcca9 Expose license check state 2023-12-12 16:59:45 +00:00
crschnick
0dce414bf7 Update readme 2023-12-12 16:59:05 +00:00
crschnick
6df9169e38 Rework connection sharing 2023-12-11 17:52:45 +00:00
crschnick
42f83e331a Improve corruption error messages 2023-12-11 16:49:28 +00:00
crschnick
2c89984ffb Update discord link 2023-12-11 16:46:00 +00:00
crschnick
429900e748 Add discord banner 2023-12-11 16:18:53 +00:00
crschnick
95be983c18 Update readme 2023-12-11 16:13:25 +00:00
crschnick
08bf3669ba Fix history context menu 2023-12-10 16:46:05 +00:00
crschnick
695d478878 [stage] 2023-12-10 15:42:24 +00:00
crschnick
efe4a8cc9f Fix cd synchronization 2023-12-10 15:39:34 +00:00
crschnick
e17c5cdad3 Add history bar for navbar 2023-12-10 15:11:50 +00:00
crschnick
c40c9c1f84 Show unavailable scan targets 2023-12-10 13:44:19 +00:00
crschnick
575cea68e3 Add documentation for installation detection 2023-12-09 12:08:04 +00:00
crschnick
83435b0642 Add coc 2023-12-09 02:49:02 +00:00
crschnick
8b547f79ee [stage] 2023-12-09 02:09:59 +00:00
crschnick
e1f62830b1 Rework file references for ssh keys 2023-12-08 09:11:42 +00:00
crschnick
7574ff8666 Add support for JetBrains editors 2023-12-08 09:10:56 +00:00
crschnick
6ce40962ac Add easy option to discard git output 2023-12-08 06:06:13 +00:00
crschnick
bdcc6a1295 Properly update state on children refresh 2023-12-08 04:39:44 +00:00
crschnick
0a8881a9cf Listen for cancel shortcuts in alerts, for #110 2023-12-08 03:03:19 +00:00
crschnick
b134be7fce Add chmod 755 action 2023-12-06 16:23:59 +00:00
crschnick
aa76ce1a94 Fix some root categories being deletable 2023-12-06 16:23:50 +00:00
crschnick
5274d58c36 [release] 2023-12-06 01:40:24 +00:00
crschnick
d84fafd4a8 Fix askpass caching 2023-12-05 21:50:29 +00:00
crschnick
8a567380df Lock storage io 2023-12-05 20:17:44 +00:00
crschnick
b061ab7a34 Always run exit timer 2023-12-05 19:06:17 +00:00
crschnick
ce32613dd4 Add threading debug 2023-12-05 19:05:33 +00:00
crschnick
b1f5c0549c Update changelog 2023-12-05 16:25:34 +00:00
crschnick
fb70b6766b Add chmod browser action 2023-12-05 16:04:13 +00:00
crschnick
d9e496f1cb [release] 2023-12-05 14:43:51 +00:00
crschnick
efe75a7332 Change user name query 2023-12-04 23:00:05 +00:00
crschnick
c2e5e0fa0d Small browser fixes 2023-12-04 16:40:30 +00:00
crschnick
dcb43a43ab Improve local file normalization 2023-12-04 14:49:37 +00:00
crschnick
6ef7f35887 Small fixes 2023-12-04 14:49:25 +00:00
crschnick
4afa34378b [stage] 2023-12-03 17:25:50 +00:00
crschnick
49755b2785 Clear cache passwords on repeated request 2023-12-03 16:16:35 +00:00
crschnick
cb0cb97af3 Rework command building and exit codes 2023-12-03 12:31:44 +00:00
crschnick
2b95ea5d6e Improve window clamping 2023-12-02 06:58:43 +00:00
crschnick
d04271f632 [stage] 2023-12-02 04:18:45 +00:00
crschnick
29ac6b85ca [stage] 2023-12-02 03:22:29 +00:00
crschnick
501d64fde2 More git storage fixes 2023-12-02 02:57:47 +00:00
crschnick
c5f94a7b0b More git storage rework 2023-12-02 01:08:11 +00:00
crschnick
71e0ce35e8 Improve beacon connection initialization and timeout 2023-12-01 23:31:28 +00:00
crschnick
7a4e79af4f Add timer to shutdown procedure 2023-12-01 23:29:20 +00:00
crschnick
faed51f996 Improve distribution type initialization 2023-12-01 23:28:31 +00:00
crschnick
e3265a573d Timeout fixes [stage] 2023-12-01 00:28:05 +00:00
crschnick
d5b3d1ac65 Update changelog 2023-12-01 00:05:41 +00:00
crschnick
9eaafa204a Fix elevation failures 2023-11-30 22:24:23 +00:00
crschnick
a4ba5d9726 Don't block on shutdown kill 2023-11-30 17:29:51 +00:00
crschnick
ba843e8b13 Add startup connection timeout 2023-11-30 17:12:16 +00:00
crschnick
67b2a7957f Fix commands getting stuck on start failure 2023-11-29 22:15:40 +00:00
crschnick
700f539dfb Fix macos quit not working 2023-11-29 19:25:32 +00:00
crschnick
68895cfb55 Fix clear issue for warp properly 2023-11-29 18:01:59 +00:00
crschnick
8e6d1cafb4 More fixes for git storage 2023-11-29 17:31:17 +00:00
crschnick
6b9f32039c Bump gradle 2023-11-29 17:30:10 +00:00
crschnick
031b0c101b [stage] 2023-11-28 22:25:58 +00:00
crschnick
66a928fc6f Hide sharing option by default 2023-11-28 22:25:08 +00:00
crschnick
195300607e Rework git storage handling some more 2023-11-28 18:22:56 +00:00
crschnick
1853f1a9b5 Update tag notice 2023-11-28 14:58:43 +00:00
crschnick
68c42bfc3d Rework operation mode error handling 2023-11-28 14:54:52 +00:00
crschnick
1ea317d05c Update changelog 2023-11-27 23:13:02 +00:00
crschnick
fa1e3ee52d Check for storage load for git storage 2023-11-27 23:10:49 +00:00
crschnick
be36ad0554 Small ssh fixes 2023-11-27 23:10:23 +00:00
crschnick
df9594ae86 Remove unnecessary platform initialization code 2023-11-27 23:09:21 +00:00
crschnick
b146b9bb27 Disable help 2023-11-27 16:57:18 +00:00
crschnick
767c87670d Small text fixes 2023-11-27 16:14:45 +00:00
crschnick
98721f8dc5 Additional git fixes [stage] 2023-11-27 14:21:43 +00:00
crschnick
08c1586240 Rework git storage functionality 2023-11-27 13:29:17 +00:00
crschnick
37366ff1a8 Always activate iterm to put it into foreground 2023-11-26 18:39:35 +00:00
crschnick
aa00cd6521 Improve git storage functionality 2023-11-26 15:54:14 +00:00
crschnick
f5eebdae73 Try to fix macos self-updater 2023-11-26 08:53:36 +00:00
crschnick
ecd8ab24ce Fix NPE 2023-11-26 08:53:21 +00:00
crschnick
110a84d8fe Clamp window sizes to screen 2023-11-25 17:49:15 +00:00
crschnick
795b00eb8a Fix file execute action not launching script 2023-11-25 10:37:13 +00:00
crschnick
dbec0b8fd7 Script order fixes [release] 2023-11-24 09:11:23 +00:00
crschnick
88c5c1c7ac Focus provider combobox 2023-11-24 08:36:27 +00:00
crschnick
774b005588 Update changelog 2023-11-24 08:19:41 +00:00
crschnick
1aadbc3a76 Fix browser script integration 2023-11-24 07:24:02 +00:00
crschnick
df517b9d74 Use atlantafx bss stylesheet 2023-11-23 15:37:55 +00:00
crschnick
2494d9d45f Use binary stylesheets when possible 2023-11-22 19:24:14 +00:00
crschnick
32e3b2341b Try to fix macos terminal race condition 2023-11-22 09:39:03 +00:00
crschnick
68624d8648 Fix changelog window order 2023-11-22 09:38:42 +00:00
crschnick
7c14674754 Fix directory icons [release] 2023-11-22 06:53:00 +00:00
crschnick
d561ea89f3 Update changelog 2023-11-22 05:45:39 +00:00
crschnick
5a847e047e Fix browser file icon matching 2023-11-21 16:11:25 +00:00
crschnick
6c96646476 Browser fixes 2023-11-21 15:23:41 +00:00
crschnick
86b25afb11 Add security policy 2023-11-21 13:21:32 +00:00
crschnick
fe2a1ffa5e Fix dialect command check 2023-11-21 11:39:46 +00:00
crschnick
77df569c46 Small fixes 2023-11-21 04:20:01 +00:00
crschnick
1fdb46eee5 Clear file system exceptions in a special way 2023-11-21 03:03:39 +00:00
crschnick
e373a2d241 Small fixes 2023-11-20 06:57:54 +00:00
crschnick
757c25bbc1 Dynamically handle missing exit codes 2023-11-20 04:32:39 +00:00
crschnick
5466fd916a More Windows fixes 2023-11-20 03:39:05 +00:00
crschnick
087fcd4b82 macos fixes 2023-11-18 07:40:32 +00:00
crschnick
bdc2dacf95 [release] 2023-11-18 05:17:12 +00:00
crschnick
76c29f719b Appearance fixes 2023-11-18 05:08:46 +00:00
crschnick
56bd49b932 Buffering fixes 2023-11-18 03:51:50 +00:00
crschnick
eac9504e14 Merge branch 1.7.5 into master [release] 2023-11-18 01:29:38 +00:00
crschnick
63ab4cfef5 Padding fixes 2023-11-15 03:36:30 +00:00
crschnick
b91eb0fda5 Merge branch 1.7.3-fixes into master 2023-11-15 03:25:18 +00:00
crschnick
36a9a78896 [release] 2023-11-04 05:52:19 +00:00
crschnick
bc7bde024a Merge branch 1.7.3 into master 2023-11-04 05:36:47 +00:00
Christopher Schnick
8ef97ccc9f
Merge pull request #91 from fadkeabhi/patch-1
doc(README): remove typo
2023-10-27 05:06:03 +02:00
crschnick
1e75827fd7 Add more os icons [release] 2023-10-26 17:37:31 +00:00
crschnick
685813863b Small fixes [release] 2023-10-26 17:04:14 +00:00
crschnick
6cdc728aa1 Add nixos logo 2023-10-26 16:22:29 +00:00
crschnick
8a252a1137 Store entry refresh fixes 2023-10-26 16:22:07 +00:00
crschnick
3a510b0e7a Signing fixes [stage] 2023-10-25 21:49:27 +00:00
crschnick
4df64b5e9e Bug fixes 2023-10-25 20:44:13 +00:00
crschnick
8b1754ca4c [release] 2023-10-25 17:47:32 +00:00
crschnick
64cb93b079 Fix version [release] 2023-10-25 17:17:43 +00:00
crschnick
6bd96563d0 Small visual fixes [release] 2023-10-25 17:17:07 +00:00
crschnick
b410d8e863 Small bug fixes 2023-10-25 15:38:57 +00:00
crschnick
9f0eb217f8 Add terminals to list 2023-10-25 14:24:01 +00:00
crschnick
119ebcbf6c Add more terminal support 2023-10-25 14:21:50 +00:00
crschnick
f800ecd46d Fix category display not working on init 2023-10-25 14:21:21 +00:00
crschnick
54e75ce6bd Fix javafx bundling 2023-10-25 14:20:49 +00:00
crschnick
5d55da8821 Use javafx jmods instead of jars [stage] 2023-10-24 19:01:38 +00:00
crschnick
656be93d44 Improve view state initialization 2023-10-23 19:40:13 +00:00
crschnick
d1c2cc27af Rework licensing [stage] 2023-10-23 18:38:13 +00:00
crschnick
d38e6ea289 [stage] 2023-10-22 14:50:58 +00:00
crschnick
1faff72321 Category choice comp fixes and storage dirty optimizations 2023-10-22 14:10:08 +00:00
crschnick
d87f74fffc Bring scripts to shell sessions 2023-10-22 10:09:08 +00:00
crschnick
1bc650d7ac Improve path error message for terminals 2023-10-21 21:11:35 +00:00
crschnick
ca3acc2d1d Don't send empty reports now for real 2023-10-21 21:11:14 +00:00
crschnick
45e501f4fd Remove fxtrayicon dep 2023-10-21 21:10:33 +00:00
crschnick
72f91f39d0 [stage] 2023-10-21 10:30:35 +00:00
crschnick
5e1fc3910a Tray GTK fixes 2023-10-21 10:13:08 +00:00
crschnick
9ef6dcacb6 Create logo_24x24_t.png 2023-10-21 09:39:15 +00:00
crschnick
5ec88ccc9e Tray fixes 2023-10-21 09:07:03 +00:00
crschnick
48a43ba5a5 Use own implementation for tray icon 2023-10-21 08:54:14 +00:00
crschnick
03b09c9c8f [stage] 2023-10-20 14:38:59 +00:00
ABHISHEK FADAKE
fb9b398d7e
doc(README): remove typo
Corrected "sever" to "server" in the README.md file.
2023-10-20 19:09:35 +05:30
crschnick
15088095e2 [stage] 2023-10-20 13:34:49 +00:00
crschnick
b7612237e1 Small bug fixes [release] 2023-10-20 12:24:09 +00:00
crschnick
d14c244f9a [release] 2023-10-20 10:39:50 +00:00
crschnick
118a1ababc Small fixes [stage] 2023-10-20 10:31:27 +00:00
crschnick
6a6a155056 [release] 2023-10-20 09:42:19 +00:00
crschnick
4f86e6231b Small bug fixes 2023-10-20 09:34:56 +00:00
crschnick
7c2b07f560 Small bug fixes 2023-10-20 07:52:43 +00:00
crschnick
1f3afa3ad4 Small bugfixes and refactor 2023-10-19 16:52:35 +00:00
crschnick
e78dba1f2e Small various fixes 2023-10-18 12:10:55 +00:00
crschnick
53185a603f Fix signing [stage] 2023-10-18 07:10:15 +00:00
crschnick
06d9c777fc Small fixes and polishing 2023-10-18 04:17:34 +00:00
crschnick
7995d95b8d More performance optimizations 2023-10-16 10:53:32 +00:00
crschnick
c80a31bffe Performance improvements 2023-10-16 03:37:22 +00:00
crschnick
b9ebce0771 Optimizations for large connection amounts 2023-10-15 00:04:20 +00:00
crschnick
1e990389f4 Refactor 2023-10-14 21:24:46 +00:00
crschnick
ee740b43f8 Small fixes 2023-10-14 16:13:04 +00:00
crschnick
e34fd638a3 Fix window not shown on second startup 2023-10-13 19:43:52 +00:00
crschnick
0d065dc14e Store children rework and script fixes 2023-10-12 15:23:51 +00:00
crschnick
09052de9ee Refactor 2023-10-11 10:15:58 +00:00
crschnick
a37821e22c Various bug fixes for updater, exit behaviour, error handling 2023-10-11 09:54:02 +00:00
crschnick
b507ed8a11 Small script fixes 2023-10-10 10:37:22 +00:00
crschnick
bc5887537a Small bug fixes [stage] 2023-10-10 08:36:13 +00:00
crschnick
cad89dd337 Shell fixes for bsd and opnsense 2023-10-10 05:41:30 +00:00
crschnick
7638d8404a Various small fixes 2023-10-09 00:49:25 +00:00
crschnick
65a1ee2e73 Color rework and refactor 2023-10-08 04:39:53 +00:00
crschnick
ee41792491 macos fixes 2023-10-07 17:19:23 +00:00
crschnick
f277cc0a37 Color improvements and terminal fixes 2023-10-07 16:33:01 +00:00
crschnick
993e1d5446 Add support for connection colors 2023-10-06 20:41:01 +00:00
crschnick
45e3323828 [stage] 2023-10-05 23:52:02 +00:00
crschnick
43d7e0830c More script rework, state rework, category fixes, and bug fixes 2023-10-05 23:40:52 +00:00
crschnick
87d1d45cae Fix NPE 2023-10-05 16:25:33 +00:00
crschnick
34b9e1e2c5 Nix fixes 2023-10-05 15:58:54 +00:00
crschnick
cb3b5084fb Gradle build fixes 2023-10-05 15:19:48 +00:00
crschnick
f5d317e2ab Update readmes 2023-10-05 11:53:52 +00:00
crschnick
e7d595c70a Fix script names 2023-10-05 11:53:27 +00:00
crschnick
680dd75abe Prepare for nixpkg 2023-10-04 17:19:44 +00:00
crschnick
216a950d5c Fix broken links in changelog 2023-10-04 17:19:32 +00:00
crschnick
1b3bf708e6 Update PTB title 2023-10-04 17:19:22 +00:00
crschnick
d96a38d7b2 Merge branch 1.7 into master 2023-10-04 14:34:03 +00:00
crschnick
2429a70c42 Improve download section 2023-09-30 07:04:09 +00:00
crschnick
b2bd9f093f Add missing Dracula theme 2023-09-30 07:03:39 +00:00
crschnick
8ffd1802a1 Small fixes 2023-09-29 06:35:55 +00:00
crschnick
b72ade3587 Fix opacity type 2023-09-28 06:48:20 +00:00
crschnick
80001ffafa Update readmes and links 2023-09-28 06:30:53 +00:00
crschnick
631b6edc30 Improve custom themes 2023-09-28 05:06:18 +00:00
crschnick
8ae8adbf8c Improve theming, make window opacity dynamic, and fix platform error handling 2023-09-28 04:47:03 +00:00
crschnick
ad5514124c Don't send empty reports 2023-09-27 06:51:32 +00:00
crschnick
bd8eea3159 [release] 2023-09-27 03:37:24 +00:00
crschnick
10dcdbc5e2 Section bug fixes 2023-09-27 03:01:08 +00:00
crschnick
227bcb8015 Merge branch acc into master 2023-09-27 00:47:51 +00:00
crschnick
f295286a8b Fix launch 2023-09-13 08:16:20 +00:00
crschnick
de0a235d91 Update install script 2023-09-13 08:06:00 +00:00
crschnick
d90db71ee7 Fix quoting 2023-09-13 07:48:33 +00:00
crschnick
07d08014d5 Stage install fixes 2023-09-13 06:58:32 +00:00
crschnick
72757331c5 Fix staging links 2023-09-13 06:36:02 +00:00
crschnick
d629fdddc8 Add model info to branching actions 2023-09-07 19:18:55 +00:00
crschnick
f382f30476 Add multipurpose cache support 2023-09-07 16:42:52 +00:00
crschnick
c19774e141 Disable incomplete ash support on master 2023-08-30 17:24:10 +00:00
crschnick
dbb66c3d31 Improve naming for scan operations 2023-08-27 09:30:14 +00:00
crschnick
cc981384b6 Add more docker actions [stage] 2023-08-27 08:42:57 +00:00
crschnick
141d4b32d6 Add ash support 2023-08-27 06:29:06 +00:00
crschnick
ccd71c734c Remove log level from settings 2023-08-27 05:23:15 +00:00
crschnick
fbd7a6e1f4 Fix possible NPE 2023-08-27 04:52:08 +00:00
crschnick
326961c20d Bump gradle version 2023-08-26 16:02:47 +00:00
crschnick
94db70bb2e Check for existence of target files for move operations 2023-08-26 15:40:47 +00:00
crschnick
3f6bba601d Add support for elementary terminal 2023-08-26 13:07:39 +00:00
crschnick
e85945f25a Add elementary os logo 2023-08-26 09:08:34 +00:00
crschnick
6c4feb3539 More gradle build fixes 2023-08-26 07:28:19 +00:00
crschnick
b2a6549fb6 Gradle build fixes 2023-08-26 07:19:41 +00:00
crschnick
3de789500e Improve download notice 2023-08-26 03:13:08 +00:00
crschnick
0b163394d2 Fix drag and drop NPE 2023-08-25 06:27:52 +00:00
crschnick
a10353fc1a Fix vscode localization 2023-08-23 09:08:38 +00:00
crschnick
ae187e4c1b Fix header size 2023-08-23 09:05:12 +00:00
crschnick
aeb1904cf3 Update readmes 2023-08-23 09:04:32 +00:00
crschnick
2da1b6594f Add support for vscode insider for #84 2023-08-23 01:36:34 +00:00
crschnick
6a8707bfcf Make update check more robust 2023-08-22 10:03:32 +00:00
crschnick
0812ae3113 Improve passcode checks [stage] 2023-08-21 23:41:55 +00:00
crschnick
52a8ba02cb Fix testing leftover [stage] 2023-08-21 23:40:33 +00:00
crschnick
b98c6ca2c5 Check for passcode prompts [stage] 2023-08-21 23:38:56 +00:00
crschnick
48a403e8c9 Use consistent script names 2023-08-21 23:07:32 +00:00
crschnick
b2fa7bf0bb Fix build [release] 2023-08-21 20:55:41 +00:00
crschnick
018c389534 Reduce strictness of temp check 2023-08-21 20:35:27 +00:00
crschnick
064ac12b36 Improve error message 2023-08-21 20:33:21 +00:00
crschnick
00d549d2ac Fix debug script permissions 2023-08-21 02:29:17 +00:00
crschnick
ca2468797d Don't show logs button when there are no logs 2023-08-20 22:55:49 +00:00
crschnick
62d9252e59 macos fixes [stage] 2023-08-20 22:12:18 +00:00
crschnick
a5d35ad693 [stage] 2023-08-20 21:45:31 +00:00
crschnick
99cc92eb8b Rework connection timeouts 2023-08-20 21:44:55 +00:00
crschnick
99da294b0a Add macos specific handlers 2023-08-20 19:10:12 +00:00
crschnick
b9dff411fb Immediately hide tray icon 2023-08-20 19:09:59 +00:00
crschnick
80f44608a1 Disable browser tab hover timer 2023-08-20 19:09:48 +00:00
crschnick
76cbf75049 mac dist fixes [stage] 2023-08-20 09:42:34 +00:00
crschnick
28ab1e03ba Attempt to restructure elevation 2023-08-20 08:29:48 +00:00
crschnick
4180b26b8e macOS fixes 2023-08-20 06:51:17 +00:00
crschnick
a25d629f01 Fixes [stage] 2023-08-20 02:59:19 +00:00
crschnick
3097d1e02a [stage] 2023-08-20 02:33:53 +00:00
crschnick
0cd8a0916b Fix choco updater failing 2023-08-20 01:44:18 +00:00
crschnick
f0e8385e2b Fix NPE in browser when no editor was set 2023-08-20 01:43:47 +00:00
crschnick
a80c22f34c Fix int field paste breaking it 2023-08-20 01:43:27 +00:00
crschnick
f736ce1a7a Fix dist script permissions 2023-08-20 01:43:14 +00:00
crschnick
a9ce57f6c5 Fix NPE on file rename undo 2023-08-19 20:28:19 +00:00
crschnick
ba295e975e Fix outdated data in scan dialog 2023-08-19 20:28:03 +00:00
crschnick
1d55692d26 Check for 32bit notepad++ installation 2023-08-19 19:08:56 +00:00
crschnick
abcf67099b Small fixes 2023-08-18 04:37:56 +00:00
crschnick
de9ef6fb6c Small fixes [stage] 2023-08-17 16:14:54 +00:00
crschnick
59c592dc7e Fix arch handling of installation script 2023-08-17 03:31:20 +00:00
crschnick
4f32420f89 Fix aarch64 check 2023-08-17 03:08:40 +00:00
crschnick
d3869c8818 Small fixes 2023-08-17 03:03:05 +00:00
crschnick
6dd476a40a Shell exit fixes 2023-08-16 23:42:39 +00:00
crschnick
267e2a6968 Bump version [release] 2023-08-16 18:45:43 +00:00
crschnick
ee6d4cb570 Script generation fixes [release] 2023-08-16 18:27:05 +00:00
crschnick
179ace133f Relax location check on macOS 2023-08-16 17:40:27 +00:00
crschnick
b50a6a3955 Separate SVG logic 2023-08-16 17:02:56 +00:00
crschnick
a938f23a81 Various fixes 2023-08-16 16:49:11 +00:00
crschnick
11293012e1 Use proper exit codes 2023-08-16 16:48:26 +00:00
crschnick
378e9bff6f Small browser fixes 2023-08-16 16:48:15 +00:00
crschnick
7f85b4b535 Improve terminal clearing 2023-08-15 19:34:46 +00:00
crschnick
d1953fbe10 Improve options validation display 2023-08-15 11:05:49 +00:00
crschnick
ce6a56d234 Improve automatic scan dialog 2023-08-14 17:17:34 +00:00
crschnick
63269bbbfb Rework script building 2023-08-14 17:16:50 +00:00
crschnick
b16cb3ee59 Better filter for store tree view 2023-08-14 17:16:28 +00:00
crschnick
1f8f078523 Automatically close modal overlays with enter 2023-08-14 17:16:09 +00:00
crschnick
ce41640044 Fix paste not working in file browser 2023-08-14 17:15:39 +00:00
crschnick
e836fdf2a3 Try to set class name for Linux manually 2023-08-14 13:19:55 +00:00
crschnick
be95f78426 Add arm downloads 2023-08-13 12:40:25 +00:00
crschnick
9b9af02f3e [release] 2023-08-13 10:13:40 +00:00
crschnick
2d1549a328 Fix date sorting 2023-08-13 09:31:13 +00:00
crschnick
3bf0594920 Fix path lookup for applications 2023-08-12 17:26:01 +00:00
crschnick
b995277e2e Create changelog 2023-08-12 13:05:16 +00:00
crschnick
58db351c26 Bump version [stage] 2023-08-12 10:55:49 +00:00
crschnick
4961f53c26 Add support for gnome text editor 2023-08-12 10:30:30 +00:00
crschnick
54aefedd28 Fix javafx dependency arch across operating systems 2023-08-12 10:03:36 +00:00
crschnick
830d312245 Clear screen for warp 2023-08-12 08:54:25 +00:00
crschnick
0c22b62699 Attempt to clear screen in mac terminal 2023-08-12 07:54:46 +00:00
crschnick
e44b6ddc96 Fix sort mode serialization 2023-08-12 07:54:38 +00:00
crschnick
473974ada9 Implement sorting 2023-08-11 18:52:05 +00:00
crschnick
202f9e4939 Hide certain stores from tree [stage] 2023-08-11 14:54:42 +00:00
crschnick
d000bc89c8 Improve storage update propagation 2023-08-11 12:40:22 +00:00
crschnick
fe7586cbec Add ability to accept host keys 2023-08-11 08:43:43 +00:00
crschnick
cdf205e705 Small fixes 2023-08-11 06:48:54 +00:00
crschnick
6666b5bd65 Update readmes 2023-08-10 16:40:48 +00:00
crschnick
b7704b8013 Make writing log files optional 2023-08-10 15:41:04 +00:00
crschnick
8199fba5cc Search path for terminals on windows as well 2023-08-10 15:40:40 +00:00
crschnick
8b695c7ba2 Fix NPE 2023-08-10 15:40:17 +00:00
crschnick
33ca8cca9c [release] 2023-08-10 10:26:03 +00:00
crschnick
3b92f7d093 macOS fixes 2023-08-09 14:12:27 +00:00
crschnick
9932895ff5 Remove office category 2023-08-09 14:11:25 +00:00
crschnick
6ca4c9d3cb Secret serialization fixes 2023-08-09 14:11:14 +00:00
crschnick
fab26e130e Attempt to set title and improve tray resolutions 2023-08-09 06:41:39 +00:00
crschnick
a67ee784b3 Tray improvements 2023-08-09 06:22:12 +00:00
crschnick
22dc0b2579 Fix permission issue 2023-08-09 06:13:35 +00:00
crschnick
bea3c7c693 Try to improve tray icon look 2023-08-09 05:52:10 +00:00
crschnick
06f62c96fb Small fixes 2023-08-09 05:51:57 +00:00
crschnick
c84db86457 Refactor and cleanup 2023-08-08 10:04:32 +00:00
crschnick
d14088d9bc Appearance improvements [stage] 2023-08-08 09:12:38 +00:00
crschnick
908b960977 Fix tray state becoming inconsistent 2023-08-08 05:46:07 +00:00
crschnick
3d32d6cd84 Rework password handling 2023-08-08 04:24:07 +00:00
crschnick
f8b56ab774 Ssh secret input rework 2023-08-06 15:00:40 +00:00
crschnick
e6340e77c4 More polishing [stage] 2023-08-06 04:36:29 +00:00
crschnick
069b681f21 Add select all option to scan dialog 2023-08-05 10:45:00 +00:00
crschnick
b0881a2909 Rework states, fixed children, and storage logic [stage] 2023-08-05 10:13:17 +00:00
crschnick
5c6b98fd14 Fix operation mode handling when tray is not available 2023-08-04 05:03:24 +00:00
crschnick
03b5b67a42 Improve store toggles [stage] 2023-08-04 00:02:26 +00:00
crschnick
245ba1a238 Initial split for pref categories 2023-08-03 22:46:53 +00:00
crschnick
1553ca95f4 Fix macos terminal hang 2023-08-03 19:11:10 +00:00
crschnick
8c4c68f24a Use platform threads in development mode instead of virtual ones 2023-08-03 18:30:46 +00:00
crschnick
454e8aea75 Resilience fixes 2023-08-03 17:33:34 +00:00
crschnick
56930c07a6 More polishing 2023-08-03 17:20:53 +00:00
crschnick
c554482bb1 [stage] 2023-08-02 19:40:31 +00:00
crschnick
44bcdce14a More polishing 2023-08-02 18:30:19 +00:00
crschnick
1f2919278f Refactor plus several fixes 2023-08-01 19:34:08 +00:00
crschnick
5be0748c9f Add ssh config support plus polishing 2023-07-30 20:22:32 +00:00
crschnick
c70d6da314 Various fixes and cleanup [stage] [noannounce] 2023-07-29 15:14:38 +00:00
crschnick
0b31eed2a5 [stage] [noannounce] 2023-07-29 12:36:04 +00:00
crschnick
9e085afb46 [release] 2023-07-22 13:47:02 +00:00
crschnick
c9be795984 Fix sidebar crusher 2023-07-22 13:06:18 +00:00
crschnick
cc0763c7c1 Properly handle launcher errors 2023-07-21 18:56:33 +00:00
crschnick
c2544d15d2 [release] 2023-07-21 18:40:43 +00:00
crschnick
488190bd5e Fix startup bug in Windows [release] 2023-07-21 18:39:34 +00:00
crschnick
c92dbf762d Look in path for windows applications 2023-07-21 17:01:17 +00:00
crschnick
d01bf6bd48 Prepare for fish support 2023-07-21 17:00:56 +00:00
crschnick
df85aa08a1 Small fixes [release] 2023-07-21 12:38:16 +00:00
crschnick
15e2986577 Fix AUR install 2023-07-20 14:14:08 +00:00
crschnick
3d2f5b4adf Update scripts 2023-07-20 14:11:33 +00:00
crschnick
9db4cc300b Add bitdefender notice 2023-07-20 11:19:09 +00:00
crschnick
6c1700cd01 [stage] 2023-07-20 10:53:05 +00:00
crschnick
c138c5867f AUR fixes 2023-07-20 10:39:16 +00:00
crschnick
48d53a54a8 Prepare aur build 2023-07-19 13:53:25 +00:00
crschnick
a6372f8872 Build fixes 2023-07-19 10:17:20 +00:00
crschnick
bf5b8c8857 Properly use startup behavior setting 2023-07-19 10:17:12 +00:00
crschnick
8143f69da2 Update to graalvm 20 and some small adjustments [stage] 2023-07-19 08:50:19 +00:00
crschnick
3c7967d2c0 [stage] 2023-07-18 12:46:47 +00:00
crschnick
c8cf6aa3fb Add dynamic tunnels 2023-07-18 11:53:46 +00:00
crschnick
a218f9ac35 Improve remote file chooser 2023-07-17 19:34:36 +00:00
crschnick
2172de1865 Improve grid layout 2023-07-17 07:15:43 +00:00
crschnick
eb341b0c08 Hide ssh file system choice plus refactor 2023-07-17 06:03:47 +00:00
crschnick
324a48f157 File browser transfer adjustments 2023-07-17 01:03:12 +00:00
crschnick
6909019231 Fix kitty name 2023-07-15 17:52:42 +00:00
crschnick
e804951763 Add mac terminals 2023-07-15 17:49:48 +00:00
crschnick
e73c16892c Add support for more terminal and editors 2023-07-15 17:29:38 +00:00
crschnick
4631fc88ba Restyle sidebar 2023-07-15 14:39:09 +00:00
crschnick
b02ac3139f [release] 2023-07-13 14:08:58 +00:00
crschnick
ec96c9fabf Fix docker inspect elevation 2023-07-13 12:59:23 +00:00
crschnick
f72a8a8de7 Update changelog 2023-07-13 12:46:14 +00:00
crschnick
1b9a2e9f3b Don't show move dialog for copies 2023-07-13 12:46:03 +00:00
crschnick
23736d41c0 Fix connection creation dialog error message issues 2023-07-13 07:45:23 +00:00
crschnick
888fa3d92b Fix possible NPE 2023-07-13 07:04:41 +00:00
crschnick
286c573ebd Fix filter field becoming stuck in a loop and freezing up 2023-07-13 06:35:46 +00:00
crschnick
df2d4c9ca9 Clarify portable update notice 2023-07-12 11:02:37 +00:00
crschnick
50e3dffd9b Add section for malware detection 2023-07-12 08:32:45 +00:00
crschnick
47cb5a8b2d Rework temp directory handling again 2023-07-12 08:09:51 +00:00
crschnick
7eea7c0fc3 Rework temporary directory handling [stage] 2023-07-11 16:56:06 +00:00
crschnick
4048e3b730 Fix show details action failing for windows root drives 2023-07-11 06:59:39 +00:00
crschnick
d1216811c8 Check tempory directory permissions [stage] 2023-07-11 05:10:49 +00:00
crschnick
40878d3a4d [stage] 2023-07-11 04:41:03 +00:00
crschnick
5861007eff Interpret empty value as null in integer field 2023-07-10 08:13:08 +00:00
crschnick
9437791629 Fix race condition when closing browser tabs 2023-07-10 08:12:54 +00:00
crschnick
db18b54c93 [release] 2023-07-10 04:47:33 +00:00
crschnick
5a47eaf3d2 Use proper exit code on unknown state 2023-07-10 04:15:13 +00:00
crschnick
2e54c9368d Determine vscode installation directory on windows from Path 2023-07-10 04:00:07 +00:00
crschnick
b3f299dbe6 [stage] 2023-07-09 14:04:56 +00:00
crschnick
35ca2ae716 Improve substitution of variables in custom commands 2023-07-09 12:53:09 +00:00
crschnick
068e79bbe6 Fix tempory directory not being cleaned 2023-07-09 12:28:44 +00:00
crschnick
b2d685f523 Properly implement storage directory switching 2023-07-09 11:39:23 +00:00
crschnick
4c08385098 Check whether platform has already exited in terminal error handler 2023-07-09 07:11:36 +00:00
crschnick
b05d24dd14 Make font loading more robust 2023-07-09 07:11:03 +00:00
crschnick
5cfe1eddfd Fix application launch debug output 2023-07-09 06:51:34 +00:00
crschnick
e9982b4903 Clarify staging notice 2023-07-09 06:51:24 +00:00
crschnick
d2e71d0a27 [stage] 2023-07-09 06:18:16 +00:00
crschnick
99c93cee84 Clarify xfce terminal version 2023-07-09 04:19:40 +00:00
crschnick
54cc66d0d2 Add method to check shell initialization 2023-07-09 04:19:21 +00:00
crschnick
c93efb6ae0 Check for shell initialization in terminal error handler 2023-07-09 04:18:43 +00:00
crschnick
53813f0cd1 Improve custom program setting description 2023-07-09 04:08:50 +00:00
crschnick
43b2cde018 Fix null pointer in case no external program to launch has been set 2023-07-09 04:08:24 +00:00
crschnick
6868f6567b Fix out of bounds exception when pasting an empty selection 2023-07-09 04:03:59 +00:00
crschnick
2af3e33097 Update documentation links 2023-07-09 03:52:16 +00:00
crschnick
87051e1c98 Clarify error message 2023-07-09 03:49:30 +00:00
crschnick
f3a905a7fe Fix tabby terminal not launching on macOS 2023-07-08 10:52:34 +00:00
crschnick
db4c0775b3 Update README.md 2023-07-08 02:48:23 +00:00
crschnick
e85150d1cc Catch all jna exceptions in registry helper 2023-07-08 02:47:25 +00:00
crschnick
3448cf95f7 Improve application error message on macOS 2023-07-08 02:47:05 +00:00
crschnick
e4a157fd82 Update README.md 2023-07-07 07:07:34 +00:00
crschnick
f464c2c1ac Small fixes [release] 2023-07-06 06:38:48 +00:00
crschnick
447ec12023 Various fixes [stage] 2023-07-05 04:17:29 +00:00
crschnick
a097ae7a41 Rework lxd connections 2023-07-04 05:42:00 +00:00
crschnick
75e85f3764 [stage] 2023-07-04 01:29:27 +00:00
crschnick
313cc922ee Various small improvements 2023-07-04 01:28:33 +00:00
crschnick
d84789b995 [stage] 2023-07-02 15:35:33 +00:00
crschnick
8ca763f185 More fixes 2023-07-02 15:31:46 +00:00
crschnick
0b6aee858c Fix cut off text in welcome page 2023-06-24 10:46:22 +00:00
crschnick
8b2e21211b Update script usage 2023-06-24 10:12:19 +00:00
crschnick
882b3c12a0 Fix for spaces in ps1 script 2023-06-24 09:09:08 +00:00
crschnick
7ad4dffabb Add script to readme 2023-06-24 08:46:08 +00:00
crschnick
bfde1a8e70 ps1 install script fixes 2023-06-24 08:41:52 +00:00
crschnick
b643472932 Add ps1 download script 2023-06-24 07:09:45 +00:00
crschnick
a1c544a4d2 Update logo 2023-06-23 19:52:51 +00:00
crschnick
e765558b14 Update readme 2023-06-23 19:47:09 +00:00
crschnick
c6efb9eec8 Fix NPE [release] 2023-06-22 19:18:47 +00:00
crschnick
ac7140f8ae [release] 2023-06-22 19:11:06 +00:00
crschnick
569e262c97 Fix NPEs for failed stores 2023-06-22 19:08:11 +00:00
crschnick
a2364d6f47 Small macOS fixes 2023-06-22 17:49:39 +00:00
crschnick
8261f6f7ec Split about page in settings 2023-06-22 17:47:31 +00:00
crschnick
29de7cddd2 Improve font smoothing 2023-06-22 14:55:04 +00:00
crschnick
af207615e6 Add support for explicit login shell open command [stage] 2023-06-21 20:16:00 +00:00
crschnick
e96e1f0b41 More logo fixes [stage] 2023-06-21 14:36:47 +00:00
crschnick
ee102dd001 macOS icon fixes 2023-06-21 13:32:02 +00:00
crschnick
1354e00dbd Update logo 2023-06-21 13:14:19 +00:00
crschnick
319a1d2750 Wording fixes 2023-06-20 16:43:00 +00:00
crschnick
6b72d28050 [stage] 2023-06-20 13:10:22 +00:00
crschnick
4b2c98afb2 Update download script information 2023-06-20 13:04:39 +00:00
crschnick
827aa96d9d Linux font fixes 2023-06-20 12:49:13 +00:00
crschnick
67eee13fca About page polish [stage] 2023-06-20 10:24:35 +00:00
crschnick
1aa50d50ca Add more terminals plus various small improvements [stage] 2023-06-19 16:05:26 +00:00
crschnick
4689bcc2aa Various small UI improvements 2023-06-19 09:03:34 +00:00
crschnick
7605a4331a More support for symlinks 2023-06-18 17:49:28 +00:00
crschnick
f8b2afe44c Improve browser state data structure 2023-06-16 03:55:51 +00:00
crschnick
0274ade547 Refactor and cleanup 2023-06-16 03:38:39 +00:00
crschnick
6af56c451a Make browser restore functionality work 2023-06-16 01:24:07 +00:00
crschnick
26c3c16872 Rework browser intro and about pages 2023-06-15 11:54:14 +00:00
crschnick
c2ff618698 Improve browser styling 2023-06-14 08:54:00 +00:00
crschnick
0f6616195a Recognize csh, not yet supported though 2023-06-14 04:27:55 +00:00
crschnick
67654f3005 Preview executed commands in connection creation window 2023-06-13 16:40:52 +00:00
crschnick
6d4e800333 Clarify tty to ansi escapes 2023-06-13 01:03:47 +00:00
crschnick
d8089f1b9d Improve error messages for missing external application 2023-06-13 01:03:24 +00:00
crschnick
06a0fefa8c Lock temporary directory to prevent concurrency issues 2023-06-12 01:09:04 +00:00
crschnick
e085445344 Add showcase property and rework property loading 2023-06-12 01:08:43 +00:00
crschnick
2221430c78 Fix shell detection [release] 2023-06-10 16:07:23 +00:00
crschnick
c96284a445 [release] 2023-06-09 15:56:49 +00:00
crschnick
73e351d5ef Fix browser race condition 2023-06-09 15:53:50 +00:00
crschnick
20b96d7f25 Update README.md 2023-06-08 18:34:37 +00:00
crschnick
f9df5c1f7e [release] 2023-06-08 16:56:16 +00:00
crschnick
5dd9a3c984 Show name of system in path check 2023-06-08 16:23:46 +00:00
crschnick
ed60342f65 Update README.md 2023-06-08 15:55:33 +00:00
crschnick
62d38a55a2 Center image 2023-06-08 15:31:54 +00:00
crschnick
36994d7abf Update readme 2023-06-08 15:08:15 +00:00
crschnick
fa96073ada Small bug fixes [stage] 2023-06-08 13:53:04 +00:00
crschnick
01ebb66500 Update README.md 2023-06-08 00:00:52 +00:00
crschnick
c82449b138 Many small fixes [stage] 2023-06-07 15:02:19 +00:00
crschnick
5573cc92b0 Fix update terminal not working 2023-06-07 15:01:19 +00:00
crschnick
70089f2dde [stage] 2023-06-06 23:52:22 +00:00
crschnick
d36c2b00ae Properly clean temp directory 2023-06-06 23:42:52 +00:00
crschnick
e72fc2c487 Clear temp directory 2023-06-06 22:57:13 +00:00
crschnick
8406d4c03b [stage] 2023-06-06 19:43:32 +00:00
crschnick
8517b87e2e Add opacity comment 2023-06-06 18:02:54 +00:00
crschnick
772faef0fe Small fixes 2023-06-06 18:02:42 +00:00
crschnick
a056cf95a2 Fix password field styling 2023-06-06 18:02:21 +00:00
crschnick
b997e7674d Add more checks when adding store 2023-06-06 18:01:39 +00:00
crschnick
84e054978c Browser list performance improvements 2023-06-06 13:54:08 +00:00
crschnick
f58c304d6f Various small fixes 2023-06-06 13:40:10 +00:00
crschnick
b86671d971 Small fixes [stage] 2023-06-05 12:46:57 +00:00
crschnick
271e2c8013 Refactor commands and error handling 2023-06-05 11:12:44 +00:00
crschnick
e92ded1c71 Various small improvements 2023-06-04 15:04:14 +00:00
crschnick
ae65f12d81 Update readme 2023-06-03 23:17:27 +00:00
crschnick
ae6a3ecb36 Fix alpine linux dir listings being empty 2023-06-03 18:17:17 +00:00
crschnick
cc9941e276 Accessibility fixes 2023-06-03 17:14:43 +00:00
crschnick
2ffea800b5 macos fixes 2023-06-03 16:22:13 +00:00
crschnick
fcc47b9038 Accessibility improvements 2023-06-03 13:51:20 +00:00
crschnick
544597c267 Update readmes 2023-06-02 17:44:55 +00:00
crschnick
d75aade762 Add download links to readme 2023-06-02 16:44:41 +00:00
crschnick
ed809801f4 Improve scan dialog responsiveness 2023-06-02 16:35:05 +00:00
crschnick
1d0a77a2ff Add tabby support 2023-06-02 16:33:47 +00:00
crschnick
b889cd3fab Improve startup time and fix some small issues 2023-06-02 08:43:29 +00:00
crschnick
f4c440fef1 Update readmes 2023-06-02 08:42:34 +00:00
crschnick
458d6dc2e9 [release] 2023-06-01 10:54:53 +00:00
crschnick
eca94d24f2 Small fixes 2023-06-01 10:50:40 +00:00
crschnick
7d09b88a70 Packaging fixes [stage] 2023-06-01 09:16:18 +00:00
crschnick
0da5f283ed Fix navbar style 2023-06-01 06:47:48 +00:00
crschnick
76b565eb0a Improve debian distribution 2023-06-01 05:42:38 +00:00
crschnick
82030109dc Small fixes [stage] 2023-06-01 03:25:30 +00:00
crschnick
347ce98bef Update dependencies and fix a few issues 2023-06-01 01:23:45 +00:00
crschnick
4a21dffdab Introduce file kinds 2023-05-30 10:08:58 +00:00
crschnick
daa011ffe6 Various fixes [stage] 2023-05-30 01:59:08 +00:00
crschnick
52cdcfa0aa File browser overview page improvements 2023-05-29 00:33:34 +00:00
crschnick
40a5c6a306 Improve styling of navigation bar 2023-05-27 22:29:25 +00:00
crschnick
ff99506d3a Small fixes 2023-05-27 17:34:29 +00:00
crschnick
e7b5d7e9ee Various small improvements 2023-05-27 13:35:47 +00:00
crschnick
59d0706129 Small fixes 2023-05-26 21:01:57 +00:00
crschnick
8d9b4c9d4e Browser improvements 2023-05-25 22:29:24 +00:00
crschnick
ff2956ef29 Small fix [release] [noannounce] 2023-05-24 08:22:01 +00:00
crschnick
f36481ba26 [release] 2023-05-24 05:44:50 +00:00
crschnick
52e930c7a3 Fix GUI validation sometimes not working 2023-05-24 05:19:12 +00:00
crschnick
cad8e5e380 Add elevation check functionality 2023-05-24 05:16:43 +00:00
crschnick
6d586b3045 Many small fixes 2023-05-24 01:53:23 +00:00
crschnick
40f3945e92 Homebrew fixes 2023-05-23 04:27:06 +00:00
crschnick
b171dd099c More temporary directory fixes 2023-05-22 21:23:05 +00:00
crschnick
442f8f7324 Small fixes 2023-05-22 20:24:47 +00:00
crschnick
cdb0d02ad5 Small macOS init file fixes 2023-05-22 16:24:20 +00:00
crschnick
fbad0be1e2 iTerm fixes 2023-05-22 14:05:54 +00:00
crschnick
55dc32bacc Attempt to fix zsh issues 2023-05-22 13:33:32 +00:00
crschnick
cc1fb789c6 Final fixes [release] 2023-05-21 14:12:06 +00:00
crschnick
f48b1fa212 Change context menu behavior 2023-05-20 18:13:21 +00:00
crschnick
48d155400c Small bug fixes 2023-05-20 17:35:59 +00:00
crschnick
a2e04a24e6 Improve download script 2023-05-20 15:26:28 +00:00
crschnick
623d6be4ad Rename project [stage] 2023-05-20 14:23:36 +00:00
crschnick
19f4b0abc4 Add changelog 2023-05-20 13:49:58 +00:00
crschnick
8038e88b28 [release] 2023-05-11 11:48:16 +00:00
crschnick
2454575049 Update konsole note 2023-05-11 11:10:41 +00:00
crschnick
3ba2664458 Fix konsole new tab option 2023-05-11 10:54:00 +00:00
crschnick
f064efaa29 Partially revert no init script changes for terminals 2023-05-11 10:26:55 +00:00
crschnick
4bf42831f3 Exclusively use rc or profile file 2023-05-11 10:14:51 +00:00
crschnick
d3485e9f6d Attempt to prevent double init file execution when terminal opens 2023-05-11 09:53:54 +00:00
crschnick
2c9d26f65f Improve download script 2023-05-10 00:13:49 +00:00
crschnick
90bc07981f Make download script use arm64 when needed 2023-05-09 23:26:47 +00:00
crschnick
d0ca3e8a27 File browser refinements 2023-05-08 23:12:53 +00:00
crschnick
5845abb481 [release] 2023-05-08 14:13:52 +00:00
crschnick
6fb3cc3978 [stage] 2023-05-08 13:59:29 +00:00
crschnick
7b36c1a96b Add custom title to opened terminal windows 2023-05-07 23:58:19 +00:00
crschnick
0845c7e27d Refine Kubernetes support 2023-05-07 22:47:56 +00:00
crschnick
09f7ebbf45 Update changelog [stage] 2023-05-06 21:31:35 +00:00
crschnick
2a828721db File browser improvements 2023-05-06 12:28:18 +00:00
crschnick
240d6698d6 Prepare for multi arch mac builds 2023-05-04 19:02:31 +00:00
crschnick
e9af3d5bc7 Improve detection for initial launch 2023-05-04 13:37:13 +00:00
crschnick
4152d6e1db Improve connection entry change listener 2023-05-03 17:13:31 +00:00
crschnick
cd771d4039 Small fix [release] [noannounce] 2023-05-03 17:05:11 +00:00
crschnick
09178d171b [release] 2023-05-03 16:13:32 +00:00
crschnick
c1886cc9cd Fix file browser connection list not updating 2023-05-03 16:08:01 +00:00
crschnick
758f721690 Rework scans 2023-05-03 12:24:03 +00:00
crschnick
114332ff2f Add arch information 2023-05-03 10:20:54 +00:00
crschnick
8f906d4a7d Work for passing secret environment variables 2023-05-02 21:00:28 +00:00
crschnick
78a51f5f52 Renames 2023-05-02 18:55:35 +00:00
crschnick
e0678aaf74 Switch to Apache license 2023-05-02 13:30:40 +00:00
crschnick
bc74938a60 Small file browser fixes [release] [noannounce] 2023-05-01 14:42:02 +00:00
crschnick
db6ba6439e Svg cache fixes [release] 2023-05-01 10:40:16 +00:00
crschnick
9f4359a4eb Homebrew fixes 2023-05-01 09:21:14 +00:00
crschnick
e1fa5a9c52 Initial work for homebrew 2023-05-01 07:16:09 +00:00
crschnick
9b2d4b7376 Update readmes 2023-05-01 07:06:35 +00:00
crschnick
cb0324f81a Small fixes 2023-05-01 07:06:12 +00:00
crschnick
45de844680 Homebrew fixes 2023-04-29 19:53:52 +00:00
crschnick
ae42b07759 More file loading fixes [stage] 2023-04-29 18:51:39 +00:00
crschnick
f2d11c548a Translations fixes 2023-04-28 19:05:05 +00:00
crschnick
1596483250 More user interface improvements 2023-04-28 18:30:31 +00:00
crschnick
62a6438e97 More file browser fixes 2023-04-27 22:50:51 +00:00
crschnick
39828f4d15 Move antivirus section 2023-04-27 22:32:50 +00:00
crschnick
c5901fae7d Update security page 2023-04-27 20:12:24 +00:00
crschnick
405c024e9c Improve file browser performance by caching icons 2023-04-27 19:37:54 +00:00
crschnick
dbd0cb2d68 Show file attributes in file explorer 2023-04-26 23:01:27 +00:00
crschnick
a458f6ffe5 Better separate staging environment [stage] 2023-04-26 14:34:59 +00:00
crschnick
1e10c595e4 Improve shell connection validation 2023-04-26 14:19:50 +00:00
crschnick
c511a72187 Rework shell detection and initialization
This should fix some issues with Powershell Core and ttys
2023-04-26 13:45:32 +00:00
crschnick
7c095cea4c Remove -f from chmod 2023-04-25 19:51:38 +00:00
crschnick
d07d59ad7d Report underlying exception when terminal open fails 2023-04-25 19:36:25 +00:00
crschnick
7f700ee326 Fix askpass NPE in powershell environments 2023-04-25 19:35:37 +00:00
crschnick
d5995a1f94 Bump version 2023-04-25 14:56:23 +00:00
crschnick
75f67dd975 Make privacy policy more clear and accurate 2023-04-25 14:48:19 +00:00
crschnick
b60c3d1bba Fix NPE [release] [noannounce] 2023-04-25 10:58:03 +00:00
crschnick
2b2614cc9b [release] 2023-04-25 10:20:36 +00:00
crschnick
bc52cafaa1 Check if update is already prepared 2023-04-25 09:58:53 +00:00
crschnick
339e9317e2 Persist expandable property 2023-04-24 17:29:52 +00:00
crschnick
2c8a08b690 Fix for pss io 2023-04-24 15:11:40 +00:00
crschnick
4e124466f8 The add ability to expand and collapse connections 2023-04-23 14:30:45 +00:00
crschnick
6d415c6fe3 [stage] 2023-04-23 11:10:08 +00:00
crschnick
56dcf905ef Various fixes 2023-04-23 11:04:39 +00:00
crschnick
b211e10184 Add better error messages for integrations 2023-04-23 08:52:42 +00:00
crschnick
6ca4b056e7 Fix macOS program detection 2023-04-22 07:44:32 +00:00
crschnick
bb519fcd1b Update SECURITY.md 2023-04-21 14:16:52 +00:00
crschnick
53d541d1c7 Add privacy policy link 2023-04-21 13:04:19 +00:00
crschnick
a47eea8d3e Fix NPE [release] 2023-04-21 12:15:23 +00:00
crschnick
5ac27e6ff5 Update SECURITY.md 2023-04-21 09:44:11 +00:00
crschnick
c30a6eb341 Update SECURITY.md 2023-04-21 09:43:49 +00:00
crschnick
7f635590f6 Rework security page 2023-04-21 09:41:33 +00:00
crschnick
5b7f142175 [release] 2023-04-21 07:13:27 +00:00
crschnick
b6d2b8cb5e Make error display text selectable 2023-04-20 14:28:50 +00:00
crschnick
8da34c670a Add support for message formatting in process control 2023-04-20 14:17:39 +00:00
crschnick
fe6d56d71e Small fixes [stage] 2023-04-20 12:18:59 +00:00
crschnick
1d998c7863 [stage] 2023-04-20 08:16:43 +00:00
crschnick
a35c6f2d83 Automatically fully refresh connection when initially added 2023-04-20 08:14:56 +00:00
crschnick
0f306a5d50 Fix posix askpass 2023-04-20 07:06:13 +00:00
crschnick
73131a3f0c Fixes [stage] 2023-04-20 05:10:42 +00:00
crschnick
097a463e0e Prefs fix 2023-04-19 06:07:12 +00:00
crschnick
d464118d83 File browser performance improvements 2023-04-19 05:40:35 +00:00
crschnick
3029bd54f6 Windows 7 fixes 2023-04-19 03:15:23 +00:00
crschnick
ccb8072625 Small fixes 2023-04-19 01:44:33 +00:00
crschnick
aaf4ab9560 Check choco version 2023-04-18 02:52:10 +00:00
crschnick
61685004e9 Small wording changes 2023-04-17 11:15:43 +00:00
crschnick
780898451b Improves some settings descriptions 2023-04-16 23:14:29 +00:00
crschnick
9233089e37 Fix other image 2023-04-16 23:14:20 +00:00
crschnick
9f5ffd8d53 Fix image 2023-04-16 23:10:59 +00:00
crschnick
902b1534a8 Update read me 2023-04-16 23:06:59 +00:00
crschnick
fc68b96f72 Add flatpak packager 2023-04-15 00:10:50 +00:00
crschnick
58da0572a1 [stage] 2023-04-14 23:16:27 +00:00
crschnick
c3793ed0c0 Error handler improvements 2023-04-14 08:53:55 +00:00
crschnick
8abc82971a Askpass fixes 2023-04-13 20:34:30 +00:00
crschnick
f777604573 Add support for powershell core terminal 2023-04-13 16:28:13 +00:00
crschnick
1e30ca595c More staging fixes [stage] 2023-04-13 03:23:07 +00:00
crschnick
202ebdcf1d Attempt to stage [stage] 2023-04-12 22:15:56 +00:00
crschnick
6e00f55fd8 Implements staging [stage] 2023-04-12 20:47:31 +00:00
crschnick
c86cc138c0 Small fixes 2023-04-12 20:11:40 +00:00
crschnick
edd4656e3a Merge branch 'pssessions' 2023-04-12 17:53:58 +00:00
crschnick
d0117fb78e Final updater fixes 2023-04-11 21:20:09 +00:00
crschnick
86ec53ea56 Update README.md 2023-04-10 15:47:17 +00:00
crschnick
0f7effef06 Merge branch 'gradle8' 2023-04-10 15:22:07 +00:00
crschnick
6440062efd Prefer iTerm over Warp for now 2023-04-10 14:36:40 +00:00
crschnick
98e1daf893 Fix platform thread issue 2023-04-09 17:14:07 +00:00
crschnick
8e5cab4ec2 add dependabot 2023-04-09 13:18:11 +00:00
crschnick
ecef6c15af Add security policy 2023-04-09 12:18:33 +00:00
crschnick
d9a18daed5 Improve drag and drop styling in file browser 2023-04-08 10:19:54 +00:00
crschnick
005bba4542 Adapt extension loading messages to development environment 2023-04-08 07:14:02 +00:00
crschnick
157f16ff9e Improve extension loading mechanism 2023-04-07 21:29:27 +00:00
crschnick
849b23cede Fix NPE 2023-04-07 19:16:34 +00:00
crschnick
5b68bc0448 [release] 2023-04-07 18:22:43 +00:00
crschnick
4a3cd7377a Make error handlers consistent 2023-04-07 18:21:31 +00:00
crschnick
ff27f54535 Separate shell init commands not into lines anymore
This should improve any script requirement issues.
2023-04-07 17:18:01 +00:00
crschnick
ffe2326e7f [release] 2023-04-07 02:18:09 +00:00
crschnick
e91b6da9dd Fix powershell temp dir stack overflow 2023-04-07 02:16:47 +00:00
crschnick
d482c23cf6 [release] 2023-04-06 22:12:01 +00:00
crschnick
848e086daa Timeout adjustment 2023-04-06 19:33:00 +00:00
crschnick
9a284b9926 Improve unsupported feature message 2023-04-06 18:09:00 +00:00
crschnick
fe396e1b11 Improve os detection and file existance checks 2023-04-06 15:46:22 +00:00
crschnick
4726ad975c Use system font by default 2023-04-06 15:45:14 +00:00
crschnick
08032c96e5 Small fix [release] 2023-04-05 23:47:58 +00:00
crschnick
9d7fcab111 Another power shell open fix [release] 2023-04-05 23:12:52 +00:00
crschnick
aee815cb4b [release] 2023-04-05 21:52:51 +00:00
crschnick
b132ed9555 Fix terminal open when script file path contains bases 2023-04-05 21:46:10 +00:00
crschnick
bd9cc5cfcb Fix drag and drop sometimes having the wrong target 2023-04-05 20:55:29 +00:00
crschnick
7eb840e5a8 [release] 2023-04-05 14:41:53 +00:00
crschnick
3b313855b8 [release] 2023-04-05 14:41:02 +00:00
crschnick
e37f6c9658 Improve dev update check 2023-04-05 13:58:43 +00:00
crschnick
aa6d646c4e Update readme 2023-04-05 13:58:16 +00:00
crschnick
0c8a146096 Small updater fixes [release] 2023-04-04 19:39:10 +00:00
crschnick
d836bfcce6 Updater fixes [release] 2023-04-04 18:58:28 +00:00
crschnick
819ad3e839 [release] 2023-04-04 17:17:02 +00:00
crschnick
35dad1156e Fix file browser shift selection 2023-04-04 17:11:09 +00:00
crschnick
0cf07edc2d Remove and disable unused modules 2023-04-04 16:11:31 +00:00
crschnick
8117b228ee Fix last used time not updating 2023-04-04 15:49:44 +00:00
crschnick
acebff1445 Fix possible NPE 2023-04-04 08:42:48 +00:00
crschnick
92f319d20c Fix possible out of bounds 2023-04-04 08:19:29 +00:00
crschnick
a3d211beeb Include install script in repository 2023-04-04 08:17:31 +00:00
crschnick
33dcf90f75 Update readme 2023-04-03 17:39:01 +00:00
crschnick
bfa913fd3d Remove documentation link for now 2023-04-03 17:20:15 +00:00
crschnick
b5268a4a76 Log more os information in error report 2023-04-03 17:19:01 +00:00
crschnick
289d4d89cc Properly escape docker ls command 2023-04-03 15:42:16 +00:00
crschnick
75145293c7 Update README.md 2023-04-03 15:41:31 +00:00
crschnick
e3b6ed2de3 File browser visual improvements and bug fixes 2023-04-03 14:32:33 +00:00
crschnick
5b0fca5a14 Show changelog before installing update 2023-04-03 03:37:49 +00:00
crschnick
ea24d3e46e Small file browser fixes [release] 2023-04-02 23:58:16 +00:00
crschnick
94a6b8d5cc Add file browser icons 2023-04-01 21:10:17 +00:00
crschnick
a516541c8d [release] 2023-04-01 12:59:01 +00:00
crschnick
e4a85573c0 Properly fix os type detection for windows on different locales 2023-04-01 12:45:11 +00:00
crschnick
b1f2272fc2 Rework updater to fix some bugs [release] 2023-04-01 12:04:01 +00:00
crschnick
5444bc785f Fix desktop shortcuts 2023-03-31 20:16:01 +00:00
crschnick
0d3274b1a4 [release] 2023-03-31 15:23:58 +00:00
crschnick
33836f8690 Update readme 2023-03-31 13:43:13 +00:00
crschnick
60c64d8d55 [release] 2023-03-31 13:40:10 +00:00
crschnick
e2308366fb Allow for the creation of connection desktop shortcuts for all distributions 2023-03-31 13:32:12 +00:00
crschnick
2763ca40c8 Implement file browser download window 2023-03-30 18:56:18 +00:00
crschnick
7fd2e89c77 Small fixes [release] 2023-03-30 10:06:25 +00:00
crschnick
56234f2abf Add warning when no known terminal is recognized [release] 2023-03-29 18:14:04 +00:00
crschnick
90e18d0627 Fix portable version not launching [release] 2023-03-29 17:53:28 +00:00
crschnick
53068eb943 Small bug fixes [release] 2023-03-28 16:58:23 +00:00
Christopher Schnick
1ff572da03
Update README.md 2023-03-28 13:56:11 +02:00
Christopher Schnick
eeb0a1eefe
Update README.md 2023-03-28 13:43:55 +02:00
crschnick
1eabf90a62 Some UI reorganization [release] 2023-03-28 10:21:12 +00:00
crschnick
3ebe0d4639 More shell fixes 2023-03-27 18:59:17 +00:00
crschnick
97079fb58d Shell encoding fixes [release] 2023-03-27 10:49:25 +00:00
crschnick
f777352dbe Bump version [release] 2023-03-25 10:53:09 +00:00
crschnick
1d2b1539a8 Encoding fixes on windows [release] 2023-03-25 10:51:08 +00:00
crschnick
890d110b45 Error handler fixes 2023-03-24 17:27:43 +00:00
crschnick
7284081206 [release] 2023-03-23 13:16:15 +00:00
crschnick
eba727619f Various bug fixes 2023-03-23 12:38:31 +00:00
crschnick
71e9539efd Small fixes and improvements [release] 2023-03-22 11:42:47 +00:00
crschnick
b881e548fc Small fixes [release] 2023-03-21 14:35:40 +00:00
crschnick
1f9d5ab2e6 Small browser fixes [release] 2023-03-20 13:49:28 +00:00
crschnick
4bb7627488 More shell rework 2023-03-19 19:38:29 +00:00
crschnick
11667d7876 More shell improvements 2023-03-18 06:43:36 +00:00
crschnick
ae2b7289cc Shell handling improvements 2023-03-17 04:52:57 +00:00
crschnick
1230a22969 Bump version [release] 2023-03-16 01:43:49 +00:00
crschnick
6e39480537 Various small improvements [release] 2023-03-15 23:51:25 +00:00
crschnick
ba56fc25ab macOS file fixes 2023-03-15 19:50:49 +00:00
crschnick
d5e024c43e More file browser improvements [release] 2023-03-15 17:33:07 +00:00
crschnick
355de8e2fc File browser improvements 2023-03-14 22:02:40 +00:00
crschnick
9e505c68ed Fix gnome-terminal bug 2023-03-13 21:44:38 +00:00
crschnick
516af8c66a Small improvements [release] 2023-03-13 19:47:24 +00:00
crschnick
849e8f0ef4 Improve hierarchical store display [release] 2023-03-13 15:07:08 +00:00
crschnick
add30cb413 Bug fixes 2023-03-13 13:03:38 +00:00
crschnick
9e3637a97b Bug fixes [release] 2023-03-12 12:49:10 +00:00
crschnick
eecafd3cf9 Fixes for sh connections [release] 2023-03-11 10:01:08 +00:00
crschnick
a752b0c79a macOS fixes 2023-03-11 01:46:53 +00:00
crschnick
f2bf943d56 Shell fixes 2023-03-11 01:33:02 +00:00
crschnick
294c9459a2 macOS fixes 2023-03-10 22:57:06 +00:00
crschnick
ce296fe287 macOS fixes 2023-03-10 09:23:48 +00:00
crschnick
9fbbf7d958 New release [release] 2023-03-09 23:49:47 +00:00
crschnick
1ea4018379 Various small fixes 2023-03-09 20:45:13 +00:00
crschnick
782be482d0 New release [release] 2023-03-08 23:17:55 +00:00
crschnick
90ce2de529 More elevation fixes 2023-03-08 23:00:25 +00:00
crschnick
872406fc4c Local askpass fixes 2023-03-07 22:36:02 +00:00
crschnick
56048bd5e3 More updater fixes 2023-03-07 17:34:23 +00:00
crschnick
906676cf58 Update script fixes 2023-03-07 15:49:38 +00:00
crschnick
beb4ccd01f Update fixes 2023-03-07 14:17:54 +00:00
crschnick
3ec708ef37 Various small fixes [release] 2023-03-06 19:42:26 +00:00
crschnick
b7fa352ae5 Many small fixes [release] 2023-03-05 11:39:38 +00:00
crschnick
c936e37509 Various small fixes [release] 2023-03-04 10:45:52 +00:00
crschnick
1b23c833ed Interface improvements and fixes [release] 2023-03-03 14:33:41 +00:00
crschnick
d879c13aa4 Improvements and refactor for shell connections 2023-03-02 16:57:49 +00:00
crschnick
d5a7e2fb64 Update readme 2023-02-28 14:22:49 +00:00
crschnick
1bde4c8909 Update images 2023-02-27 05:38:36 +00:00
crschnick
c19533c92b Update readme 2023-02-27 05:35:16 +00:00
crschnick
417aa16f1d Various small fixes [release] 2023-02-26 20:26:45 +00:00
crschnick
26a7592ed6 More browser improvements 2023-02-25 02:46:40 +00:00
crschnick
e0b34ff480 More browser improvements 2023-02-24 22:16:27 +00:00
crschnick
01a3177843 Various file system fixes 2023-02-23 17:37:24 +00:00
crschnick
491d3c201c New release [release] 2023-02-22 15:14:46 +00:00
crschnick
6629f218d7 Small fixes for Linux 2023-02-22 14:50:24 +00:00
crschnick
689962b1fb Fix typo 2023-02-22 14:17:30 +00:00
crschnick
5d5c3ea140 Fix platform thread issue 2023-02-22 14:17:04 +00:00
crschnick
305f8d69e8 More browser fixes 2023-02-22 14:14:01 +00:00
crschnick
7ca4d64e2d Browser fixes 2023-02-22 12:03:41 +00:00
crschnick
fab1d75f45 Many small fixes 2023-02-21 23:41:39 +00:00
crschnick
27d25ca666 New release [release] 2023-02-21 11:13:10 +00:00
crschnick
14fa662db5 Small startup fixes 2023-02-21 10:57:57 +00:00
crschnick
84a129191d Small fixes 2023-02-21 09:58:39 +00:00
crschnick
0a23e950d9 Small fixes 2023-02-20 18:39:15 +00:00
crschnick
3b3f238b68 Small fixes 2023-02-20 17:18:11 +00:00
crschnick
aa95c746b9 Cleanup [release] 2023-02-20 12:00:06 +00:00
crschnick
e7e3b50632 New release [release] 2023-02-20 11:33:18 +00:00
crschnick
bd6b04dca0 Small fixes 2023-02-20 10:36:00 +00:00
crschnick
8bd3288051 Renaming fixes 2023-02-20 10:02:17 +00:00
crschnick
f304b66055 Shell refactoring 2023-02-20 09:49:10 +00:00
crschnick
211b516b7b Merge branch 'browser' 2023-02-19 17:44:47 +00:00
crschnick
b3a5b427fe Move some files 2023-02-14 18:37:48 +00:00
crschnick
767941d096 Move to atlantafx 2023-02-14 18:37:13 +00:00
crschnick
287f3d084d Improve settings menu and connection creation [release] 2023-02-13 23:04:20 +00:00
crschnick
28a8c95c71 [release] 2023-02-12 22:44:37 +00:00
crschnick
49de9b8a97 Set proper terminal default value 2023-02-12 22:44:24 +00:00
crschnick
4b30bed0a8 More macOS integration fixes 2023-02-12 21:57:50 +00:00
crschnick
7d2c328fa0 Improve macos permission prompt 2023-02-12 21:09:53 +00:00
crschnick
642d5c44ec MacOS permission fixes 2023-02-12 20:15:34 +00:00
crschnick
a4b3729896 Preparations for macos permission system checks 2023-02-12 19:45:08 +00:00
crschnick
44f7b4922d Fix many small issues 2023-02-12 16:48:07 +00:00
crschnick
5539eaa420 Fix some links 2023-02-11 16:11:59 +00:00
crschnick
cd7f8d821c Update building instructions 2023-02-11 15:55:23 +00:00
Christopher Schnick
3f2d1abd3d Fix demo 2023-02-11 15:58:09 +01:00
crschnick
3f6831ef87 Try to fix demo video 2023-02-11 14:51:39 +00:00
crschnick
49f6dd432e Add demo 2023-02-11 14:48:29 +00:00
crschnick
c553dca81b Implement launch functionality [release] 2023-02-11 14:22:07 +00:00
crschnick
197f5220af Various small bug fixes 2023-02-10 21:52:20 +00:00
crschnick
14d39c23ab Add community links 2023-02-10 19:39:23 +00:00
crschnick
8a9452ab46 Exclude cli for now 2023-02-10 18:35:32 +00:00
crschnick
4bb1ab768b Update image 2023-02-10 17:42:24 +00:00
crschnick
28593307be Update read me and small fixes 2023-02-10 17:40:03 +00:00
crschnick
6f7207645f Attempt to fix build error [release] 2023-02-10 15:09:48 +00:00
crschnick
c91fe5b5c5 Small fixes [release] 2023-02-10 14:36:15 +00:00
crschnick
2d931ce1a4 Include moditect plugin with default modules 2023-02-10 14:20:49 +00:00
crschnick
84b43ff319 Properly escape variablesb 2023-02-10 13:58:55 +00:00
crschnick
504ceea720 Fix string escapes 2023-02-10 13:55:16 +00:00
crschnick
fd7ee5ab17 Fix default modules 2023-02-10 13:52:42 +00:00
crschnick
f2772fb2a4 Build fixes 2023-02-10 13:46:54 +00:00
crschnick
131dec75ec Reformat 2023-02-09 21:30:07 +00:00
crschnick
7be8087b19 Migrate all actions, fix various bugs 2023-02-09 21:06:58 +00:00
crschnick
51bbf8ad67 Restructure proc module 2023-02-08 21:34:19 +00:00
crschnick
d5c99ba49f More cleanup 2023-02-08 15:09:20 +00:00
crschnick
d58abf83b9 Cleanup 2023-02-08 14:34:32 +00:00
crschnick
ad92311a7d Various small fixes 2023-02-07 21:54:59 +00:00
crschnick
37760e06fd Add missing modules and bump version 2023-02-06 15:49:36 +00:00
crschnick
becda111b0 Small fixes 2023-02-06 15:31:41 +00:00
crschnick
4e1c38f27d Small MacOS integration fixes 2023-02-06 13:26:37 +00:00
crschnick
ef3b7c448f Small fixes 2023-02-06 12:52:28 +00:00
crschnick
137c6ff9f8 MacOS fixes 2023-02-05 19:57:14 +00:00
crschnick
45b4b84bf3 Improve macos shortcuts 2023-02-05 19:40:47 +00:00
crschnick
22783c949d Add macOS scheme handler 2023-02-05 18:14:25 +00:00
crschnick
4222703946 Shortcut fixes 2023-02-05 17:21:36 +00:00
crschnick
6d07f5bab5 Implement more robust action system and desktop shortcuts 2023-02-05 15:04:18 +00:00
crschnick
3eb165cce7 Fix private file handling 2023-02-04 10:34:39 +00:00
crschnick
6b924e7021 Readd private files 2023-02-04 09:58:02 +00:00
crschnick
241362dce0 Small bug fixes [release] 2023-02-04 09:53:05 +00:00
crschnick
84931ed6ad Fix small typos for mac integrations 2023-02-04 09:25:57 +00:00
crschnick
0ee12c8e5a More mac fixes 2023-02-04 09:17:02 +00:00
crschnick
fd05f9bb0f More fixes for mac 2023-02-04 08:40:15 +00:00
crschnick
ded82617e6 More mac integration fixes 2023-02-04 08:14:37 +00:00
crschnick
480b8d37ff Polish various features 2023-02-03 18:07:02 +00:00
crschnick
b16a547996 Rework external application handling 2023-02-03 13:56:11 +00:00
crschnick
b0b6199dd3 Fixes for mac integration 2023-02-03 13:05:12 +00:00
crschnick
e474b1b423 Fix Linux editor detection 2023-02-03 11:10:08 +00:00
crschnick
0bc01de98b Rework default external application detection 2023-02-03 10:20:18 +00:00
crschnick
c3cd8f0088 More small additions [release] 2023-02-02 14:17:40 +00:00
crschnick
bf176393ad Small fixes 2023-02-02 13:58:50 +00:00
crschnick
12bb4f744d Prepare for repo move 2023-02-02 13:46:40 +00:00
crschnick
90192b274c Update description 2023-02-02 12:54:50 +00:00
crschnick
5a7864aa8e Update README.md 2023-02-02 12:47:23 +00:00
crschnick
38640e3dcd Implement support for external editors in text areas 2023-02-02 11:58:53 +00:00
crschnick
c1cafcfced Small fixes [release] 2023-02-01 18:18:42 +00:00
crschnick
76c78d9d78 Small additional fixes [release] 2023-02-01 11:05:05 +00:00
crschnick
de14d0a37b New release [release] 2023-02-01 10:33:00 +00:00
crschnick
464e04a6bb Various shell connection optimizations 2023-02-01 10:15:51 +00:00
crschnick
d3046933c1 Debug env 2023-02-01 10:10:26 +00:00
crschnick
51dda0606f Try to improve release 2023-02-01 10:05:26 +00:00
crschnick
219a1a2c9f Try to fix push 2023-02-01 09:16:26 +00:00
crschnick
4b6d0d5f28 New release 2023-01-29 09:49:58 +00:00
crschnick
06405de396 Merge main repository 2023-01-27 02:34:46 +00:00
Christopher Schnick
20a334912a Many small fixes 2023-01-20 00:36:42 +01:00
Christopher Schnick
537a73589d Small fixes for mac 2023-01-15 11:16:10 +01:00
Christopher Schnick
4a25a29fbb Fix dependencies 2023-01-14 15:44:12 +01:00
Christopher Schnick
fe52f10ca8 Add host helper 2023-01-10 08:23:28 +01:00
Christopher Schnick
8036b866ad Rework shell choice comp 2023-01-10 08:23:19 +01:00
Christopher Schnick
c882057067 Introduce API for daemon modes 2023-01-09 06:19:02 +01:00
Christopher Schnick
73f3521ee8 Various small improvements 2023-01-07 22:19:54 +01:00
Christopher Schnick
18a8bc8dc5 Add a few more exchanges and improve secret handling 2023-01-07 04:12:17 +01:00
Christopher Schnick
16f165a92c Session fixes 2023-01-05 21:41:33 +01:00
Christopher Schnick
b54066bf89 Implement ability to disable shell history 2023-01-04 20:33:54 +01:00
Christopher Schnick
a6e9b78b09 Refactor shells 2023-01-04 05:23:57 +01:00
Christopher Schnick
b14393134f Various small fixes 2023-01-01 16:38:52 +01:00
Christopher Schnick
742dde4820 Small fixes for error handling 2022-12-31 22:13:07 +01:00
Christopher Schnick
423fad1f05 More cleanup 2022-12-30 12:54:53 +01:00
Christopher Schnick
661ad8a79c Cleanup 2022-12-30 12:54:40 +01:00
Christopher Schnick
b9501bad43 Implement module installations and rework some exchanges 2022-12-30 12:53:16 +01:00
Christopher Schnick
0c8624168f Fix daemon external start up command on mac 2022-12-28 05:05:22 +01:00
Christopher Schnick
e130d1ab20 More installation fixes 2022-12-28 03:57:56 +01:00
Christopher Schnick
a658b0391a Add more installation information 2022-12-28 03:30:06 +01:00
Christopher Schnick
38021704ff Fix installation pass for mac 2022-12-27 14:56:51 +01:00
Christopher Schnick
72b96e8b80 Fix NPE 2022-12-27 11:16:53 +01:00
Christopher Schnick
27732bec35 Fixes for mac 2022-12-27 06:54:42 +01:00
Christopher Schnick
6439f7b754 Various small fixes for beacon exchanges 2022-12-25 10:19:18 +01:00
Christopher Schnick
6b6a6befb8 Bundle annotation indexer 2022-12-23 11:13:23 +01:00
Christopher Schnick
9a2a1a8745 Various small fixes 2022-12-23 10:29:08 +01:00
Christopher Schnick
71b5d2d716 Implement various fixes for sink drains 2022-12-22 03:57:15 +01:00
Christopher Schnick
aa32c52284 Bump version 2022-12-19 21:48:32 +01:00
Christopher Schnick
f6f706a4aa Fix version typo 2022-12-19 21:48:17 +01:00
Christopher Schnick
3d9937bafd More fixes for beacon start up 2022-12-19 21:47:27 +01:00
Christopher Schnick
1dcd88e709 More beacon server startup fixes 2022-12-19 21:19:44 +01:00
Christopher Schnick
32e9772154 Fix beacon server start up failing 2022-12-19 19:55:51 +01:00
Christopher Schnick
402c6850a0 New release 2022-12-19 17:33:54 +01:00
Christopher Schnick
07fd453268 Remove commons codec and commons compress from default commons dependencies 2022-12-19 15:58:01 +01:00
Christopher Schnick
7be90d0666 Refactor 2022-12-19 00:31:50 +01:00
Christopher Schnick
6f912798af Restructure gradle scripts 2022-12-18 18:04:51 +01:00
Christopher Schnick
c38472e870 Bump versions and rework registry query 2022-12-17 22:29:55 +01:00
Christopher Schnick
3436d69d71 Bump gradle and GraalVM versions 2022-12-17 21:37:13 +01:00
Christopher Schnick
a172c9c40b Fix bug in beacon server launch (again) 2022-12-16 21:45:07 +01:00
Christopher Schnick
ac1cbd687f Fix wrong token variable 2022-12-16 20:00:09 +01:00
Christopher Schnick
1e0c7d0505 Small fixes relating to shells 2022-12-16 19:49:40 +01:00
Christopher Schnick
a2cdcb149a Fix beacon server start up on Linux again 2022-12-16 16:45:39 +01:00
Christopher Schnick
afd5dd4553 More fixes for beacon server start up 2022-12-16 13:53:26 +01:00
Christopher Schnick
0798c53762 Fix custom daemon start up 2022-12-16 13:09:07 +01:00
Christopher Schnick
22619224c5 Small bug fixes 2022-12-15 21:52:29 +01:00
Christopher Schnick
5d849cc048 Small fixes for proxies 2022-12-15 20:08:51 +01:00
Christopher Schnick
188051bfa5 Create ExecScriptHelper.java 2022-12-14 20:20:41 +01:00
Christopher Schnick
da1fe7a511 Remove connection hashes and rename machines to shells 2022-12-14 17:40:16 +01:00
Christopher Schnick
1b77a7cc81 Add utility method for mapped list bindings 2022-12-13 23:52:15 +01:00
Christopher Schnick
2b300f32e0 Introduce connection hashes 2022-12-13 23:52:04 +01:00
Christopher Schnick
88ca584410 More shell fixes 2022-12-11 11:19:12 +01:00
Christopher Schnick
e9340c40a8 Small shell fixes and cleanup 2022-12-11 03:45:51 +01:00
Christopher Schnick
87d0830f3c Remove interactivity switch for sh 2022-12-10 05:20:04 +01:00
Christopher Schnick
9f9eccffdd More shell fixes 2022-12-10 04:33:40 +01:00
Christopher Schnick
1ca9e2c751 Small fixes for shells 2022-12-10 00:16:05 +01:00
Christopher Schnick
b3cfae5a92 More fixes for shells and prefs 2022-12-09 01:44:12 +01:00
Christopher Schnick
1982661aa1 Fixes for shells 2022-12-07 21:55:58 +01:00
Christopher Schnick
c15c1204f0 Fixes for shells 2022-12-07 00:13:16 +01:00
Christopher Schnick
936095aa24 Rework on shells 2022-12-05 00:50:58 +01:00
Christopher Schnick
40df1a3233 Small shell changes and move some comps to extension module 2022-12-03 15:39:11 +01:00
Christopher Schnick
0d65db95f9 Small fixes and cleanup 2022-12-02 18:46:46 +01:00
Christopher Schnick
768da75992 Update MessageExchanges.java 2022-12-01 16:28:33 +01:00
Christopher Schnick
c5e886fcfb Deobfuscator fix 2022-12-01 15:40:24 +01:00
Christopher Schnick
fa93d1c8e1 Cleanup 2022-12-01 11:58:06 +01:00
Christopher Schnick
778485b4a9 Deobfuscate received server error messages 2022-12-01 11:27:07 +01:00
Christopher Schnick
5056b248a9 Update workflow calling condition 2022-11-30 19:58:45 +01:00
Christopher Schnick
051b4f3d3d New release 2022-11-30 19:53:56 +01:00
Christopher Schnick
959fd06c37 Integrate fxcomps into extension module 2022-11-30 19:50:04 +01:00
Christopher Schnick
ca5dd74086 Move fxcomps into this repository 2022-11-30 19:23:44 +01:00
Christopher Schnick
74691c5a03 More work on proxies 2022-11-30 19:04:35 +01:00
Christopher Schnick
803ff2ccf2 More work on proxies 2022-11-27 21:39:41 +01:00
Christopher Schnick
de70b0d5b0 Refactor 2022-11-27 14:59:36 +01:00
Christopher Schnick
696dc036ac More fixes for proxies 2022-11-26 16:44:09 +01:00
Christopher Schnick
097d23f306 Basic work for proxies 2022-11-26 12:32:09 +01:00
Christopher Schnick
02a2502fa5 Cleanup 2022-11-24 09:41:03 +01:00
4525 changed files with 137588 additions and 20953 deletions

8
.gitattributes vendored
View file

@ -1,3 +1,7 @@
* text=auto
* text=auto eol=lf
*.sh text eol=lf
*.bat text eol=crlf
*.png binary
*.xcf binary
*.xcf binary
*.properties linguist-generated

10
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: "gradle"
directory: "/"
schedule:
interval: "daily"
- package-ecosystem: github-actions
directory: /
schedule:
interval: "daily"

View file

@ -1,28 +0,0 @@
name: Build
on:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-20.04
steps:
- name: Git checkout
uses: actions/checkout@v2
with:
submodules: 'true'
- name: Set up GraalVM
uses: graalvm/setup-graalvm@v1
with:
version: '22.3.0'
java-version: '19'
github-token: ${{ secrets.XPIPE_GITHUB_TOKEN }}
- name: Verify Gradle Wrapper
uses: gradle/wrapper-validation-action@v1
- name: Execute build
run: ./gradlew clean build

View file

@ -1,39 +0,0 @@
name: Publish
on:
push:
branches:
- master
jobs:
publish:
runs-on: ubuntu-20.04
steps:
- name: Git checkout
uses: actions/checkout@v2
with:
submodules: 'true'
- name: Set up GraalVM
uses: graalvm/setup-graalvm@v1
with:
version: '22.3.0'
java-version: '19'
github-token: ${{ secrets.XPIPE_GITHUB_TOKEN }}
- name: Verify Gradle Wrapper
uses: gradle/wrapper-validation-action@v1
- name: Publish
run: ./gradlew publish
env:
GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }}
GPG_KEY: ${{ secrets.GPG_KEY }}
SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
- name: JReleaser
run: ./gradlew jreleaserRelease
env:
XPIPE_GITHUB_TOKEN: ${{ secrets.XPIPE_GITHUB_TOKEN }}
XPIPE_DISCORD_WEBHOOK: ${{ secrets.XPIPE_DISCORD_WEBHOOK }}

23
.gitignore vendored
View file

@ -1,11 +1,24 @@
.gradle/
build/
.idea
local/
local_test/
local_stage/
.idea/*
!.idea/codeStyles
!.idea/inspectionProfiles
lib/
dev.properties
extensions.txt
dev_storage
local/
local*/
local_*/
.vs
.vscode
bin
obj
out
bin
.DS_Store
ComponentsGenerated.wxs
!dist/javafx/**/lib
!dist/javafx/**/bin
xcuserdata/
*.dylib
project.xcworkspace

127
CODE_OF_CONDUCT.md Normal file
View file

@ -0,0 +1,127 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement via [hello@xpipe.io](mailto:hello@xpipe.io).
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

117
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,117 @@
# Development
Any contribution is welcomed!
There are no real formal contribution guidelines right now, they will maybe come later.
## Repository Structure
- [core](core) - Shared core classes of the XPipe Java API, XPipe extensions, and the XPipe daemon implementation.
This mainly concerns API classes not a lot of implementation.
- [beacon](beacon) - The XPipe beacon component is responsible for handling all communications between the XPipe
daemon and the client applications, for example APIs and the CLI
- [app](app) - Contains the XPipe daemon implementation, the XPipe desktop application, and an
API to create all different kinds of extensions for the XPipe platform
- [dist](dist) - Tools to create a distributable package of XPipe
- [ext](ext) - Available XPipe extensions. Essentially every concrete feature implementation is implemented as an extension
## Development Setup
You need to have an up-to-date version of XPipe installed on your local system in order to properly
run XPipe in a development environment.
This is due to the fact that some components are only included in the release version and not in this repository.
XPipe is able to automatically detect your local installation and fetch the required
components from it when it is run in a development environment.
Note that in case the current master branch is ahead of the latest release, it might happen that there are some incompatibilities when loading data from your local XPipe installation.
You should therefore always check out the matching version tag for your local repository and local XPipe installation.
You can find the available version tags at https://github.com/xpipe-io/xpipe/tags.
So for example if you currently have XPipe `13.0` installed, you should run `git reset --hard 13.0` first to properly compile against it.
You need to have JDK for Java 22 installed to compile the project.
If you are on Linux or macOS, you can easily accomplish that by running
```bash
curl -s "https://get.sdkman.io" | bash
. "$HOME/.sdkman/bin/sdkman-init.sh"
sdk install java 22.0.2-graalce
sdk default java 22.0.2-graalce
```
.
On Windows, you have to manually install a JDK, e.g. from [Adoptium](https://adoptium.net/temurin/releases/?version=21).
You can configure a few development options in the file `app/dev.properties` which will be automatically generated when gradle is first run.
## Building and Running
You can use the gradle wrapper to build and run the project:
- `gradlew app:run` will run the desktop application. You can set various useful properties in `app/build.gradle`
- `gradlew clean dist` will create a distributable production version in `dist/build/dist/base`.
- `gradlew <project>:test` will run the tests of the specified project.
You are also able to properly debug the built production application through two different methods:
- The `dist/build/dist/base/app/scripts/xpiped_debug` script will launch the application in debug mode and with a console attached to it
- The `dist/build/dist/base/app/scripts/xpiped_debug_attach` script attaches a debugger with the help of [AttachMe](https://plugins.jetbrains.com/plugin/13263-attachme).
Just make sure that the attachme process is running within IntelliJ, and the debugger should launch automatically once you start up the application.
Note that when any unit test is run using a debugger, the XPipe daemon process that is started will also attempt
to connect to that debugger through [AttachMe](https://plugins.jetbrains.com/plugin/13263-attachme) as well.
## Modularity and IDEs
All XPipe components target [Java 22](https://openjdk.java.net/projects/jdk/22/) and make full use of the Java Module System (JPMS).
All components are modularized, including all their dependencies.
In case a dependency is (sadly) not modularized yet, module information is manually added using [extra-java-module-info](https://github.com/gradlex-org/extra-java-module-info).
Further, note that as this is a pretty complicated Java project that fully utilizes modularity,
many IDEs still have problems building this project properly.
For example, you can't build this project in eclipse or vscode as it will complain about missing modules.
The tested and recommended IDE is IntelliJ.
When setting up the project in IntelliJ, make sure that the correct JDK (Java 22)
is selected both for the project and for gradle itself.
## Contributing guide
Especially when starting out, it might be a good idea to start with easy tasks first. Here's a selection of suitable common tasks that are very easy to implement:
### Interacting via the HTTP API
You can create clients that communicate with the XPipe daemon via its HTTP API.
To get started, see the [OpenAPI spec](/openapi.yaml).
### Implementing support for a new editor
All code for handling external editors can be found [here](https://github.com/xpipe-io/xpipe/blob/master/app/src/main/java/io/xpipe/app/prefs/ExternalEditorType.java). There you will find plenty of working examples that you can use as a base for your own implementation.
### Implementing support for a new terminal
All code for handling external terminals can be found [here](https://github.com/xpipe-io/xpipe/blob/master/app/src/main/java/io/xpipe/app/terminal/). There you will find plenty of working examples that you can use as a base for your own implementation.
### Adding more context menu actions in the file browser
In case you want to implement your own actions for certain file types in the file browser, you can easily do so. You can find most existing actions [here](https://github.com/xpipe-io/xpipe/tree/master/ext/base/src/main/java/io/xpipe/ext/base/browser) to get some inspiration.
Once you created your custom classes, you have to register them in your module info, just like [here](https://github.com/xpipe-io/xpipe/blob/master/ext/base/src/main/java/module-info.java).
### Implementing custom actions for the connection hub
All actions that you can perform for certain connections in the connection overview tab are implemented using an [Action API](https://github.com/xpipe-io/xpipe/blob/master/app/src/main/java/io/xpipe/app/ext/ActionProvider.java). You can find a sample implementation [here](https://github.com/xpipe-io/xpipe/blob/master/ext/base/src/main/java/io/xpipe/ext/base/action/SampleAction.java) and many common action implementations [here](https://github.com/xpipe-io/xpipe/tree/master/ext/base/src/main/java/io/xpipe/ext/base/action).
### Adding more predefined scripts
You can add custom script definitions [here](https://github.com/xpipe-io/xpipe/tree/master/ext/base/src/main/java/io/xpipe/ext/base/script/PredefinedScriptStore.java) and [here](https://github.com/xpipe-io/xpipe/tree/master/ext/base/src/main/resources/io/xpipe/ext/base/resources/scripts).
### Adding more system icons for system autodetection
You can register new system types [here](https://github.com/xpipe-io/xpipe/blob/master/app/src/main/java/io/xpipe/app/resources/SystemIcons.java) and add the respective icons [here](https://github.com/xpipe-io/xpipe/tree/master/app/src/main/resources/io/xpipe/app/resources/img/system).
### Adding more file icons for specific types
You can register file types [here](https://github.com/xpipe-io/xpipe/blob/master/app/src/main/resources/io/xpipe/app/resources/file_list.txt) and add the respective icons [here](https://github.com/xpipe-io/xpipe/tree/master/app/src/main/resources/io/xpipe/app/resources/img/browser).
The existing file list and icons are taken from the [vscode-icons](https://github.com/vscode-icons/vscode-icons) project. Due to limitations in the file definition list compatibility, some file types might not be listed by their proper extension and are therefore not being applied correctly even though the images and definitions exist already.
### Implementing something else
if you want to work on something that was not listed here, you can still do so of course. You can reach out on the [Discord server](https://discord.gg/8y89vS8cRb) to discuss any development plans and get you started.
### Adding translations
See the [translation guide](/lang) for details.

View file

@ -1,7 +0,0 @@
Copyright 2022 Christopher Schnick
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

203
LICENSE.md Normal file
View file

@ -0,0 +1,203 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2023 Christopher Schnick
Copyright 2023 XPipe UG (haftungsbeschränkt)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

213
README.md
View file

@ -1,37 +1,196 @@
[![Build Status](https://github.com/xpipe-io/xpipe_java/actions/workflows/build.yml/badge.svg)](https://github.com/xpipe-io/xpipe_java/actions/workflows/build.yml)
[![Publish Status](https://github.com/xpipe-io/xpipe_java/actions/workflows/publish.yml/badge.svg)](https://github.com/xpipe-io/xpipe_java/actions/workflows/publish.yml)
<p align="center">
<a href="https://xpipe.io" target="_blank" rel="noopener">
<img src="https://github.com/xpipe-io/.github/raw/main/img/banner.png" alt="XPipe Banner" />
</a>
</p>
## X-Pipe Java
<h1></h1>
The fundamental components of the [X-Pipe project](https://xpipe.io).
This repository contains the following four modules:
## About
- Core - Shared core classes of the X-Pipe Java API, X-Pipe extensions, and the X-Pipe daemon implementation
- API - The API that can be used to interact with X-Pipe from any JVM-based language10
- Beacon - The X-Pipe beacon component is responsible for handling all communications between the X-Pipe daemon
and the client applications, for example the various programming language APIs and the CLI
- Extension - An API to create all different kinds of extensions for the X-Pipe platform
XPipe is a new type of shell connection hub and remote file manager that allows you to access your entire server infrastructure from your local machine. It works on top of your installed command-line programs and does not require any setup on your remote systems. So if you normally use CLI tools like `ssh`, `docker`, `kubectl`, etc. to connect to your servers, you can just use XPipe on top of that.
## Installation / Usage
XPipe fully integrates with your tools such as your favourite text/code editors, terminals, shells, command-line tools and more. The platform is designed to be extensible, allowing anyone to add easily support for more tools or to implement custom functionality through a modular extension system.
The *core* and *extension* modules are used in X-Pipe extension development.
For setup instructions, see the [X-Pipe extension development](https://xpipe-io.readthedocs.io/en/latest/dev/extensions/index.html) section.
It currently supports:
The *beacon* module handles all communication and serves as a
reference when implementing the communication of an API or program that interacts with the X-Pipe daemon.
- [SSH](https://docs.xpipe.io/guide/ssh) connections, config files, and tunnels
- [Docker](https://docs.xpipe.io/guide/docker), [Podman](https://docs.xpipe.io/guide/podman), [LXD](https://docs.xpipe.io/guide/lxc), and [incus](https://docs.xpipe.io/guide/lxc) containers
- [Proxmox PVE](https://docs.xpipe.io/guide/proxmox) virtual machines and containers
- [Hyper-V](https://docs.xpipe.io/guide/hyperv), [KVM](https://docs.xpipe.io/guide/kvm), [VMware Player/Workstation/Fusion](https://docs.xpipe.io/guide/vmware) virtual machines
- [Kubernetes](https://docs.xpipe.io/guide/kubernetes) clusters, pods, and containers
- [Tailscale](https://docs.xpipe.io/guide/tailscale) and [Teleport](https://docs.xpipe.io/guide/teleport) connections
- Windows Subsystem for Linux, Cygwin, and MSYS2 environments
- Powershell Remote Sessions
- RDP and VNC connections
The *api* module serves as a reference implementation for other potential X-Pipe APIs
and can also be used to access X-Pipe functionalities from your Java programs.
For setup instructions, see the [X-Pipe Java API Usage](https://xpipe-io.readthedocs.io/en/latest/dev/api/java/index.html) section.
## Connection hub
## Development Notes
- Easily connect to and access all kinds of remote connections in one place
- Organize all your connections in hierarchical categories so you can keep an overview hundreds of connections
- Create specific login environments on any system to instantly jump into a properly set up environment for every use case
- Quickly perform various commonly used actions like starting/stopping containers, establishing tunnels, and more
- Create desktop shortcuts that automatically open remote connections in your terminal without having to open any GUI
All X-Pipe components target [JDK 17](https://openjdk.java.net/projects/jdk/17/) and make full use of the Java Module System (JPMS).
All components are modularized, including all their dependencies.
In case a dependency is (sadly) not modularized yet, module information is manually added using [moditect](https://github.com/moditect/moditect-gradle-plugin).
These dependency generation rules are accumulated in the [X-Pipe dependencies](https://github.com/xpipe-io/xpipe_java_deps)
repository, which is shared between all components and integrated as a git submodule.
![Connections](https://github.com/xpipe-io/.github/raw/main/img/hub_shadow.png)
Some unit tests depend on a connection to an X-Pipe daemon to properly function.
To launch the installed daemon, it is required that you either have X-Pipe
installed or have set the `XPIPE_HOME` environment variable in case you are using a portable version.
## Powerful file management
- Interact with the file system of any remote system using a workflow optimized for professionals
- Quickly open a terminal session into any directory in your favourite terminal emulator
- Utilize your entire arsenal of locally installed programs to open and edit remote files
- Dynamically elevate sessions with sudo when required without having to restart the session
- Seamlessly transfer files from and to your system desktop environment
- Work and perform transfers on multiple systems at the same time with the built-in tabbed multitasking
![Browser](https://github.com/xpipe-io/.github/raw/main/img/browser_shadow.png)
## Terminal launcher
- Boots you into a shell session in your favourite terminal with one click. Automatically fills password prompts and more
- Comes with support for all commonly used terminal emulators across all operating systems
- Supports opening custom terminal emulators as well via a custom command-line spec
- Works with all command shells such as bash, zsh, cmd, PowerShell, and more, locally and remote
- Connects to a system while the terminal is still starting up, allowing for faster connections than otherwise possible
![Terminal](https://github.com/xpipe-io/.github/raw/main/img/terminal_shadow.png)
<br>
<p align="center">
<img src="https://github.com/xpipe-io/.github/raw/main/img/terminal.gif" alt="Terminal launcher"/>
</p>
<br>
## Versatile scripting system
- Create reusable simple shell scripts, templates, and groups to run on connected remote systems
- Automatically make your scripts available in the PATH on any remote system without any setup
- Setup shell init environments for connections to fully customize your work environment for every purpose
- Open custom shells and custom remote connections by providing your own commands
![scripts](https://github.com/xpipe-io/.github/raw/main/img/scripts_shadow.png)
## Secure vault
- All data is stored exclusively on your local system in a cryptographically secure vault. You can also choose to increase security by using a custom master passphrase for further encryption
- XPipe is able to retrieve secrets automatically from your password manager via it's command-line interface.
- There are no servers involved, all your information stays on your systems. The XPipe application does not send any personal or sensitive information to outside services.
- Vault changes can be pushed and pulled from your own remote git repository by multiple team members across many systems
# Downloads
Note that this is a desktop application that should be run on your local desktop workstation, not on any server or containers. It will be able to connect to your server infrastructure from there.
## Windows
Installers are the easiest way to get started and come with an optional automatic update functionality:
- [Windows .msi Installer (x86-64)](https://github.com/xpipe-io/xpipe/releases/latest/download/xpipe-installer-windows-x86_64.msi)
If you don't like installers, you can also use a portable version that is packaged as an archive:
- [Windows .zip Portable (x86-64)](https://github.com/xpipe-io/xpipe/releases/latest/download/xpipe-portable-windows-x86_64.zip)
Alternatively, you can also use the following package managers:
- [choco](https://community.chocolatey.org/packages/xpipe) to install it with `choco install xpipe`.
- [winget](https://github.com/microsoft/winget-cli) to install it with `winget install xpipe-io.xpipe --source winget`.
## macOS
Installers are the easiest way to get started and come with an optional automatic update functionality:
- [MacOS .pkg Installer (x86-64)](https://github.com/xpipe-io/xpipe/releases/latest/download/xpipe-installer-macos-x86_64.pkg)
- [MacOS .pkg Installer (ARM 64)](https://github.com/xpipe-io/xpipe/releases/latest/download/xpipe-installer-macos-arm64.pkg)
If you don't like installers, you can also use a portable version that is packaged as an archive:
- [MacOS .dmg Portable (x86-64)](https://github.com/xpipe-io/xpipe/releases/latest/download/xpipe-portable-macos-x86_64.dmg)
- [MacOS .dmg Portable (ARM 64)](https://github.com/xpipe-io/xpipe/releases/latest/download/xpipe-portable-macos-arm64.dmg)
Alternatively, you can also use [Homebrew](https://github.com/xpipe-io/homebrew-tap) to install XPipe with `brew install --cask xpipe-io/tap/xpipe`.
## Linux
You can install XPipe the fastest by pasting the installation command into your terminal. This will perform the setup automatically.
The script supports installation via `apt`, `dnf`, `yum`, `zypper`, `rpm`, and `pacman` on Linux:
```
bash <(curl -sL https://github.com/xpipe-io/xpipe/raw/master/get-xpipe.sh)
```
Of course, there are also other installation methods available.
### Debian-based distros
The following debian installers are available:
- [Linux .deb Installer (x86-64)](https://github.com/xpipe-io/xpipe/releases/latest/download/xpipe-installer-linux-x86_64.deb)
- [Linux .deb Installer (ARM 64)](https://github.com/xpipe-io/xpipe/releases/latest/download/xpipe-installer-linux-arm64.deb)
Note that you should use apt to install the package with `sudo apt install <file>` as other package managers, for example dpkg,
are not able to resolve and install any dependency packages.
### RHEL-based distros
The rpm releases are signed with the GPG key https://xpipe.io/signatures/crschnick.asc.
You can import it via `rpm --import https://xpipe.io/signatures/crschnick.asc` to allow your rpm-based package manager to verify the release signature.
The following rpm installers are available:
- [Linux .rpm Installer (x86-64)](https://github.com/xpipe-io/xpipe/releases/latest/download/xpipe-installer-linux-x86_64.rpm)
- [Linux .rpm Installer (ARM 64)](https://github.com/xpipe-io/xpipe/releases/latest/download/xpipe-installer-linux-arm64.rpm)
The same applies here, you should use a package manager that supports resolving and installing required dependencies if needed.
### Arch
There is an official [AUR package](https://aur.archlinux.org/packages/xpipe) available that you can either install manually or via an AUR helper such as with `yay -S xpipe`.
### NixOS
There's an official [xpipe nixpkg](https://search.nixos.org/packages?channel=unstable&show=xpipe&from=0&size=50&sort=relevance&type=packages&query=xpipe) available that you can install with `nix-env -iA nixos.xpipe`. This one is however not always up to date.
There is also a custom repository that contains the latest up-to-date releases: https://github.com/xpipe-io/nixpkg.
You can install XPipe by following the instructions in the linked repository.
### Portable
In case you prefer to use an archive version that you can extract anywhere, you can use these:
- [Linux .tar.gz Portable (x86-64)](https://github.com/xpipe-io/xpipe/releases/latest/download/xpipe-portable-linux-x86_64.tar.gz)
- [Linux .tar.gz Portable (ARM 64)](https://github.com/xpipe-io/xpipe/releases/latest/download/xpipe-portable-linux-arm64.tar.gz)
Alternatively, there are also AppImages available:
- [Linux .AppImage Portable (x86-64)](https://github.com/xpipe-io/xpipe/releases/latest/download/xpipe-portable-linux-x86_64.AppImage)
- [Linux .AppImage Portable (ARM 64)](https://github.com/xpipe-io/xpipe/releases/latest/download/xpipe-portable-linux-arm64.AppImage)
Note that the portable version assumes that you have some basic packages for graphical systems already installed
as it is not a perfect standalone version. It should however run on most systems.
## Docker container
XPipe is a desktop application first and foremost. It requires a full desktop environment to function with various installed applications such as terminals, editors, shells, CLI tools, and more. So there is no true web-based interface for XPipe.
Since it might make sense however to access your XPipe environment from the web, there is also a so-called webtop docker container image for XPipe. [XPipe Webtop](https://github.com/xpipe-io/xpipe-webtop) is a web-based desktop environment that can be run in a container and accessed from a browser via KasmVNC. The desktop environment comes with XPipe and various terminals and editors preinstalled and configured.
# Further information
## Open source model
XPipe follows an open core model, which essentially means that the main application is open source while certain other components are not. This mainly concerns the features only available in the homelab/professional plan and the shell handling library implementation. Furthermore, some CI pipelines and tests that run on private servers are also not included in the open repository.
The distributed XPipe application consists out of two parts:
- The open-source core that you can find this repository. It is licensed under the [Apache License 2.0](/LICENSE.md).
- The closed-source extensions, mostly for homelab/professional plan features, which are not included in this repository
Additional features are available in the homelab/professional plan . For more details see https://xpipe.io/pricing.
If your enterprise puts great emphasis on having access to the full source code, there are also full source-available enterprise options available.
## Documentation
You can find the documentation at https://docs.xpipe.io.
## Discord
[![Discord](https://discordapp.com/api/guilds/979695018782646285/widget.png?style=banner2)](https://discord.gg/8y89vS8cRb)

7
SECURITY.md Normal file
View file

@ -0,0 +1,7 @@
# Security
Due to its nature, XPipe has to handle a lot of sensitive information. Therefore, the security, integrity, and privacy of your data has topmost priority.
More information about the security approach of the XPipe application can be found on the documentation website at https://docs.xpipe.io/reference/security.
You can report security vulnerabilities in this GitHub repository in a confidential manner. We will get back to you as soon as possible if you do.

View file

@ -1,20 +0,0 @@
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.xpipe/xpipe-api/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.xpipe/xpipe-api)
[![javadoc](https://javadoc.io/badge2/io.xpipe/xpipe-api/javadoc.svg)](https://javadoc.io/doc/io.xpipe/xpipe-api)
## X-Pipe Java API
The X-Pipe API for Java allows you to use most of the X-Pipe functionality from Java applications:
- Create data stores and data sources
- Query and work with the contents of data sources
- Write data to data sources
## Setup
Either install the [maven dependency](https://maven-badges.herokuapp.com/maven-central/io.xpipe/xpipe-api) from Maven Central
using your favourite build tool or alternatively download the `xpipe-api.jar`, `xpipe-core.jar`, and `xpipe-beacon.jar`
from the [releases page](https://github.com/xpipe-io/xpipe_java/releases/latest) and add them to the classpath.
## Usage
See [the API documentation](https://xpipe-io.readthedocs.io/en/latest/dev/api/java/index.html).

View file

@ -1,38 +0,0 @@
plugins {
id 'java-library'
id 'maven-publish'
id 'signing'
id "org.moditect.gradleplugin" version "1.0.0-rc3"
}
apply from: "$projectDir/../gradle_scripts/java.gradle"
apply from: "$projectDir/../gradle_scripts/junit.gradle"
System.setProperty('excludeExtensionLibrary', 'true')
apply from: "$projectDir/../gradle_scripts/extension_test.gradle"
version = file('../misc/version').text
group = 'io.xpipe'
archivesBaseName = 'xpipe-api'
repositories {
mavenCentral()
}
test {
enabled = true
}
dependencies {
api project(':core')
implementation project(':beacon')
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: "2.13.0"
}
configurations {
testImplementation.extendsFrom(dep)
}
apply from: 'publish.gradle'
apply from: "$projectDir/../gradle_scripts/publish-base.gradle"

View file

@ -1,40 +0,0 @@
publishing {
publications {
mavenJava(MavenPublication) {
artifactId = project.archivesBaseName
from components.java
pom.withXml {
def pomNode = asNode()
pomNode.dependencies.'*'.findAll().each() {
it.scope*.value = 'compile'
}
}
pom {
name = 'X-Pipe Java API'
description = 'Contains everything necessary to interact with X-Pipe from Java applications.'
url = 'https://github.com/xpipe-io/xpipe_java/api'
licenses {
license {
name = 'The MIT License (MIT)'
url = 'https://github.com/xpipe-io/xpipe_java/LICENSE.md'
}
}
developers {
developer {
id = 'crschnick'
name = 'Christopher Schnick'
email = 'crschnick@xpipe.io'
}
}
scm {
connection = 'scm:git:git://github.com/xpipe-io/xpipe_java.git'
developerConnection = 'scm:git:ssh://github.com/xpipe-io/xpipe_java.git'
url = 'https://github.com/xpipe-io/xpipe_java'
}
}
}
}
}

View file

@ -1,12 +0,0 @@
package io.xpipe.api;
import java.io.InputStream;
public interface DataRaw extends DataSource {
InputStream open();
byte[] readAll();
byte[] read(int maxBytes);
}

View file

@ -1,229 +0,0 @@
package io.xpipe.api;
import io.xpipe.api.impl.DataSourceImpl;
import io.xpipe.core.source.DataSourceId;
import io.xpipe.core.source.DataSourceReference;
import io.xpipe.core.source.DataSourceType;
import io.xpipe.core.store.DataStore;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
/**
* Represents a reference to a data source that is managed by X-Pipe.
* <p>
* The actual data is only queried when required and is not cached.
* Therefore, the queried data is always up-to-date at the point of calling a method that queries the data.
* <p>
* As soon a data source reference is created, the data source is locked
* within X-Pipe to prevent concurrent modification and the problems that can arise from it.
* By default, the lock is held until the calling program terminates and prevents
* other applications from modifying the data source in any way.
* To unlock the data source earlier, you can make use the {@link #unlock()} method.
*/
public interface DataSource {
/**
* NOT YET IMPLEMENTED!
* <p>
* Creates a new supplier data source that will be interpreted as the generated data source.
* In case this program should be a data source generator, this method has to be called at
* least once to register that it actually generates a data source.
* <p>
* All content that is written to this data source until the generator program terminates is
* will be available later on when the data source is used as a supplier later on.
* <p>
* In case this method is called multiple times, the same data source is returned.
*
* @return the generator data source
*/
static DataSource drain() {
return null;
}
/**
* NOT YET IMPLEMENTED!
* <p>
* Creates a data source sink that will block with any read operations
* until an external data producer routes the output into this sink.
*/
static DataSource sink() {
return null;
}
/**
* Wrapper for {@link #get(DataSourceReference)}.
*
* @throws IllegalArgumentException if {@code id} is not a valid data source id
*/
static DataSource getById(String id) {
return get(DataSourceReference.id(id));
}
/**
* Wrapper for {@link #get(DataSourceReference)} using the latest reference.
*/
static DataSource getLatest() {
return get(DataSourceReference.latest());
}
/**
* Wrapper for {@link #get(DataSourceReference)} using a name reference.
*/
static DataSource getByName(String name) {
return get(DataSourceReference.name(name));
}
/**
* Retrieves the data source for a given reference.
*
* @param ref the data source reference
*/
static DataSource get(DataSourceReference ref) {
return DataSourceImpl.get(ref);
}
/**
* Releases the lock held by this program for this data source such
* that other applications can modify the data source again.
*/
static void unlock() {
throw new UnsupportedOperationException();
}
/**
* Wrapper for {@link #create(DataSourceId, String, InputStream)} that creates an anonymous data source.
*/
public static DataSource createAnonymous(String type, Path path) {
return create(null, type, path);
}
/**
* Wrapper for {@link #create(DataSourceId, String, InputStream)}.
*/
public static DataSource create(DataSourceId id, String type, Path path) {
try (var in = Files.newInputStream(path)) {
return create(id, type, in);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
/**
* Wrapper for {@link #create(DataSourceId, String, InputStream)} that creates an anonymous data source.
*/
public static DataSource createAnonymous(String type, URL url) {
return create(null, type, url);
}
/**
* Wrapper for {@link #create(DataSourceId, String, InputStream)}.
*/
public static DataSource create(DataSourceId id, String type, URL url) {
try (var in = url.openStream()) {
return create(id, type, in);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
/**
* Wrapper for {@link #create(DataSourceId, String, InputStream)} that creates an anonymous data source.
*/
public static DataSource createAnonymous(String type, InputStream in) {
return create(null, type, in);
}
/**
* Creates a new data source from an input stream.
*
* @param id the data source id
* @param type the data source type
* @param in the input stream to read
* @return a {@link DataSource} instances that can be used to access the underlying data
*/
public static DataSource create(DataSourceId id, String type, InputStream in) {
return DataSourceImpl.create(id, type, in);
}
/**
* Creates a new data source from an input stream.
*
* @param id the data source id
* @return a {@link DataSource} instances that can be used to access the underlying data
*/
public static DataSource create(DataSourceId id, io.xpipe.core.source.DataSource<?> source) {
return DataSourceImpl.create(id, source);
}
/**
* Creates a new data source from an input stream.
* 1
*
* @param id the data source id
* @param type the data source type
* @param in the data store to add
* @return a {@link DataSource} instances that can be used to access the underlying data
*/
public static DataSource create(DataSourceId id, String type, DataStore in) {
return DataSourceImpl.create(id, type, in);
}
void forwardTo(DataSource target);
void appendTo(DataSource target);
public io.xpipe.core.source.DataSource<?> getInternalSource();
/**
* Returns the id of this data source.
*/
DataSourceId getId();
/**
* Returns the type of this data source.
*/
DataSourceType getType();
DataSourceConfig getConfig();
/**
* Attempts to cast this object to a {@link DataTable}.
*
* @throws UnsupportedOperationException if the data source is not a table
*/
default DataTable asTable() {
throw new UnsupportedOperationException("Data source is not a table");
}
/**
* Attempts to cast this object to a {@link DataStructure}.
*
* @throws UnsupportedOperationException if the data source is not a structure
*/
default DataStructure asStructure() {
throw new UnsupportedOperationException("Data source is not a structure");
}
/**
* Attempts to cast this object to a {@link DataText}.
*
* @throws UnsupportedOperationException if the data source is not a text
*/
default DataText asText() {
throw new UnsupportedOperationException("Data source is not a text");
}
/**
* Attempts to cast this object to a {@link DataRaw}.
*
* @throws UnsupportedOperationException if the data source is not raw
*/
default DataRaw asRaw() {
throw new UnsupportedOperationException("Data source is not raw");
}
}

View file

@ -1,32 +0,0 @@
package io.xpipe.api;
import java.util.Map;
/**
* Represents the current configuration of a data source.
*/
public final class DataSourceConfig {
/**
* The data source provider id.
*/
private final String provider;
/**
* The set configuration parameters.
*/
private final Map<String, String> configInstance;
public DataSourceConfig(String provider, Map<String, String> configInstance) {
this.provider = provider;
this.configInstance = configInstance;
}
public String getProvider() {
return provider;
}
public Map<String, String> getConfig() {
return configInstance;
}
}

View file

@ -1,23 +0,0 @@
package io.xpipe.api;
import io.xpipe.api.connector.XPipeApiConnection;
import io.xpipe.beacon.exchange.cli.StoreAddExchange;
import io.xpipe.beacon.util.QuietDialogHandler;
import io.xpipe.core.store.DataStore;
import java.util.Map;
public class DataStores {
public static void addNamedStore(DataStore store, String name) {
XPipeApiConnection.execute(con -> {
var req = StoreAddExchange.Request.builder()
.storeInput(store)
.name(name)
.build();
StoreAddExchange.Response res = con.performSimpleExchange(req);
new QuietDialogHandler(res.getConfig(), con, Map.of()).handle();
});
}
}

View file

@ -1,7 +0,0 @@
package io.xpipe.api;
import io.xpipe.core.data.node.DataStructureNode;
public interface DataStructure extends DataSource {
DataStructureNode read();
}

View file

@ -1,26 +0,0 @@
package io.xpipe.api;
import io.xpipe.core.data.node.ArrayNode;
import io.xpipe.core.data.node.TupleNode;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
public interface DataTable extends Iterable<TupleNode>, DataSource {
Stream<TupleNode> stream();
ArrayNode readAll();
ArrayNode read(int maxRows);
default int countAndDiscard() {
AtomicInteger count = new AtomicInteger();
try (var stream = stream()) {
stream.forEach(dataStructureNodes -> {
count.getAndIncrement();
});
}
return count.get();
}
}

View file

@ -1,52 +0,0 @@
package io.xpipe.api;
import io.xpipe.api.impl.DataTableAccumulatorImpl;
import io.xpipe.core.data.node.DataStructureNode;
import io.xpipe.core.data.node.DataStructureNodeAcceptor;
import io.xpipe.core.data.type.TupleType;
import io.xpipe.core.source.DataSourceId;
/**
* An accumulator for table data.
* <p>
* This class can be used to construct new table data sources by
* accumulating the rows using {@link #add(DataStructureNode)} or {@link #acceptor()} and then calling
* {@link #finish(DataSourceId)} to complete the construction process and create a new data source.
*/
public interface DataTableAccumulator {
public static DataTableAccumulator create(TupleType type) {
return new DataTableAccumulatorImpl(type);
}
/**
* Wrapper for {@link #finish(DataSourceId)}.
*/
default DataTable finish(String id) {
return finish(DataSourceId.fromString(id));
}
/**
* Finishes the construction process and returns the data source reference.
*
* @param id the data source id to assign
*/
DataTable finish(DataSourceId id);
/**
* Adds a row to the table.
*
* @param row the row to add
*/
void add(DataStructureNode row);
/**
* Creates a tuple acceptor that adds all accepted tuples to the table.
*/
DataStructureNodeAcceptor<DataStructureNode> acceptor();
/**
* Returns the current amount of rows added to the table.
*/
int getCurrentRows();
}

View file

@ -1,17 +0,0 @@
package io.xpipe.api;
import java.util.List;
import java.util.stream.Stream;
public interface DataText extends DataSource {
List<String> readAllLines();
List<String> readLines(int maxLines);
Stream<String> lines();
String readAll();
String read(int maxCharacters);
}

View file

@ -1,151 +0,0 @@
package io.xpipe.api.connector;
import io.xpipe.beacon.BeaconClient;
import io.xpipe.beacon.BeaconConnection;
import io.xpipe.beacon.BeaconException;
import io.xpipe.beacon.BeaconServer;
import io.xpipe.beacon.exchange.cli.DialogExchange;
import io.xpipe.core.dialog.DialogReference;
import io.xpipe.core.util.XPipeDaemonMode;
import io.xpipe.core.util.XPipeInstallation;
import java.util.Optional;
public final class XPipeApiConnection extends BeaconConnection {
private XPipeApiConnection() {
}
public static XPipeApiConnection open() {
var con = new XPipeApiConnection();
con.constructSocket();
return con;
}
public static void finishDialog(DialogReference reference) {
try (var con = new XPipeApiConnection()) {
con.constructSocket();
var element = reference.getStart();
while (true) {
if (element != null && element.requiresExplicitUserInput()) {
throw new IllegalStateException();
}
DialogExchange.Response response = con.performSimpleExchange(DialogExchange.Request.builder()
.dialogKey(reference.getDialogId())
.build());
element = response.getElement();
if (response.getElement() == null) {
break;
}
}
} catch (BeaconException e) {
throw e;
} catch (Exception e) {
throw new BeaconException(e);
}
}
public static void execute(Handler handler) {
try (var con = new XPipeApiConnection()) {
con.constructSocket();
handler.handle(con);
} catch (BeaconException e) {
throw e;
} catch (Exception e) {
throw new BeaconException(e);
}
}
public static <T> T execute(Mapper<T> mapper) {
try (var con = new XPipeApiConnection()) {
con.constructSocket();
return mapper.handle(con);
} catch (BeaconException e) {
throw e;
} catch (Exception e) {
throw new BeaconException(e);
}
}
public static Optional<BeaconClient> waitForStartup(Process process) {
for (int i = 0; i < 160; i++) {
if (process != null && !process.isAlive() && process.exitValue() != 0) {
return Optional.empty();
}
try {
Thread.sleep(500);
} catch (InterruptedException ignored) {
}
var s = BeaconClient.tryConnect(BeaconClient.ApiClientInformation.builder()
.version("?")
.language("Java")
.build());
if (s.isPresent()) {
return s;
}
}
return Optional.empty();
}
public static void waitForShutdown() {
for (int i = 0; i < 40; i++) {
try {
Thread.sleep(500);
} catch (InterruptedException ignored) {
}
var r = BeaconServer.isRunning();
if (!r) {
return;
}
}
}
@Override
protected void constructSocket() {
if (!BeaconServer.isRunning()) {
try {
start();
} catch (Exception ex) {
throw new BeaconException("Unable to start xpipe daemon", ex);
}
var r = waitForStartup(null);
if (r.isEmpty()) {
throw new BeaconException("Wait for xpipe daemon timed out");
} else {
beaconClient = r.get();
return;
}
}
try {
beaconClient = BeaconClient.connect(BeaconClient.ApiClientInformation.builder()
.version("?")
.language("Java")
.build());
} catch (Exception ex) {
throw new BeaconException("Unable to connect to running xpipe daemon", ex);
}
}
private void start() throws Exception {
var installation = XPipeInstallation.getLocalDefaultInstallationBasePath(true);
BeaconServer.start(installation, XPipeDaemonMode.BACKGROUND);
}
@FunctionalInterface
public static interface Handler {
void handle(BeaconConnection con) throws Exception;
}
@FunctionalInterface
public static interface Mapper<T> {
T handle(BeaconConnection con) throws Exception;
}
}

View file

@ -1,44 +0,0 @@
package io.xpipe.api.impl;
import io.xpipe.api.DataRaw;
import io.xpipe.api.DataSourceConfig;
import io.xpipe.core.source.DataSourceId;
import io.xpipe.core.source.DataSourceType;
import java.io.InputStream;
public class DataRawImpl extends DataSourceImpl implements DataRaw {
public DataRawImpl(
DataSourceId sourceId,
DataSourceConfig sourceConfig,
io.xpipe.core.source.DataSource<?> internalSource
) {
super(sourceId, sourceConfig, internalSource);
}
@Override
public InputStream open() {
return null;
}
@Override
public byte[] readAll() {
return new byte[0];
}
@Override
public byte[] read(int maxBytes) {
return new byte[0];
}
@Override
public DataSourceType getType() {
return DataSourceType.RAW;
}
@Override
public DataRaw asRaw() {
return this;
}
}

View file

@ -1,164 +0,0 @@
package io.xpipe.api.impl;
import io.xpipe.api.DataSource;
import io.xpipe.api.DataSourceConfig;
import io.xpipe.api.connector.XPipeApiConnection;
import io.xpipe.beacon.exchange.*;
import io.xpipe.core.source.DataSourceId;
import io.xpipe.core.source.DataSourceReference;
import io.xpipe.core.store.DataStore;
import io.xpipe.core.store.StreamDataStore;
import java.io.InputStream;
public abstract class DataSourceImpl implements DataSource {
private final DataSourceId sourceId;
private final DataSourceConfig config;
private final io.xpipe.core.source.DataSource<?> internalSource;
public DataSourceImpl(
DataSourceId sourceId, DataSourceConfig config, io.xpipe.core.source.DataSource<?> internalSource
) {
this.sourceId = sourceId;
this.config = config;
this.internalSource = internalSource;
}
public static DataSource get(DataSourceReference ds) {
return XPipeApiConnection.execute(con -> {
var req = QueryDataSourceExchange.Request.builder().ref(ds).build();
QueryDataSourceExchange.Response res = con.performSimpleExchange(req);
var config = new DataSourceConfig(res.getProvider(), res.getConfig());
return switch (res.getType()) {
case TABLE -> {
yield new DataTableImpl(res.getId(), config, res.getInternalSource());
}
case STRUCTURE -> {
yield new DataStructureImpl(res.getId(), config, res.getInternalSource());
}
case TEXT -> {
yield new DataTextImpl(res.getId(), config, res.getInternalSource());
}
case RAW -> {
yield new DataRawImpl(res.getId(), config, res.getInternalSource());
}
case COLLECTION -> throw new UnsupportedOperationException("Unimplemented case: " + res.getType());
default -> throw new IllegalArgumentException("Unexpected value: " + res.getType());
};
});
}
public static DataSource create(DataSourceId id, io.xpipe.core.source.DataSource<?> source) {
var startReq =
AddSourceExchange.Request.builder().source(source).target(id).build();
var returnedId = XPipeApiConnection.execute(con -> {
AddSourceExchange.Response r = con.performSimpleExchange(startReq);
return r.getId();
});
var ref = DataSourceReference.id(returnedId);
return get(ref);
}
public static DataSource create(DataSourceId id, String type, DataStore store) {
if (store instanceof StreamDataStore s && s.isContentExclusivelyAccessible()) {
store = XPipeApiConnection.execute(con -> {
var internal = con.createInternalStreamStore();
var req = WriteStreamExchange.Request.builder()
.name(internal.getUuid().toString())
.build();
con.performOutputExchange(req, out -> {
try (InputStream inputStream = s.openInput()) {
inputStream.transferTo(out);
}
});
return internal;
});
}
var startReq = ReadExchange.Request.builder()
.provider(type)
.store(store)
.target(id)
.configureAll(false)
.build();
var startRes = XPipeApiConnection.execute(con -> {
ReadExchange.Response r = con.performSimpleExchange(startReq);
return r;
});
var configInstance = startRes.getConfig();
XPipeApiConnection.finishDialog(configInstance);
var ref = id != null ? DataSourceReference.id(id) : DataSourceReference.latest();
return get(ref);
}
public static DataSource create(DataSourceId id, String type, InputStream in) {
var store = XPipeApiConnection.execute(con -> {
var internal = con.createInternalStreamStore();
var req = WriteStreamExchange.Request.builder()
.name(internal.getUuid().toString())
.build();
con.performOutputExchange(req, out -> {
in.transferTo(out);
});
return internal;
});
var startReq = ReadExchange.Request.builder()
.provider(type)
.store(store)
.target(id)
.configureAll(false)
.build();
var startRes = XPipeApiConnection.execute(con -> {
ReadExchange.Response r = con.performSimpleExchange(startReq);
return r;
});
var configInstance = startRes.getConfig();
XPipeApiConnection.finishDialog(configInstance);
var ref = id != null ? DataSourceReference.id(id) : DataSourceReference.latest();
return get(ref);
}
@Override
public void forwardTo(DataSource target) {
XPipeApiConnection.execute(con -> {
var req = ForwardExchange.Request.builder()
.source(DataSourceReference.id(sourceId))
.target(DataSourceReference.id(target.getId()))
.build();
ForwardExchange.Response res = con.performSimpleExchange(req);
});
}
@Override
public void appendTo(DataSource target) {
XPipeApiConnection.execute(con -> {
var req = ForwardExchange.Request.builder()
.source(DataSourceReference.id(sourceId))
.target(DataSourceReference.id(target.getId()))
.append(true)
.build();
ForwardExchange.Response res = con.performSimpleExchange(req);
});
}
public io.xpipe.core.source.DataSource<?> getInternalSource() {
return internalSource;
}
@Override
public DataSourceId getId() {
return sourceId;
}
@Override
public DataSourceConfig getConfig() {
return config;
}
}

View file

@ -1,33 +0,0 @@
package io.xpipe.api.impl;
import io.xpipe.api.DataSourceConfig;
import io.xpipe.api.DataStructure;
import io.xpipe.core.data.node.DataStructureNode;
import io.xpipe.core.source.DataSourceId;
import io.xpipe.core.source.DataSourceType;
public class DataStructureImpl extends DataSourceImpl implements DataStructure {
DataStructureImpl(
DataSourceId sourceId,
DataSourceConfig sourceConfig,
io.xpipe.core.source.DataSource<?> internalSource
) {
super(sourceId, sourceConfig, internalSource);
}
@Override
public DataSourceType getType() {
return DataSourceType.STRUCTURE;
}
@Override
public DataStructure asStructure() {
return this;
}
@Override
public DataStructureNode read() {
return null;
}
}

View file

@ -1,110 +0,0 @@
package io.xpipe.api.impl;
import io.xpipe.api.DataSource;
import io.xpipe.api.DataTable;
import io.xpipe.api.DataTableAccumulator;
import io.xpipe.api.connector.XPipeApiConnection;
import io.xpipe.api.util.TypeDescriptor;
import io.xpipe.beacon.BeaconException;
import io.xpipe.beacon.exchange.ReadExchange;
import io.xpipe.beacon.exchange.WriteStreamExchange;
import io.xpipe.beacon.exchange.cli.StoreAddExchange;
import io.xpipe.beacon.util.QuietDialogHandler;
import io.xpipe.core.data.node.DataStructureNode;
import io.xpipe.core.data.node.DataStructureNodeAcceptor;
import io.xpipe.core.data.node.TupleNode;
import io.xpipe.core.data.type.TupleType;
import io.xpipe.core.data.typed.TypedDataStreamWriter;
import io.xpipe.core.impl.InternalStreamStore;
import io.xpipe.core.source.DataSourceId;
import io.xpipe.core.source.DataSourceReference;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
public class DataTableAccumulatorImpl implements DataTableAccumulator {
private final XPipeApiConnection connection;
private final TupleType type;
private int rows;
private InternalStreamStore store;
private TupleType writtenDescriptor;
private OutputStream bodyOutput;
public DataTableAccumulatorImpl(TupleType type) {
this.type = type;
connection = XPipeApiConnection.open();
store = new InternalStreamStore();
var addReq = StoreAddExchange.Request.builder().storeInput(store).name(store.getUuid().toString()).build();
StoreAddExchange.Response addRes = connection.performSimpleExchange(addReq);
QuietDialogHandler.handle(addRes.getConfig(), connection);
connection.sendRequest(WriteStreamExchange.Request.builder().name(store.getUuid().toString()).build());
bodyOutput = connection.sendBody();
}
@Override
public synchronized DataTable finish(DataSourceId id) {
try {
bodyOutput.close();
} catch (IOException e) {
throw new BeaconException(e);
}
WriteStreamExchange.Response res = connection.receiveResponse();
connection.close();
var req = ReadExchange.Request.builder()
.target(id)
.store(store)
.provider("xpbt")
.configureAll(false)
.build();
ReadExchange.Response response = XPipeApiConnection.execute(con -> {
return con.performSimpleExchange(req);
});
var configInstance = response.getConfig();
XPipeApiConnection.finishDialog(configInstance);
return DataSource.get(DataSourceReference.id(id)).asTable();
}
private void writeDescriptor() {
if (writtenDescriptor != null) {
return;
}
writtenDescriptor = TupleType.tableType(type.getNames());
connection.withOutputStream(out -> {
out.write((TypeDescriptor.create(type.getNames())).getBytes(StandardCharsets.UTF_8));
});
}
@Override
public synchronized void add(DataStructureNode row) {
TupleNode toUse = type.matches(row)
? row.asTuple()
: type.convert(row).orElseThrow().asTuple();
connection.withOutputStream(out -> {
writeDescriptor();
TypedDataStreamWriter.writeStructure(out, toUse, writtenDescriptor);
rows++;
});
}
@Override
public synchronized DataStructureNodeAcceptor<DataStructureNode> acceptor() {
return node -> {
add(node);
return true;
};
}
@Override
public synchronized int getCurrentRows() {
return rows;
}
}

View file

@ -1,128 +0,0 @@
package io.xpipe.api.impl;
import io.xpipe.api.DataSourceConfig;
import io.xpipe.api.DataTable;
import io.xpipe.api.connector.XPipeApiConnection;
import io.xpipe.beacon.BeaconConnection;
import io.xpipe.beacon.BeaconException;
import io.xpipe.beacon.exchange.api.QueryTableDataExchange;
import io.xpipe.core.data.node.ArrayNode;
import io.xpipe.core.data.node.DataStructureNode;
import io.xpipe.core.data.node.TupleNode;
import io.xpipe.core.data.typed.TypedAbstractReader;
import io.xpipe.core.data.typed.TypedDataStreamParser;
import io.xpipe.core.data.typed.TypedDataStructureNodeReader;
import io.xpipe.core.source.DataSourceId;
import io.xpipe.core.source.DataSourceReference;
import io.xpipe.core.source.DataSourceType;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public class DataTableImpl extends DataSourceImpl implements DataTable {
DataTableImpl(
DataSourceId id,
DataSourceConfig sourceConfig,
io.xpipe.core.source.DataSource<?> internalSource
) {
super(id, sourceConfig, internalSource);
}
@Override
public DataTable asTable() {
return this;
}
public Stream<TupleNode> stream() {
var iterator = new TableIterator();
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED), false)
.onClose(iterator::finish);
}
@Override
public DataSourceType getType() {
return DataSourceType.TABLE;
}
@Override
public ArrayNode readAll() {
return read(Integer.MAX_VALUE);
}
@Override
public ArrayNode read(int maxRows) {
List<DataStructureNode> nodes = new ArrayList<>();
XPipeApiConnection.execute(con -> {
var req = QueryTableDataExchange.Request.builder()
.ref(DataSourceReference.id(getId()))
.maxRows(maxRows)
.build();
con.performInputExchange(req, (QueryTableDataExchange.Response res, InputStream in) -> {
var r = new TypedDataStreamParser(res.getDataType());
r.parseStructures(in, TypedDataStructureNodeReader.of(res.getDataType()), nodes::add);
});
});
return ArrayNode.of(nodes);
}
@Override
public Iterator<TupleNode> iterator() {
return new TableIterator();
}
;
private class TableIterator implements Iterator<TupleNode> {
private final BeaconConnection connection;
private final TypedDataStreamParser parser;
private final TypedAbstractReader nodeReader;
private TupleNode node;
{
connection = XPipeApiConnection.open();
var req = QueryTableDataExchange.Request.builder()
.ref(DataSourceReference.id(getId()))
.maxRows(Integer.MAX_VALUE)
.build();
connection.sendRequest(req);
QueryTableDataExchange.Response response = connection.receiveResponse();
nodeReader = TypedDataStructureNodeReader.of(response.getDataType());
parser = new TypedDataStreamParser(response.getDataType());
connection.receiveBody();
}
private void finish() {
connection.close();
}
@Override
public boolean hasNext() {
connection.checkClosed();
try {
node = (TupleNode) parser.parseStructure(connection.getInputStream(), nodeReader);
} catch (IOException e) {
throw new BeaconException(e);
}
if (node == null) {
// finish();
}
return node != null;
}
@Override
public TupleNode next() {
connection.checkClosed();
return node;
}
}
}

View file

@ -1,122 +0,0 @@
package io.xpipe.api.impl;
import io.xpipe.api.DataSourceConfig;
import io.xpipe.api.DataText;
import io.xpipe.api.connector.XPipeApiConnection;
import io.xpipe.beacon.BeaconConnection;
import io.xpipe.beacon.BeaconException;
import io.xpipe.beacon.exchange.api.QueryTextDataExchange;
import io.xpipe.core.source.DataSourceId;
import io.xpipe.core.source.DataSourceReference;
import io.xpipe.core.source.DataSourceType;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.List;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public class DataTextImpl extends DataSourceImpl implements DataText {
DataTextImpl(
DataSourceId sourceId,
DataSourceConfig sourceConfig,
io.xpipe.core.source.DataSource<?> internalSource
) {
super(sourceId, sourceConfig, internalSource);
}
@Override
public DataSourceType getType() {
return DataSourceType.TEXT;
}
@Override
public DataText asText() {
return this;
}
@Override
public List<String> readAllLines() {
return readLines(Integer.MAX_VALUE);
}
@Override
public List<String> readLines(int maxLines) {
try (Stream<String> lines = lines()) {
return lines.limit(maxLines).toList();
}
}
@Override
public Stream<String> lines() {
var iterator = new Iterator<String>() {
private final BeaconConnection connection;
private final BufferedReader reader;
private String nextValue;
{
connection = XPipeApiConnection.open();
var req = QueryTextDataExchange.Request.builder()
.ref(DataSourceReference.id(getId()))
.maxLines(-1)
.build();
connection.sendRequest(req);
connection.receiveResponse();
reader = new BufferedReader(new InputStreamReader(connection.receiveBody(), StandardCharsets.UTF_8));
}
private void close() {
connection.close();
}
@Override
public boolean hasNext() {
connection.checkClosed();
try {
nextValue = reader.readLine();
} catch (IOException e) {
throw new BeaconException(e);
}
return nextValue != null;
}
@Override
public String next() {
return nextValue;
}
};
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED), false)
.onClose(iterator::close);
}
@Override
public String readAll() {
try (Stream<String> lines = lines()) {
return lines.collect(Collectors.joining("\n"));
}
}
@Override
public String read(int maxCharacters) {
StringBuilder builder = new StringBuilder();
lines().takeWhile(s -> {
if (builder.length() > maxCharacters) {
return false;
}
builder.append(s);
return true;
});
return builder.toString();
}
}

View file

@ -1,12 +0,0 @@
package io.xpipe.api.util;
import java.util.List;
import java.util.stream.Collectors;
public class TypeDescriptor {
public static String create(List<String> names) {
return "[" + names.stream().map(n -> n != null ? "\"" + n + "\"" : null).collect(Collectors.joining(","))
+ "]\n";
}
}

View file

@ -1,8 +0,0 @@
module io.xpipe.api {
exports io.xpipe.api;
exports io.xpipe.api.connector;
exports io.xpipe.api.util;
requires transitive io.xpipe.core;
requires io.xpipe.beacon;
}

View file

@ -1,19 +0,0 @@
package io.xpipe.api.test;
import io.xpipe.beacon.BeaconDaemonController;
import io.xpipe.core.util.XPipeDaemonMode;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
public class ApiTest {
@BeforeAll
public static void setup() throws Exception {
BeaconDaemonController.start(XPipeDaemonMode.TRAY);
}
@AfterAll
public static void teardown() throws Exception {
BeaconDaemonController.stop();
}
}

View file

@ -1,29 +0,0 @@
package io.xpipe.api.test;
import io.xpipe.api.DataTableAccumulator;
import io.xpipe.core.data.node.TupleNode;
import io.xpipe.core.data.node.ValueNode;
import io.xpipe.core.data.type.TupleType;
import io.xpipe.core.data.type.ValueType;
import org.junit.jupiter.api.Test;
import java.util.List;
public class DataTableAccumulatorTest extends ApiTest {
@Test
public void test() {
var type = TupleType.of(List.of("col1", "col2"), List.of(ValueType.of(), ValueType.of()));
var acc = DataTableAccumulator.create(type);
var val = type.convert(TupleNode.of(List.of(ValueNode.of("val1"), ValueNode.of("val2"))))
.orElseThrow();
acc.add(val);
var table = acc.finish(":test");
// Assertions.assertEquals(table.getInfo().getDataType(), TupleType.tableType(List.of("col1", "col2")));
// Assertions.assertEquals(table.getInfo().getRowCountIfPresent(), OptionalInt.empty());
// var read = table.read(1).at(0);
// Assertions.assertEquals(val, read);
}
}

View file

@ -1,22 +0,0 @@
package io.xpipe.api.test;
import io.xpipe.api.DataSource;
import io.xpipe.core.source.DataSourceId;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
public class DataTableTest extends ApiTest {
@BeforeAll
public static void setupStorage() throws Exception {
DataSource.create(
DataSourceId.fromString(":usernames"), "csv", DataTableTest.class.getResource("username.csv"));
}
@Test
public void testGet() {
var table = DataSource.getById(":usernames").asTable();
var r = table.read(2);
var a = 0;
}
}

View file

@ -1,9 +0,0 @@
module io.xpipe.api.test {
requires io.xpipe.api;
requires io.xpipe.beacon;
requires org.junit.jupiter.api;
opens io.xpipe.api.test;
exports io.xpipe.api.test;
}

View file

@ -1,6 +0,0 @@
Username;Identifier ;First name;Last name
booker12;9012;Rachel;Booker
grey07;2070;Laura;Grey
johnson81;4081;Craig;Johnson
jenkins46;9346;Mary;Jenkins
smith79;5079;Jamie;Smith
1 Username Identifier First name Last name
2 booker12 9012 Rachel Booker
3 grey07 2070 Laura Grey
4 johnson81 4081 Craig Johnson
5 jenkins46 9346 Mary Jenkins
6 smith79 5079 Jamie Smith

182
app/build.gradle Normal file
View file

@ -0,0 +1,182 @@
plugins {
id 'application'
id 'jvm-test-suite'
id 'java-library'
}
repositories {
mavenCentral()
}
apply from: "$rootDir/gradle/gradle_scripts/java.gradle"
apply from: "$rootDir/gradle/gradle_scripts/javafx.gradle"
apply from: "$rootDir/gradle/gradle_scripts/jna.gradle"
apply from: "$rootDir/gradle/gradle_scripts/lombok.gradle"
configurations {
implementation.extendsFrom(javafx)
api.extendsFrom(jna)
}
dependencies {
api project(':core')
api project(':beacon')
compileOnly 'org.hamcrest:hamcrest:3.0'
compileOnly 'org.junit.jupiter:junit-jupiter-api:5.11.4'
compileOnly 'org.junit.jupiter:junit-jupiter-params:5.11.4'
api 'com.vladsch.flexmark:flexmark:0.64.8'
api 'com.vladsch.flexmark:flexmark-util:0.64.8'
api 'com.vladsch.flexmark:flexmark-util-options:0.64.8'
api 'com.vladsch.flexmark:flexmark-util-data:0.64.8'
api 'com.vladsch.flexmark:flexmark-util-ast:0.64.8'
api 'com.vladsch.flexmark:flexmark-util-builder:0.64.8'
api 'com.vladsch.flexmark:flexmark-util-sequence:0.64.8'
api 'com.vladsch.flexmark:flexmark-util-misc:0.64.8'
api 'com.vladsch.flexmark:flexmark-util-dependency:0.64.8'
api 'com.vladsch.flexmark:flexmark-util-collection:0.64.8'
api 'com.vladsch.flexmark:flexmark-util-format:0.64.8'
api 'com.vladsch.flexmark:flexmark-util-html:0.64.8'
api 'com.vladsch.flexmark:flexmark-util-visitor:0.64.8'
api 'com.vladsch.flexmark:flexmark-ext-tables:0.64.8'
api 'com.vladsch.flexmark:flexmark-ext-gfm-strikethrough:0.64.8'
api 'com.vladsch.flexmark:flexmark-ext-gfm-tasklist:0.64.8'
api 'com.vladsch.flexmark:flexmark-ext-footnotes:0.64.8'
api 'com.vladsch.flexmark:flexmark-ext-definition:0.64.8'
api 'com.vladsch.flexmark:flexmark-ext-anchorlink:0.64.8'
api 'com.vladsch.flexmark:flexmark-ext-yaml-front-matter:0.64.8'
api 'com.vladsch.flexmark:flexmark-ext-toc:0.64.8'
api("com.github.weisj:jsvg:1.7.1")
api files("$rootDir/gradle/gradle_scripts/markdowngenerator-1.3.1.1.jar")
api files("$rootDir/gradle/gradle_scripts/vernacular-1.16.jar")
api 'org.bouncycastle:bcprov-jdk18on:1.80'
api 'info.picocli:picocli:4.7.6'
api ('org.kohsuke:github-api:1.326') {
exclude group: 'org.apache.commons', module: 'commons-lang3'
}
api 'org.apache.commons:commons-lang3:3.17.0'
api 'io.sentry:sentry:7.20.0'
api 'commons-io:commons-io:2.18.0'
api group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: "2.18.2"
api group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: "2.18.2"
api group: 'org.kordamp.ikonli', name: 'ikonli-material2-pack', version: "12.2.0"
api group: 'org.kordamp.ikonli', name: 'ikonli-materialdesign2-pack', version: "12.2.0"
api group: 'org.kordamp.ikonli', name: 'ikonli-javafx', version: "12.2.0"
api group: 'org.kordamp.ikonli', name: 'ikonli-material-pack', version: "12.2.0"
api group: 'org.kordamp.ikonli', name: 'ikonli-feather-pack', version: "12.2.0"
api group: 'org.slf4j', name: 'slf4j-api', version: '2.0.16'
api group: 'org.slf4j', name: 'slf4j-jdk-platform-logging', version: '2.0.16'
api 'io.xpipe:modulefs:0.1.6'
api 'net.synedra:validatorfx:0.4.2'
api files("$rootDir/gradle/gradle_scripts/atlantafx-base-2.0.2.jar")
}
apply from: "$rootDir/gradle/gradle_scripts/local_junit_suite.gradle"
def extensionJarDepList = project.allExtensions.stream().map(p -> p.getTasksByName('jar', true)).toList();
jar {
finalizedBy(extensionJarDepList)
}
application {
mainModule = 'io.xpipe.app'
mainClass = 'io.xpipe.app.Main'
applicationDefaultJvmArgs = jvmRunArgs
}
run {
systemProperty 'io.xpipe.app.useVirtualThreads', 'false'
systemProperty 'io.xpipe.app.mode', 'gui'
systemProperty 'io.xpipe.app.writeLogs', "true"
systemProperty 'io.xpipe.app.writeSysOut', "true"
systemProperty 'io.xpipe.app.developerMode', "true"
systemProperty 'io.xpipe.app.logLevel', "trace"
systemProperty 'io.xpipe.app.fullVersion', rootProject.fullVersion
systemProperty 'io.xpipe.app.staging', isStage
// systemProperty 'io.xpipe.beacon.port', "30000"
// Apply passed xpipe properties
for (final def e in System.getProperties().entrySet()) {
if (e.getKey().toString().contains("xpipe")) {
systemProperty e.getKey().toString(), e.getValue()
}
}
workingDir = rootDir
jvmArgs += ['-XX:+EnableDynamicAgentLoading']
def exts = files(project.allExtensions.stream().map(p -> p.getTasksByName('jar', true)[0].outputs.files.singleFile).toList());
classpath += exts
dependsOn(project.allExtensions.stream().map(p -> p.getTasksByName('jar', true)[0]).toList())
}
task runAttachedDebugger(type: JavaExec) {
workingDir = rootDir
classpath = run.classpath
mainModule = 'io.xpipe.app'
mainClass = 'io.xpipe.app.Main'
modularity.inferModulePath = true
jvmArgs += jvmRunArgs
jvmArgs += List.of(
"-javaagent:${System.getProperty("user.home")}/.attachme/attachme-agent-1.2.9.jar=port:7857,host:localhost".toString(),
"-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=127.0.0.1:0"
)
jvmArgs += ['-XX:+EnableDynamicAgentLoading']
systemProperties run.systemProperties
def exts = files(project.allExtensions.stream().map(p -> p.getTasksByName('jar', true)[0].outputs.files.singleFile).toList());
classpath += exts
dependsOn(project.allExtensions.stream().map(p -> p.getTasksByName('jar', true)[0]).toList())
}
processResources {
doLast {
def cssFiles = fileTree(dir: "$sourceSets.main.output.resourcesDir/io/xpipe/app/resources/style")
cssFiles.include "**/*.css"
cssFiles.each { css ->
logger.info("converting CSS to BSS ${css}");
javaexec {
workingDir = project.projectDir
jvmArgs += "--module-path=${configurations.javafx.asFileTree.asPath},"
jvmArgs += "--add-modules=javafx.graphics"
main = "com.sun.javafx.css.parser.Css2Bin"
args css
}
delete css
}
}
doLast {
def resourcesDir = new File(sourceSets.main.output.resourcesDir, "io/xpipe/app/resources/third-party")
resourcesDir.mkdirs()
copy {
from "$rootDir/dist/licenses"
into resourcesDir
}
}
doLast {
copy {
from file("$rootDir/openapi.yaml")
into file("${sourceSets.main.output.resourcesDir}/io/xpipe/app/resources/misc");
}
}
}
distTar {
enabled = false;
}
distZip {
enabled = false;
}
assembleDist {
enabled = false;
}

View file

@ -0,0 +1,4 @@
open module io.xpipe.app.localTest {
requires org.junit.jupiter.api;
requires io.xpipe.app;
}

View file

@ -0,0 +1,13 @@
package test;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.test.LocalExtensionTest;
public class Test extends LocalExtensionTest {
@org.junit.jupiter.api.Test
public void test() {
System.out.println("a");
System.out.println(DataStorage.get().getStoreEntries());
}
}

View file

@ -0,0 +1,29 @@
package io.xpipe.app;
import io.xpipe.app.core.AppProperties;
import io.xpipe.app.core.mode.OperationMode;
public class Main {
public static void main(String[] args) {
if (args.length == 1 && args[0].equals("version")) {
AppProperties.init(args);
System.out.println(AppProperties.get().getVersion());
return;
}
// Since this is not marked as a console application, it will not print anything when you run it in a console on
// Windows
if (args.length == 1 && args[0].equals("--help")) {
System.out.println(
"""
The daemon executable xpiped does not accept any command-line arguments.
For a reference on how to use xpipe from the command-line, take a look at https://docs.xpipe.io/cli.
""");
return;
}
OperationMode.init(args);
}
}

View file

@ -0,0 +1,26 @@
package io.xpipe.app.beacon;
import io.xpipe.beacon.BeaconClientException;
import lombok.Value;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
@Value
public class AppBeaconCache {
Set<BeaconShellSession> shellSessions = new HashSet<>();
public BeaconShellSession getShellSession(UUID uuid) throws BeaconClientException {
var found = shellSessions.stream()
.filter(beaconShellSession ->
beaconShellSession.getEntry().getUuid().equals(uuid))
.findFirst();
if (found.isEmpty()) {
throw new BeaconClientException("No active shell session known for id " + uuid);
}
return found.get();
}
}

View file

@ -0,0 +1,221 @@
package io.xpipe.app.beacon;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.issue.TrackEvent;
import io.xpipe.app.resources.AppResources;
import io.xpipe.app.util.MarkdownHelper;
import io.xpipe.beacon.BeaconConfig;
import io.xpipe.beacon.BeaconInterface;
import io.xpipe.core.process.OsType;
import io.xpipe.core.util.XPipeInstallation;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpServer;
import lombok.Getter;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
public class AppBeaconServer {
private static AppBeaconServer INSTANCE;
@Getter
private final int port;
@Getter
private final boolean propertyPort;
private boolean running;
private ExecutorService executor;
private HttpServer server;
@Getter
private final Set<BeaconSession> sessions = new HashSet<>();
@Getter
private final AppBeaconCache cache = new AppBeaconCache();
@Getter
private String localAuthSecret;
private String notFoundHtml;
private final Map<String, String> resources = new HashMap<>();
public static void setupPort() {
int port;
boolean propertyPort;
if (System.getProperty(BeaconConfig.BEACON_PORT_PROP) != null) {
port = BeaconConfig.getUsedPort();
propertyPort = true;
} else {
port = XPipeInstallation.getDefaultBeaconPort();
propertyPort = false;
}
INSTANCE = new AppBeaconServer(port, propertyPort);
}
private AppBeaconServer(int port, boolean propertyPort) {
this.port = port;
this.propertyPort = propertyPort;
}
public static void init() {
try {
INSTANCE.initAuthSecret();
INSTANCE.start();
TrackEvent.withInfo("Started http server")
.tag("port", INSTANCE.getPort())
.build()
.handle();
} catch (Exception ex) {
// Not terminal!
// We can still continue without the running server
ErrorEvent.fromThrowable("Unable to start local http server on port " + INSTANCE.getPort(), ex)
.build()
.handle();
}
}
public static void reset() {
if (INSTANCE != null) {
INSTANCE.stop();
INSTANCE.deleteAuthSecret();
INSTANCE = null;
}
}
public void addSession(BeaconSession session) {
this.sessions.add(session);
}
public static AppBeaconServer get() {
return INSTANCE;
}
private void stop() {
if (!running) {
return;
}
running = false;
server.stop(0);
executor.shutdown();
try {
executor.awaitTermination(30, TimeUnit.SECONDS);
} catch (InterruptedException ignored) {}
}
private void initAuthSecret() throws IOException {
var file = XPipeInstallation.getLocalBeaconAuthFile();
var id = UUID.randomUUID().toString();
Files.writeString(file, id);
if (OsType.getLocal() != OsType.WINDOWS) {
Files.setPosixFilePermissions(file, PosixFilePermissions.fromString("rw-rw----"));
}
localAuthSecret = id;
}
private void deleteAuthSecret() {
var file = XPipeInstallation.getLocalBeaconAuthFile();
try {
Files.delete(file);
} catch (IOException ignored) {
}
}
private void start() throws IOException {
executor = Executors.newFixedThreadPool(5, r -> {
Thread t = Executors.defaultThreadFactory().newThread(r);
t.setDaemon(true);
t.setName("http handler");
t.setUncaughtExceptionHandler((t1, e) -> {
ErrorEvent.fromThrowable(e).handle();
});
return t;
});
server = HttpServer.create(
new InetSocketAddress(Inet4Address.getByAddress(new byte[] {0x7f, 0x00, 0x00, 0x01}), port), 10);
BeaconInterface.getAll().forEach(beaconInterface -> {
server.createContext(beaconInterface.getPath(), new BeaconRequestHandler<>(beaconInterface));
});
server.setExecutor(executor);
var resourceMap = Map.of(
"openapi.yaml", "misc/openapi.yaml",
"markdown.css", "misc/github-markdown-dark.css",
"highlight.min.js", "misc/highlight.min.js",
"github-dark.min.css", "misc/github-dark.min.css");
resourceMap.forEach((s, s2) -> {
server.createContext("/" + s, exchange -> {
handleResource(exchange, s2);
});
});
server.createContext("/", exchange -> {
handleCatchAll(exchange);
});
server.start();
running = true;
}
private void handleResource(HttpExchange exchange, String resource) throws IOException {
if (!resources.containsKey(resource)) {
AppResources.with(AppResources.XPIPE_MODULE, resource, file -> {
resources.put(resource, Files.readString(file));
});
}
var body = resources.get(resource).getBytes(StandardCharsets.UTF_8);
exchange.sendResponseHeaders(200, body.length);
try (var out = exchange.getResponseBody()) {
out.write(body);
}
}
private void handleCatchAll(HttpExchange exchange) throws IOException {
if (notFoundHtml == null) {
AppResources.with(AppResources.XPIPE_MODULE, "misc/api.md", file -> {
var md = Files.readString(file);
md = md.replaceAll(
Pattern.quote(
"""
> 400 Response
```json
{
"message": "string"
}
```
"""),
"");
notFoundHtml = MarkdownHelper.toHtml(
md,
head -> {
return head + "\n" + "<link rel=\"stylesheet\" href=\"markdown.css\">"
+ "\n" + "<link rel=\"stylesheet\" href=\"github-dark.min.css\">"
+ "\n" + "<script src=\"highlight.min.js\"></script>"
+ "\n" + "<script>hljs.highlightAll();</script>";
},
s -> {
return "<div style=\"max-width: 800px;margin: auto;\">" + s + "</div>";
},
"standalone");
});
}
var body = notFoundHtml.getBytes(StandardCharsets.UTF_8);
exchange.sendResponseHeaders(200, body.length);
try (var out = exchange.getResponseBody()) {
out.write(body);
}
}
}

View file

@ -0,0 +1,210 @@
package io.xpipe.app.beacon;
import io.xpipe.app.core.mode.OperationMode;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.issue.TrackEvent;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.util.ThreadHelper;
import io.xpipe.beacon.*;
import io.xpipe.core.util.JacksonMapper;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import lombok.SneakyThrows;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
public class BeaconRequestHandler<T> implements HttpHandler {
private final BeaconInterface<T> beaconInterface;
public BeaconRequestHandler(BeaconInterface<T> beaconInterface) {
this.beaconInterface = beaconInterface;
}
@Override
public void handle(HttpExchange exchange) {
if (OperationMode.isInShutdown() && !beaconInterface.acceptInShutdown()) {
writeError(exchange, new BeaconClientErrorResponse("Daemon is currently in shutdown"), 400);
return;
}
if (beaconInterface.requiresCompletedStartup()) {
while (OperationMode.isInStartup()) {
ThreadHelper.sleep(100);
}
}
if (beaconInterface.requiresEnabledApi()
&& !AppPrefs.get().enableHttpApi().get()) {
var ex = new BeaconServerException("HTTP API is not enabled in the settings menu");
writeError(exchange, ex, 403);
return;
}
if (!AppPrefs.get().disableApiAuthentication().get() && beaconInterface.requiresAuthentication()) {
var auth = exchange.getRequestHeaders().getFirst("Authorization");
if (auth == null) {
writeError(exchange, new BeaconClientErrorResponse("Missing Authorization header"), 401);
return;
}
var token = auth.replace("Bearer ", "");
var session = AppBeaconServer.get().getSessions().stream()
.filter(s -> s.getToken().equals(token))
.findFirst()
.orElse(null);
if (session == null) {
writeError(exchange, new BeaconClientErrorResponse("Unknown token"), 403);
return;
}
}
handleAuthenticatedRequest(exchange);
}
private void handleAuthenticatedRequest(HttpExchange exchange) {
T object;
Object response;
try {
if (beaconInterface.readRawRequestBody()) {
object = createDefaultRequest(beaconInterface);
} else {
try (InputStream is = exchange.getRequestBody()) {
var read = is.readAllBytes();
var rawDataRequestClass = beaconInterface.getRequestClass().getDeclaredFields().length == 1
&& beaconInterface
.getRequestClass()
.getDeclaredFields()[0]
.getType()
.equals(byte[].class);
if (!new String(read, StandardCharsets.US_ASCII).trim().startsWith("{") && rawDataRequestClass) {
object = createRawDataRequest(beaconInterface, read);
} else {
var tree = JacksonMapper.getDefault().readTree(read);
TrackEvent.trace("Parsed raw request:\n" + tree.toPrettyString());
var emptyRequestClass = tree.isEmpty()
&& beaconInterface.getRequestClass().getDeclaredFields().length == 0;
object = emptyRequestClass
? createDefaultRequest(beaconInterface)
: JacksonMapper.getDefault().treeToValue(tree, beaconInterface.getRequestClass());
TrackEvent.trace("Parsed request object:\n" + object);
}
}
}
var sync = beaconInterface.getSynchronizationObject();
if (sync != null) {
synchronized (sync) {
response = beaconInterface.handle(exchange, object);
}
} else {
response = beaconInterface.handle(exchange, object);
}
} catch (BeaconClientException clientException) {
ErrorEvent.fromThrowable(clientException).omit().expected().handle();
writeError(exchange, new BeaconClientErrorResponse(clientException.getMessage()), 400);
return;
} catch (BeaconServerException serverException) {
var cause = serverException.getCause() != null ? serverException.getCause() : serverException;
ErrorEvent.fromThrowable(cause).omit().handle();
writeError(exchange, new BeaconServerErrorResponse(cause), 500);
return;
} catch (IOException ex) {
// Handle serialization errors as normal exceptions and other IO exceptions as assuming that the connection
// is broken
if (!ex.getClass().getName().contains("jackson")) {
ErrorEvent.fromThrowable(ex).omit().expected().handle();
} else {
ErrorEvent.fromThrowable(ex).omit().expected().handle();
// Make deserialization error message more readable
var message = ex.getMessage()
.replace("$RequestBuilder", "")
.replace("Exchange$Request", "Request")
.replace("at [Source: UNKNOWN; byte offset: #UNKNOWN]", "")
.replaceAll("(\\w+) is marked non-null but is null", "field $1 is missing from object")
.trim();
writeError(exchange, new BeaconClientErrorResponse(message), 400);
}
return;
} catch (Throwable other) {
ErrorEvent.fromThrowable(other).omit().expected().handle();
writeError(exchange, new BeaconServerErrorResponse(other), 500);
return;
}
try {
var emptyResponseClass = beaconInterface.getResponseClass().getDeclaredFields().length == 0;
if (!emptyResponseClass && response != null) {
TrackEvent.trace("Sending response:\n" + response);
TrackEvent.trace("Sending raw response:\n"
+ JacksonMapper.getCensored().valueToTree(response).toPrettyString());
var bytes = JacksonMapper.getDefault()
.valueToTree(response)
.toPrettyString()
.getBytes(StandardCharsets.UTF_8);
exchange.sendResponseHeaders(200, bytes.length);
try (OutputStream os = exchange.getResponseBody()) {
os.write(bytes);
}
} else {
exchange.sendResponseHeaders(200, -1);
}
} catch (IOException ioException) {
// The exchange implementation might have already sent a response manually
if (!"headers already sent".equals(ioException.getMessage())) {
ErrorEvent.fromThrowable(ioException).omit().expected().handle();
}
} catch (Throwable other) {
ErrorEvent.fromThrowable(other).handle();
writeError(exchange, new BeaconServerErrorResponse(other), 500);
}
}
private void writeError(HttpExchange exchange, Object errorMessage, int code) {
try {
var bytes =
JacksonMapper.getDefault().writeValueAsString(errorMessage).getBytes(StandardCharsets.UTF_8);
exchange.sendResponseHeaders(code, bytes.length);
try (OutputStream os = exchange.getResponseBody()) {
os.write(bytes);
}
} catch (IOException ex) {
ErrorEvent.fromThrowable(ex).omit().expected().handle();
}
}
@SneakyThrows
@SuppressWarnings("unchecked")
private <REQ> REQ createDefaultRequest(BeaconInterface<?> beaconInterface) {
var c = beaconInterface.getRequestClass().getDeclaredMethod("builder");
c.setAccessible(true);
var b = c.invoke(null);
var m = b.getClass().getDeclaredMethod("build");
m.setAccessible(true);
return (REQ) beaconInterface.getRequestClass().cast(m.invoke(b));
}
@SneakyThrows
@SuppressWarnings("unchecked")
private <REQ> REQ createRawDataRequest(BeaconInterface<?> beaconInterface, byte[] s) {
var c = beaconInterface.getRequestClass().getDeclaredMethod("builder");
c.setAccessible(true);
var b = c.invoke(null);
var setMethod = Arrays.stream(b.getClass().getDeclaredMethods())
.filter(method -> method.getParameterCount() == 1
&& method.getParameters()[0].getType().equals(byte[].class))
.findFirst()
.orElseThrow();
setMethod.invoke(b, (Object) s);
var m = b.getClass().getDeclaredMethod("build");
m.setAccessible(true);
return (REQ) beaconInterface.getRequestClass().cast(m.invoke(b));
}
}

View file

@ -0,0 +1,12 @@
package io.xpipe.app.beacon;
import io.xpipe.beacon.BeaconClientInformation;
import lombok.Value;
@Value
public class BeaconSession {
BeaconClientInformation clientInformation;
String token;
}

View file

@ -0,0 +1,13 @@
package io.xpipe.app.beacon;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.core.process.ShellControl;
import lombok.Value;
@Value
public class BeaconShellSession {
DataStoreEntry entry;
ShellControl control;
}

View file

@ -0,0 +1,82 @@
package io.xpipe.app.beacon;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.util.ShellTemp;
import io.xpipe.beacon.BeaconClientException;
import org.apache.commons.io.FileUtils;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class BlobManager {
private static final Path TEMP = ShellTemp.getLocalTempDataDirectory("blob");
private static BlobManager INSTANCE;
private final Map<UUID, byte[]> memoryBlobs = new ConcurrentHashMap<>();
private final Map<UUID, Path> fileBlobs = new ConcurrentHashMap<>();
public static BlobManager get() {
return INSTANCE;
}
public static void init() {
INSTANCE = new BlobManager();
try {
FileUtils.forceMkdir(TEMP.toFile());
try {
// Remove old files in dir
FileUtils.cleanDirectory(TEMP.toFile());
} catch (IOException ignored) {
}
} catch (IOException e) {
ErrorEvent.fromThrowable(e).handle();
}
}
public static void reset() {
try {
FileUtils.cleanDirectory(TEMP.toFile());
} catch (IOException ignored) {
}
INSTANCE = null;
}
public Path newBlobFile() throws IOException {
var file = TEMP.resolve(UUID.randomUUID().toString());
FileUtils.forceMkdir(file.getParent().toFile());
return file;
}
public void store(UUID uuid, byte[] blob) {
memoryBlobs.put(uuid, blob);
}
public void store(UUID uuid, InputStream blob) throws IOException {
var file = TEMP.resolve(uuid.toString());
try (var fileOut = Files.newOutputStream(file)) {
blob.transferTo(fileOut);
}
fileBlobs.put(uuid, file);
}
public InputStream getBlob(UUID uuid) throws Exception {
var memory = memoryBlobs.get(uuid);
if (memory != null) {
return new ByteArrayInputStream(memory);
}
var found = fileBlobs.get(uuid);
if (found == null) {
throw new BeaconClientException("No saved data known for id " + uuid);
}
return Files.newInputStream(found);
}
}

View file

@ -0,0 +1,80 @@
package io.xpipe.app.beacon.impl;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.prefs.ExternalApplicationType;
import io.xpipe.app.terminal.TerminalView;
import io.xpipe.app.util.AskpassAlert;
import io.xpipe.app.util.SecretManager;
import io.xpipe.app.util.SecretQueryState;
import io.xpipe.beacon.BeaconClientException;
import io.xpipe.beacon.api.AskpassExchange;
import io.xpipe.core.process.OsType;
import com.sun.net.httpserver.HttpExchange;
public class AskpassExchangeImpl extends AskpassExchange {
@Override
public boolean requiresCompletedStartup() {
return false;
}
@Override
public Object handle(HttpExchange exchange, Request msg) throws BeaconClientException {
if (msg.getRequest() == null) {
var r = AskpassAlert.queryRaw(msg.getPrompt(), null);
return Response.builder().value(r.getSecret()).build();
}
var found = msg.getSecretId() != null
? SecretManager.getProgress(msg.getRequest(), msg.getSecretId())
: SecretManager.getProgress(msg.getRequest());
if (found.isEmpty()) {
throw new BeaconClientException("Unknown askpass request");
}
var p = found.get();
var secret = p.process(msg.getPrompt());
if (p.getState() != SecretQueryState.NORMAL) {
throw new BeaconClientException(SecretQueryState.toErrorMessage(p.getState()));
}
focusTerminalIfNeeded(msg.getPid());
return Response.builder().value(secret.inPlace()).build();
}
private void focusTerminalIfNeeded(long pid) {
if (TerminalView.get() == null) {
return;
}
var found = TerminalView.get().findSession(pid);
if (found.isEmpty()) {
return;
}
var term = TerminalView.get().getTerminalInstances().stream()
.filter(instance -> instance.equals(found.get().getTerminal()))
.findFirst();
if (term.isEmpty()) {
return;
}
var control = term.get().controllable();
if (control.isPresent()) {
control.get().focus();
} else {
if (OsType.getLocal() == OsType.MACOS) {
// Just focus the app, this is correct most of the time
var terminalType = AppPrefs.get().terminalType().getValue();
if (terminalType instanceof ExternalApplicationType.MacApplication m) {
m.focus();
}
}
}
}
@Override
public boolean requiresEnabledApi() {
return false;
}
}

View file

@ -0,0 +1,35 @@
package io.xpipe.app.beacon.impl;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreCategory;
import io.xpipe.beacon.BeaconClientException;
import io.xpipe.beacon.api.CategoryAddExchange;
import com.sun.net.httpserver.HttpExchange;
public class CategoryAddExchangeImpl extends CategoryAddExchange {
@Override
public Object handle(HttpExchange exchange, Request msg) throws Throwable {
if (DataStorage.get().getStoreCategoryIfPresent(msg.getParent()).isEmpty()) {
throw new BeaconClientException("Parent category with id " + msg.getParent() + " does not exist");
}
var found = DataStorage.get().getStoreCategories().stream()
.filter(dataStoreCategory -> msg.getParent().equals(dataStoreCategory.getParentCategory())
&& msg.getName().equals(dataStoreCategory.getName()))
.findAny();
if (found.isPresent()) {
return Response.builder().category(found.get().getUuid()).build();
}
var cat = DataStoreCategory.createNew(msg.getParent(), msg.getName());
DataStorage.get().addStoreCategory(cat);
return Response.builder().category(cat.getUuid()).build();
}
@Override
public Object getSynchronizationObject() {
return DataStorage.get();
}
}

View file

@ -0,0 +1,67 @@
package io.xpipe.app.beacon.impl;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.beacon.BeaconClientException;
import io.xpipe.beacon.api.ConnectionAddExchange;
import io.xpipe.core.util.ValidationException;
import com.sun.net.httpserver.HttpExchange;
public class ConnectionAddExchangeImpl extends ConnectionAddExchange {
@Override
public Object handle(HttpExchange exchange, Request msg) throws Throwable {
var found = DataStorage.get().getStoreEntryIfPresent(msg.getData(), false);
if (found.isPresent()) {
return Response.builder().connection(found.get().getUuid()).build();
}
if (msg.getCategory() != null
&& DataStorage.get()
.getStoreCategoryIfPresent(msg.getCategory())
.isEmpty()) {
throw new BeaconClientException("Category with id " + msg.getCategory() + " does not exist");
}
var entry = DataStoreEntry.createNew(msg.getName(), msg.getData());
if (msg.getCategory() != null) {
entry.setCategoryUuid(msg.getCategory());
}
try {
DataStorage.get().addStoreEntryInProgress(entry);
if (msg.getValidate()) {
entry.validateOrThrow();
}
} catch (Throwable ex) {
if (ex instanceof ValidationException) {
ErrorEvent.expected(ex);
} else if (ex instanceof StackOverflowError) {
// Cycles in connection graphs can fail hard but are expected
ErrorEvent.expected(ex);
}
throw ex;
} finally {
DataStorage.get().removeStoreEntryInProgress(entry);
}
DataStorage.get().addStoreEntryIfNotPresent(entry);
// Explicitly assign category
if (msg.getCategory() != null) {
DataStorage.get()
.moveEntryToCategory(
entry,
DataStorage.get()
.getStoreCategoryIfPresent(msg.getCategory())
.orElseThrow());
}
return Response.builder().connection(entry.getUuid()).build();
}
@Override
public Object getSynchronizationObject() {
return DataStorage.get();
}
}

View file

@ -0,0 +1,32 @@
package io.xpipe.app.beacon.impl;
import io.xpipe.app.browser.BrowserFullSessionModel;
import io.xpipe.app.core.AppLayoutModel;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.beacon.BeaconClientException;
import io.xpipe.beacon.api.ConnectionBrowseExchange;
import io.xpipe.core.store.FileSystemStore;
import com.sun.net.httpserver.HttpExchange;
public class ConnectionBrowseExchangeImpl extends ConnectionBrowseExchange {
@Override
public Object handle(HttpExchange exchange, Request msg) throws Exception {
var e = DataStorage.get()
.getStoreEntryIfPresent(msg.getConnection())
.orElseThrow(() -> new BeaconClientException("Unknown connection: " + msg.getConnection()));
if (!(e.getStore() instanceof FileSystemStore)) {
throw new BeaconClientException("Not a file system connection");
}
BrowserFullSessionModel.DEFAULT.openFileSystemSync(
e.ref(), msg.getDirectory() != null ? ignored -> msg.getDirectory() : null, null, true);
AppLayoutModel.get().selectBrowser();
return Response.builder().build();
}
@Override
public Object getSynchronizationObject() {
return DataStorage.get();
}
}

View file

@ -0,0 +1,63 @@
package io.xpipe.app.beacon.impl;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.beacon.BeaconClientException;
import io.xpipe.beacon.api.ConnectionInfoExchange;
import io.xpipe.core.store.StorePath;
import com.sun.net.httpserver.HttpExchange;
import org.apache.commons.lang3.ClassUtils;
import java.util.ArrayList;
import java.util.UUID;
import java.util.stream.Collectors;
public class ConnectionInfoExchangeImpl extends ConnectionInfoExchange {
@Override
public Object handle(HttpExchange exchange, Request msg) throws BeaconClientException {
var list = new ArrayList<InfoResponse>();
for (UUID uuid : msg.getConnections()) {
var e = DataStorage.get()
.getStoreEntryIfPresent(uuid)
.orElseThrow(() -> new BeaconClientException("Unknown connection: " + uuid));
var names = DataStorage.get()
.getStorePath(DataStorage.get()
.getStoreCategoryIfPresent(e.getCategoryUuid())
.orElseThrow())
.getNames();
var cat = new StorePath(names.subList(1, names.size()));
var cache = e.getStoreCache().entrySet().stream()
.filter(stringObjectEntry -> {
return stringObjectEntry.getValue() != null
&& (ClassUtils.isPrimitiveOrWrapper(
stringObjectEntry.getValue().getClass())
|| stringObjectEntry.getValue() instanceof String);
})
.collect(Collectors.toMap(
stringObjectEntry -> stringObjectEntry.getKey(),
stringObjectEntry -> stringObjectEntry.getValue()));
var apply = InfoResponse.builder()
.lastModified(e.getLastModified())
.lastUsed(e.getLastUsed())
.connection(e.getUuid())
.category(cat)
.name(DataStorage.get().getStorePath(e))
.rawData(e.getStore())
.usageCategory(e.getProvider().getUsageCategory())
.type(e.getProvider().getId())
.state(e.getStorePersistentState() != null ? e.getStorePersistentState() : new Object())
.cache(cache)
.build();
list.add(apply);
}
return Response.builder().infos(list).build();
}
@Override
public Object getSynchronizationObject() {
return DataStorage.get();
}
}

View file

@ -0,0 +1,28 @@
package io.xpipe.app.beacon.impl;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStorageQuery;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.beacon.api.ConnectionQueryExchange;
import com.sun.net.httpserver.HttpExchange;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
public class ConnectionQueryExchangeImpl extends ConnectionQueryExchange {
@Override
public Object handle(HttpExchange exchange, Request msg) {
var found = DataStorageQuery.query(msg.getCategoryFilter(), msg.getConnectionFilter(), msg.getTypeFilter());
return Response.builder()
.found(found.stream().map(entry -> entry.getUuid()).toList())
.build();
}
@Override
public Object getSynchronizationObject() {
return DataStorage.get();
}
}

View file

@ -0,0 +1,29 @@
package io.xpipe.app.beacon.impl;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.util.FixedHierarchyStore;
import io.xpipe.beacon.BeaconClientException;
import io.xpipe.beacon.api.ConnectionRefreshExchange;
import com.sun.net.httpserver.HttpExchange;
public class ConnectionRefreshExchangeImpl extends ConnectionRefreshExchange {
@Override
public Object handle(HttpExchange exchange, Request msg) throws Throwable {
var e = DataStorage.get()
.getStoreEntryIfPresent(msg.getConnection())
.orElseThrow(() -> new BeaconClientException("Unknown connection: " + msg.getConnection()));
if (e.getStore() instanceof FixedHierarchyStore) {
DataStorage.get().refreshChildren(e, true);
} else {
e.validateOrThrow();
}
return Response.builder().build();
}
@Override
public Object getSynchronizationObject() {
return DataStorage.get();
}
}

View file

@ -0,0 +1,32 @@
package io.xpipe.app.beacon.impl;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.beacon.BeaconClientException;
import io.xpipe.beacon.api.ConnectionRemoveExchange;
import com.sun.net.httpserver.HttpExchange;
import java.util.ArrayList;
import java.util.UUID;
public class ConnectionRemoveExchangeImpl extends ConnectionRemoveExchange {
@Override
public Object handle(HttpExchange exchange, Request msg) throws BeaconClientException {
var entries = new ArrayList<DataStoreEntry>();
for (UUID uuid : msg.getConnections()) {
var e = DataStorage.get()
.getStoreEntryIfPresent(uuid)
.orElseThrow(() -> new BeaconClientException("Unknown connection: " + uuid));
entries.add(e);
}
DataStorage.get().deleteWithChildren(entries.toArray(DataStoreEntry[]::new));
return Response.builder().build();
}
@Override
public Object getSynchronizationObject() {
return DataStorage.get();
}
}

View file

@ -0,0 +1,30 @@
package io.xpipe.app.beacon.impl;
import io.xpipe.app.ext.ShellStore;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.terminal.TerminalLauncher;
import io.xpipe.beacon.BeaconClientException;
import io.xpipe.beacon.api.ConnectionTerminalExchange;
import com.sun.net.httpserver.HttpExchange;
public class ConnectionTerminalExchangeImpl extends ConnectionTerminalExchange {
@Override
public Object handle(HttpExchange exchange, Request msg) throws Exception {
var e = DataStorage.get()
.getStoreEntryIfPresent(msg.getConnection())
.orElseThrow(() -> new BeaconClientException("Unknown connection: " + msg.getConnection()));
if (!(e.getStore() instanceof ShellStore shellStore)) {
throw new BeaconClientException("Not a shell connection");
}
var sc = shellStore.getOrStartSession();
TerminalLauncher.open(e, e.getName(), msg.getDirectory(), sc);
return Response.builder().build();
}
@Override
public Object getSynchronizationObject() {
return DataStorage.get();
}
}

View file

@ -0,0 +1,32 @@
package io.xpipe.app.beacon.impl;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.beacon.BeaconClientException;
import io.xpipe.beacon.api.ConnectionToggleExchange;
import io.xpipe.core.store.SingletonSessionStore;
import com.sun.net.httpserver.HttpExchange;
public class ConnectionToggleExchangeImpl extends ConnectionToggleExchange {
@Override
public Object handle(HttpExchange exchange, Request msg) throws Exception {
var e = DataStorage.get()
.getStoreEntryIfPresent(msg.getConnection())
.orElseThrow(() -> new BeaconClientException("Unknown connection: " + msg.getConnection()));
if (!(e.getStore() instanceof SingletonSessionStore<?> singletonSessionStore)) {
throw new BeaconClientException("Not a toggleable connection");
}
if (msg.getState()) {
singletonSessionStore.startSessionIfNeeded();
} else {
singletonSessionStore.stopSessionIfNeeded();
}
return Response.builder().build();
}
@Override
public Object getSynchronizationObject() {
return DataStorage.get();
}
}

View file

@ -0,0 +1,25 @@
package io.xpipe.app.beacon.impl;
import io.xpipe.app.core.mode.OperationMode;
import io.xpipe.app.core.window.AppMainWindow;
import io.xpipe.beacon.api.DaemonFocusExchange;
import com.sun.net.httpserver.HttpExchange;
public class DaemonFocusExchangeImpl extends DaemonFocusExchange {
@Override
public Object handle(HttpExchange exchange, Request msg) {
OperationMode.switchUp(OperationMode.GUI);
var w = AppMainWindow.getInstance();
if (w != null) {
w.focus();
}
return Response.builder().build();
}
@Override
public boolean requiresEnabledApi() {
return false;
}
}

View file

@ -0,0 +1,33 @@
package io.xpipe.app.beacon.impl;
import io.xpipe.app.core.mode.OperationMode;
import io.xpipe.beacon.BeaconClientException;
import io.xpipe.beacon.api.DaemonModeExchange;
import com.sun.net.httpserver.HttpExchange;
public class DaemonModeExchangeImpl extends DaemonModeExchange {
@Override
public Object handle(HttpExchange exchange, Request msg) throws BeaconClientException {
var mode = OperationMode.map(msg.getMode());
if (!mode.isSupported()) {
throw new BeaconClientException("Unsupported mode: " + msg.getMode().getDisplayName() + ". Supported: "
+ String.join(
", ",
OperationMode.getAll().stream()
.filter(OperationMode::isSupported)
.map(OperationMode::getId)
.toList()));
}
OperationMode.switchToSyncIfPossible(mode);
return DaemonModeExchange.Response.builder()
.usedMode(OperationMode.map(OperationMode.get()))
.build();
}
@Override
public boolean requiresEnabledApi() {
return false;
}
}

View file

@ -0,0 +1,49 @@
package io.xpipe.app.beacon.impl;
import io.xpipe.app.core.AppOpenArguments;
import io.xpipe.app.core.mode.OperationMode;
import io.xpipe.app.util.PlatformInit;
import io.xpipe.beacon.BeaconServerException;
import io.xpipe.beacon.api.DaemonOpenExchange;
import io.xpipe.core.process.OsType;
import com.sun.net.httpserver.HttpExchange;
public class DaemonOpenExchangeImpl extends DaemonOpenExchange {
private int openCounter = 0;
@Override
public Object handle(HttpExchange exchange, Request msg) throws BeaconServerException {
if (msg.getArguments().isEmpty()) {
try {
// At this point we are already loading this on another thread
// so this call will only perform the waiting
PlatformInit.init(true);
} catch (Throwable t) {
throw new BeaconServerException(t);
}
// The open command is used as a default opener on Linux
// We don't want to overwrite the default startup mode
if (OsType.getLocal() == OsType.LINUX && openCounter++ == 0) {
return Response.builder().build();
}
OperationMode.switchToAsync(OperationMode.GUI);
} else {
AppOpenArguments.handle(msg.getArguments());
}
return Response.builder().build();
}
@Override
public boolean requiresEnabledApi() {
return false;
}
@Override
public boolean requiresCompletedStartup() {
return false;
}
}

View file

@ -0,0 +1,31 @@
package io.xpipe.app.beacon.impl;
import io.xpipe.app.core.mode.OperationMode;
import io.xpipe.beacon.api.DaemonStatusExchange;
import com.sun.net.httpserver.HttpExchange;
public class DaemonStatusExchangeImpl extends DaemonStatusExchange {
@Override
public boolean requiresCompletedStartup() {
return false;
}
@Override
public Object handle(HttpExchange exchange, Request body) {
String mode;
if (OperationMode.get() == null) {
mode = "none";
} else {
mode = OperationMode.get().getId();
}
return Response.builder().mode(mode).build();
}
@Override
public boolean requiresEnabledApi() {
return false;
}
}

View file

@ -0,0 +1,29 @@
package io.xpipe.app.beacon.impl;
import io.xpipe.app.core.mode.OperationMode;
import io.xpipe.app.util.ThreadHelper;
import io.xpipe.beacon.api.DaemonStopExchange;
import com.sun.net.httpserver.HttpExchange;
public class DaemonStopExchangeImpl extends DaemonStopExchange {
@Override
public boolean requiresCompletedStartup() {
return false;
}
@Override
public Object handle(HttpExchange exchange, Request msg) {
ThreadHelper.runAsync(() -> {
ThreadHelper.sleep(1000);
OperationMode.close();
});
return Response.builder().success(true).build();
}
@Override
public boolean requiresEnabledApi() {
return false;
}
}

View file

@ -0,0 +1,39 @@
package io.xpipe.app.beacon.impl;
import io.xpipe.app.core.AppProperties;
import io.xpipe.app.core.AppVersion;
import io.xpipe.app.util.LicenseProvider;
import io.xpipe.beacon.api.DaemonVersionExchange;
import com.sun.net.httpserver.HttpExchange;
public class DaemonVersionExchangeImpl extends DaemonVersionExchange {
@Override
public boolean requiresCompletedStartup() {
return false;
}
@Override
public Object handle(HttpExchange exchange, Request msg) {
var jvmVersion = System.getProperty("java.vm.vendor") + " "
+ System.getProperty("java.vm.name") + " ("
+ System.getProperty("java.vm.version") + ")";
var version = AppProperties.get().getVersion();
var pro = LicenseProvider.get().hasPaidLicense();
return Response.builder()
.version(version)
.canonicalVersion(AppVersion.parse(version)
.map(appVersion -> appVersion.toString())
.orElse("?"))
.buildVersion(AppProperties.get().getBuild())
.jvmVersion(jvmVersion)
.pro(pro)
.build();
}
@Override
public boolean requiresEnabledApi() {
return false;
}
}

View file

@ -0,0 +1,26 @@
package io.xpipe.app.beacon.impl;
import io.xpipe.app.beacon.BlobManager;
import io.xpipe.beacon.api.FsBlobExchange;
import com.sun.net.httpserver.HttpExchange;
import lombok.SneakyThrows;
import java.util.UUID;
public class FsBlobExchangeImpl extends FsBlobExchange {
@Override
@SneakyThrows
public Object handle(HttpExchange exchange, Request msg) {
var id = UUID.randomUUID();
var size = exchange.getRequestBody().available();
if (size > 100_000_000) {
BlobManager.get().store(id, exchange.getRequestBody());
} else {
BlobManager.get().store(id, exchange.getRequestBody().readAllBytes());
}
return Response.builder().blob(id).build();
}
}

View file

@ -0,0 +1,60 @@
package io.xpipe.app.beacon.impl;
import io.xpipe.app.beacon.AppBeaconServer;
import io.xpipe.app.beacon.BlobManager;
import io.xpipe.app.ext.ConnectionFileSystem;
import io.xpipe.app.util.FixedSizeInputStream;
import io.xpipe.beacon.BeaconClientException;
import io.xpipe.beacon.api.FsReadExchange;
import com.sun.net.httpserver.HttpExchange;
import lombok.SneakyThrows;
import java.io.BufferedInputStream;
import java.io.OutputStream;
import java.nio.file.Files;
public class FsReadExchangeImpl extends FsReadExchange {
@Override
@SneakyThrows
public Object handle(HttpExchange exchange, Request msg) {
var shell = AppBeaconServer.get().getCache().getShellSession(msg.getConnection());
var fs = new ConnectionFileSystem(shell.getControl());
if (!fs.fileExists(msg.getPath().toString())) {
throw new BeaconClientException("File does not exist");
}
var size = fs.getFileSize(msg.getPath().toString());
if (size > 100_000_000) {
var file = BlobManager.get().newBlobFile();
try (var in = fs.openInput(msg.getPath().toString())) {
var fixedIn = new FixedSizeInputStream(new BufferedInputStream(in), size);
try (var fileOut =
Files.newOutputStream(file.resolve(msg.getPath().getFileName()))) {
fixedIn.transferTo(fileOut);
}
in.transferTo(OutputStream.nullOutputStream());
}
exchange.sendResponseHeaders(200, size);
try (var fileIn = Files.newInputStream(file);
var out = exchange.getResponseBody()) {
fileIn.transferTo(out);
}
} else {
byte[] bytes;
try (var in = fs.openInput(msg.getPath().toString())) {
var fixedIn = new FixedSizeInputStream(new BufferedInputStream(in), size);
bytes = fixedIn.readAllBytes();
in.transferTo(OutputStream.nullOutputStream());
}
exchange.sendResponseHeaders(200, bytes.length);
try (var out = exchange.getResponseBody()) {
out.write(bytes);
}
}
return Response.builder().build();
}
}

View file

@ -0,0 +1,29 @@
package io.xpipe.app.beacon.impl;
import io.xpipe.app.beacon.AppBeaconServer;
import io.xpipe.app.beacon.BlobManager;
import io.xpipe.app.util.ScriptHelper;
import io.xpipe.beacon.api.FsScriptExchange;
import com.sun.net.httpserver.HttpExchange;
import lombok.SneakyThrows;
import java.nio.charset.StandardCharsets;
public class FsScriptExchangeImpl extends FsScriptExchange {
@Override
@SneakyThrows
public Object handle(HttpExchange exchange, Request msg) {
var shell = AppBeaconServer.get().getCache().getShellSession(msg.getConnection());
String data;
try (var in = BlobManager.get().getBlob(msg.getBlob())) {
data = new String(in.readAllBytes(), StandardCharsets.UTF_8);
}
data = shell.getControl().getShellDialect().prepareScriptContent(data);
var file = ScriptHelper.getExecScriptFile(shell.getControl());
shell.getControl().view().writeScriptFile(file, data);
file = ScriptHelper.fixScriptPermissions(shell.getControl(), file);
return Response.builder().path(file).build();
}
}

View file

@ -0,0 +1,24 @@
package io.xpipe.app.beacon.impl;
import io.xpipe.app.beacon.AppBeaconServer;
import io.xpipe.app.beacon.BlobManager;
import io.xpipe.app.ext.ConnectionFileSystem;
import io.xpipe.beacon.api.FsWriteExchange;
import com.sun.net.httpserver.HttpExchange;
import lombok.SneakyThrows;
public class FsWriteExchangeImpl extends FsWriteExchange {
@Override
@SneakyThrows
public Object handle(HttpExchange exchange, Request msg) {
var shell = AppBeaconServer.get().getCache().getShellSession(msg.getConnection());
var fs = new ConnectionFileSystem(shell.getControl());
try (var in = BlobManager.get().getBlob(msg.getBlob());
var os = fs.openOutput(msg.getPath().toString(), in.available())) {
in.transferTo(os);
}
return Response.builder().build();
}
}

View file

@ -0,0 +1,50 @@
package io.xpipe.app.beacon.impl;
import io.xpipe.app.beacon.AppBeaconServer;
import io.xpipe.app.beacon.BeaconSession;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.beacon.BeaconAuthMethod;
import io.xpipe.beacon.BeaconClientException;
import io.xpipe.beacon.api.HandshakeExchange;
import com.sun.net.httpserver.HttpExchange;
import java.util.UUID;
public class HandshakeExchangeImpl extends HandshakeExchange {
@Override
public boolean requiresCompletedStartup() {
return false;
}
@Override
public Object handle(HttpExchange exchange, Request body) throws BeaconClientException {
if (!checkAuth(body.getAuth())) {
throw new BeaconClientException("Authentication failed");
}
var session = new BeaconSession(body.getClient(), UUID.randomUUID().toString());
AppBeaconServer.get().addSession(session);
return Response.builder().sessionToken(session.getToken()).build();
}
private boolean checkAuth(BeaconAuthMethod authMethod) {
if (authMethod instanceof BeaconAuthMethod.Local local) {
var c = local.getAuthFileContent().trim();
return AppBeaconServer.get().getLocalAuthSecret().equals(c);
}
if (authMethod instanceof BeaconAuthMethod.ApiKey key) {
var c = key.getKey().trim();
return AppPrefs.get().apiKey().get().equals(c);
}
return false;
}
@Override
public boolean requiresEnabledApi() {
return false;
}
}

View file

@ -0,0 +1,33 @@
package io.xpipe.app.beacon.impl;
import io.xpipe.app.beacon.AppBeaconServer;
import io.xpipe.beacon.api.ShellExecExchange;
import com.sun.net.httpserver.HttpExchange;
import lombok.SneakyThrows;
import java.util.concurrent.atomic.AtomicReference;
public class ShellExecExchangeImpl extends ShellExecExchange {
@Override
@SneakyThrows
public Object handle(HttpExchange exchange, Request msg) {
var existing = AppBeaconServer.get().getCache().getShellSession(msg.getConnection());
AtomicReference<String> out = new AtomicReference<>();
AtomicReference<String> err = new AtomicReference<>();
long exitCode;
try (var command = existing.getControl().command(msg.getCommand()).start()) {
var r = command.readStdoutAndStderr();
out.set(r[0]);
err.set(r[1]);
command.close();
exitCode = command.getExitCode();
}
return Response.builder()
.stdout(out.get())
.stderr(err.get())
.exitCode(exitCode)
.build();
}
}

View file

@ -0,0 +1,51 @@
package io.xpipe.app.beacon.impl;
import io.xpipe.app.beacon.AppBeaconServer;
import io.xpipe.app.beacon.BeaconShellSession;
import io.xpipe.app.ext.ShellStore;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.beacon.BeaconClientException;
import io.xpipe.beacon.api.ShellStartExchange;
import com.sun.net.httpserver.HttpExchange;
import lombok.SneakyThrows;
public class ShellStartExchangeImpl extends ShellStartExchange {
@Override
@SneakyThrows
public Object handle(HttpExchange exchange, Request msg) {
var e = DataStorage.get()
.getStoreEntryIfPresent(msg.getConnection())
.orElseThrow(() -> new BeaconClientException("Unknown connection"));
if (!(e.getStore() instanceof ShellStore s)) {
throw new BeaconClientException("Not a shell connection");
}
var existing = AppBeaconServer.get().getCache().getShellSessions().stream()
.filter(beaconShellSession -> beaconShellSession.getEntry().equals(e))
.findFirst();
var control = (existing.isPresent()
? existing.get().getControl()
: s.standaloneControl().start());
control.setNonInteractive();
control.start();
var d = control.getShellDialect().getDumbMode();
if (!d.supportsAnyPossibleInteraction()) {
control.close();
d.throwIfUnsupported();
}
if (existing.isEmpty()) {
AppBeaconServer.get().getCache().getShellSessions().add(new BeaconShellSession(e, control));
}
return Response.builder()
.shellDialect(control.getShellDialect())
.osType(control.getOsType())
.osName(control.getOsName())
.temp(control.getSystemTemporaryDirectory())
.ttyState(control.getTtyState())
.build();
}
}

View file

@ -0,0 +1,19 @@
package io.xpipe.app.beacon.impl;
import io.xpipe.app.beacon.AppBeaconServer;
import io.xpipe.beacon.api.ShellStopExchange;
import com.sun.net.httpserver.HttpExchange;
import lombok.SneakyThrows;
public class ShellStopExchangeImpl extends ShellStopExchange {
@Override
@SneakyThrows
public Object handle(HttpExchange exchange, Request msg) {
var e = AppBeaconServer.get().getCache().getShellSession(msg.getConnection());
e.getControl().close();
AppBeaconServer.get().getCache().getShellSessions().remove(e);
return Response.builder().build();
}
}

View file

@ -0,0 +1,43 @@
package io.xpipe.app.beacon.impl;
import io.xpipe.app.ext.ProcessControlProvider;
import io.xpipe.app.terminal.TerminalLauncherManager;
import io.xpipe.beacon.api.SshLaunchExchange;
import io.xpipe.core.process.ShellDialects;
import com.sun.net.httpserver.HttpExchange;
import java.util.List;
public class SshLaunchExchangeImpl extends SshLaunchExchange {
@Override
public Object handle(HttpExchange exchange, Request msg) throws Exception {
if ("echo $SHELL".equals(msg.getArguments())) {
return Response.builder().command(List.of("echo", "/bin/bash")).build();
}
var usedDialect = ShellDialects.getStartableDialects().stream()
.filter(dialect -> dialect.getExecutableName().equalsIgnoreCase(msg.getArguments()))
.findFirst();
if (msg.getArguments() != null
&& usedDialect.isEmpty()
&& !msg.getArguments().contains("SSH_ORIGINAL_COMMAND")) {
return Response.builder().command(List.of()).build();
}
// There are sometimes multiple requests by a terminal client (e.g. Termius)
// This might fail sometimes, but it is expected
var r = TerminalLauncherManager.sshLaunchExchange();
var c = ProcessControlProvider.get()
.getEffectiveLocalDialect()
.getOpenScriptCommand(r.toString())
.buildBaseParts(null);
return Response.builder().command(c).build();
}
@Override
public boolean requiresEnabledApi() {
return false;
}
}

View file

@ -0,0 +1,68 @@
package io.xpipe.app.beacon.impl;
import atlantafx.base.layout.ModalBox;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.comp.base.ModalOverlay;
import io.xpipe.app.core.AppCache;
import io.xpipe.app.core.window.AppDialog;
import io.xpipe.app.ext.ShellStore;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStorageQuery;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.app.terminal.TerminalLauncherManager;
import io.xpipe.beacon.BeaconClientException;
import io.xpipe.beacon.BeaconServerException;
import io.xpipe.beacon.api.TerminalExternalLaunchExchange;
import java.util.List;
public class TerminalExternalLaunchExchangeImpl extends TerminalExternalLaunchExchange {
@Override
public Object handle(HttpExchange exchange, Request msg) throws BeaconClientException, BeaconServerException {
var found = DataStorageQuery.queryUserInput(msg.getConnection());
if (found.isEmpty()) {
throw new BeaconClientException("No connection found for input " + msg.getConnection());
}
if (found.size() > 1) {
throw new BeaconServerException("Multiple connections found: " + found.stream().map(DataStoreEntry::getName).toList());
}
var e = found.getFirst();
var isShell = e.getStore() instanceof ShellStore;
if (!isShell) {
throw new BeaconClientException("Connection " + DataStorage.get().getStorePath(e).toString() + " is not a shell connection");
}
if (!checkPermission()) {
return Response.builder().command(List.of()).build();
}
var r = TerminalLauncherManager.externalExchange(e.ref(), msg.getArguments());
return Response.builder().command(r).build();
}
private boolean checkPermission() {
var cache = AppCache.getBoolean("externalLaunchPermitted", false);
if (cache) {
return true;
}
var r = AppDialog.confirm("externalLaunch");
if (r) {
AppCache.update("externalLaunchPermitted", true);
}
return r;
}
@Override
public boolean requiresEnabledApi() {
return false;
}
@Override
public Object getSynchronizationObject() {
return DataStorage.get();
}
}

View file

@ -0,0 +1,21 @@
package io.xpipe.app.beacon.impl;
import io.xpipe.app.terminal.TerminalLauncherManager;
import io.xpipe.beacon.BeaconClientException;
import io.xpipe.beacon.BeaconServerException;
import io.xpipe.beacon.api.TerminalLaunchExchange;
import com.sun.net.httpserver.HttpExchange;
public class TerminalLaunchExchangeImpl extends TerminalLaunchExchange {
@Override
public Object handle(HttpExchange exchange, Request msg) throws BeaconClientException, BeaconServerException {
var r = TerminalLauncherManager.launchExchange(msg.getRequest());
return Response.builder().targetFile(r).build();
}
@Override
public boolean requiresEnabledApi() {
return false;
}
}

View file

@ -0,0 +1,30 @@
package io.xpipe.app.beacon.impl;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.terminal.TerminalLauncherManager;
import io.xpipe.app.terminal.TerminalView;
import io.xpipe.beacon.BeaconClientException;
import io.xpipe.beacon.api.TerminalPrepareExchange;
import com.sun.net.httpserver.HttpExchange;
public class TerminalPrepareExchangeImpl extends TerminalPrepareExchange {
@Override
public Object handle(HttpExchange exchange, Request msg) throws BeaconClientException {
TerminalView.get().open(msg.getRequest(), msg.getPid());
TerminalLauncherManager.registerPid(msg.getRequest(), msg.getPid());
var term = AppPrefs.get().terminalType().getValue();
var unicode = term.supportsUnicode();
var escapes = term.supportsEscapes();
return Response.builder()
.supportsUnicode(unicode)
.supportsEscapeSequences(escapes)
.build();
}
@Override
public boolean requiresEnabledApi() {
return false;
}
}

View file

@ -0,0 +1,22 @@
package io.xpipe.app.beacon.impl;
import io.xpipe.app.terminal.TerminalLauncherManager;
import io.xpipe.beacon.BeaconClientException;
import io.xpipe.beacon.BeaconServerException;
import io.xpipe.beacon.api.TerminalWaitExchange;
import com.sun.net.httpserver.HttpExchange;
public class TerminalWaitExchangeImpl extends TerminalWaitExchange {
@Override
public Object handle(HttpExchange exchange, Request msg) throws BeaconClientException, BeaconServerException {
TerminalLauncherManager.waitExchange(msg.getRequest());
return Response.builder().build();
}
@Override
public boolean requiresEnabledApi() {
return false;
}
}

View file

@ -0,0 +1,58 @@
package io.xpipe.app.browser;
import io.xpipe.app.util.BooleanScope;
import io.xpipe.app.util.ThreadHelper;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import lombok.Getter;
import java.util.ArrayList;
import java.util.List;
@Getter
public class BrowserAbstractSessionModel<T extends BrowserSessionTab> {
protected final ObservableList<T> sessionEntries = FXCollections.observableArrayList();
protected final Property<T> selectedEntry = new SimpleObjectProperty<>();
protected final BooleanProperty busy = new SimpleBooleanProperty();
public void closeAsync(BrowserSessionTab e) {
ThreadHelper.runAsync(() -> {
// This is a bit ugly
// If we die on tab init, wait a bit with closing to avoid removal while it is still being inited/added
ThreadHelper.sleep(100);
closeSync(e);
});
}
public void openSync(T e, BooleanProperty externalBusy) throws Exception {
try (var b = new BooleanScope(externalBusy != null ? externalBusy : new SimpleBooleanProperty()).start()) {
e.init();
// Prevent multiple calls from interfering with each other
synchronized (this) {
sessionEntries.add(e);
// The tab pane doesn't automatically select new tabs
selectedEntry.setValue(e);
}
}
}
public void closeSync(BrowserSessionTab e) {
e.close();
synchronized (BrowserAbstractSessionModel.this) {
this.sessionEntries.remove(e);
}
}
public List<T> getSessionEntriesSnapshot() {
synchronized (this) {
return new ArrayList<>(sessionEntries);
}
}
}

View file

@ -0,0 +1,195 @@
package io.xpipe.app.browser;
import io.xpipe.app.browser.file.BrowserConnectionListComp;
import io.xpipe.app.browser.file.BrowserConnectionListFilterComp;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.file.BrowserFileSystemTabComp;
import io.xpipe.app.browser.file.BrowserFileSystemTabModel;
import io.xpipe.app.comp.Comp;
import io.xpipe.app.comp.base.DialogComp;
import io.xpipe.app.comp.base.LeftSplitPaneComp;
import io.xpipe.app.comp.base.StackComp;
import io.xpipe.app.comp.base.VerticalComp;
import io.xpipe.app.comp.store.StoreEntryWrapper;
import io.xpipe.app.core.AppLayoutModel;
import io.xpipe.app.ext.ShellStore;
import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.app.util.BindingsHelper;
import io.xpipe.app.util.FileReference;
import io.xpipe.app.util.PlatformThread;
import io.xpipe.app.util.ThreadHelper;
import io.xpipe.core.store.FileSystemStore;
import javafx.beans.property.BooleanProperty;
import javafx.collections.ListChangeListener;
import javafx.geometry.Pos;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.stage.WindowEvent;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
public class BrowserFileChooserSessionComp extends DialogComp {
private final Stage stage;
private final BrowserFileChooserSessionModel model;
public BrowserFileChooserSessionComp(Stage stage, BrowserFileChooserSessionModel model) {
this.stage = stage;
this.model = model;
}
public static void openSingleFile(
Supplier<DataStoreEntryRef<? extends FileSystemStore>> store, Consumer<FileReference> file, boolean save) {
PlatformThread.runLaterIfNeeded(() -> {
var lastWindow = Window.getWindows().stream()
.filter(window -> window.isFocused())
.findFirst();
var model = new BrowserFileChooserSessionModel(BrowserFileSystemTabModel.SelectionMode.SINGLE_FILE);
DialogComp.showWindow(save ? "saveFileTitle" : "openFileTitle", stage -> {
stage.addEventFilter(WindowEvent.WINDOW_HIDDEN, event -> {
lastWindow.ifPresent(window -> window.requestFocus());
});
var comp = new BrowserFileChooserSessionComp(stage, model);
comp.apply(struc -> struc.get().setPrefSize(1200, 700))
.styleClass("browser")
.styleClass("chooser");
return comp;
});
model.setOnFinish(fileStores -> {
file.accept(fileStores.size() > 0 ? fileStores.getFirst() : null);
});
ThreadHelper.runAsync(() -> {
model.openFileSystemAsync(store.get(), null, null);
});
});
}
@Override
protected String finishKey() {
return "select";
}
@Override
protected Comp<?> pane(Comp<?> content) {
return content;
}
@Override
protected void finish() {
stage.close();
model.finishChooser();
}
@Override
protected void discard() {
model.finishWithoutChoice();
}
@Override
public Comp<?> content() {
Predicate<StoreEntryWrapper> applicable = storeEntryWrapper -> {
return (storeEntryWrapper.getEntry().getStore() instanceof ShellStore)
&& storeEntryWrapper.getEntry().getValidity().isUsable();
};
BiConsumer<StoreEntryWrapper, BooleanProperty> action = (w, busy) -> {
ThreadHelper.runFailableAsync(() -> {
var entry = w.getEntry();
if (!entry.getValidity().isUsable()) {
return;
}
// Don't open same system again
var current = model.getSelectedEntry().getValue();
if (current != null && entry.ref().equals(current.getEntry())) {
return;
}
if (entry.getStore() instanceof ShellStore) {
model.openFileSystemAsync(entry.ref(), null, busy);
}
});
};
var bookmarkTopBar = new BrowserConnectionListFilterComp();
var bookmarksList = new BrowserConnectionListComp(
BindingsHelper.map(
model.getSelectedEntry(), v -> v != null ? v.getEntry().get() : null),
applicable,
action,
bookmarkTopBar.getCategory(),
bookmarkTopBar.getFilter());
var bookmarksContainer = new StackComp(List.of(bookmarksList)).styleClass("bookmarks-container");
bookmarksContainer
.apply(struc -> {
var rec = new Rectangle();
rec.widthProperty().bind(struc.get().widthProperty());
rec.heightProperty().bind(struc.get().heightProperty());
rec.setArcHeight(7);
rec.setArcWidth(7);
struc.get().getChildren().getFirst().setClip(rec);
})
.vgrow();
var stack = Comp.of(() -> {
var s = new StackPane();
model.getSelectedEntry().subscribe(selected -> {
PlatformThread.runLaterIfNeeded(() -> {
if (selected != null) {
s.getChildren().setAll(new BrowserFileSystemTabComp(selected, false).createRegion());
} else {
s.getChildren().clear();
}
});
});
return s;
});
var vertical = new VerticalComp(List.of(bookmarkTopBar, bookmarksContainer)).styleClass("left");
var splitPane = new LeftSplitPaneComp(vertical, stack)
.withInitialWidth(AppLayoutModel.get().getSavedState().getBrowserConnectionsWidth())
.withOnDividerChange(AppLayoutModel.get().getSavedState()::setBrowserConnectionsWidth)
.styleClass("background")
.apply(struc -> {
struc.getLeft().setMinWidth(200);
struc.getLeft().setMaxWidth(500);
});
return splitPane;
}
@Override
public Comp<?> bottom() {
return Comp.of(() -> {
var selected = new HBox();
selected.setAlignment(Pos.CENTER_LEFT);
model.getFileSelection().addListener((ListChangeListener<? super BrowserEntry>) c -> {
PlatformThread.runLaterIfNeeded(() -> {
selected.getChildren()
.setAll(c.getList().stream()
.map(s -> {
var field = new TextField(
s.getRawFileEntry().getPath());
field.setEditable(false);
field.getStyleClass().add("chooser-selection");
HBox.setHgrow(field, Priority.ALWAYS);
return field;
})
.toList());
});
});
var bottomBar = new HBox(selected);
HBox.setHgrow(selected, Priority.ALWAYS);
bottomBar.setAlignment(Pos.CENTER);
return bottomBar;
});
}
}

View file

@ -0,0 +1,106 @@
package io.xpipe.app.browser;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.file.BrowserFileSystemTabModel;
import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.app.util.BooleanScope;
import io.xpipe.app.util.DerivedObservableList;
import io.xpipe.app.util.FileReference;
import io.xpipe.app.util.ThreadHelper;
import io.xpipe.core.store.FileNames;
import io.xpipe.core.store.FileSystemStore;
import io.xpipe.core.util.FailableFunction;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
@Getter
public class BrowserFileChooserSessionModel extends BrowserAbstractSessionModel<BrowserFileSystemTabModel> {
private final BrowserFileSystemTabModel.SelectionMode selectionMode;
private final ObservableList<BrowserEntry> fileSelection = FXCollections.observableArrayList();
@Setter
private Consumer<List<FileReference>> onFinish;
public BrowserFileChooserSessionModel(BrowserFileSystemTabModel.SelectionMode selectionMode) {
this.selectionMode = selectionMode;
selectedEntry.addListener((observable, oldValue, newValue) -> {
if (newValue == null) {
fileSelection.clear();
return;
}
var l = new DerivedObservableList<>(fileSelection, true);
l.bindContent(newValue.getFileList().getSelection());
});
}
public void finishChooser() {
var chosen = new ArrayList<>(fileSelection);
synchronized (BrowserFileChooserSessionModel.this) {
var open = selectedEntry.getValue();
if (open != null) {
ThreadHelper.runAsync(() -> {
open.close();
});
}
}
var stores = chosen.stream()
.map(entry -> new FileReference(
selectedEntry.getValue().getEntry(),
entry.getRawFileEntry().getPath()))
.toList();
onFinish.accept(stores);
}
public void finishWithoutChoice() {
synchronized (BrowserFileChooserSessionModel.this) {
var open = selectedEntry.getValue();
if (open != null) {
ThreadHelper.runAsync(() -> {
open.close();
});
}
}
}
public void openFileSystemAsync(
DataStoreEntryRef<? extends FileSystemStore> store,
FailableFunction<BrowserFileSystemTabModel, String, Exception> path,
BooleanProperty externalBusy) {
if (store == null) {
return;
}
ThreadHelper.runFailableAsync(() -> {
BrowserFileSystemTabModel model;
try (var b = new BooleanScope(externalBusy != null ? externalBusy : new SimpleBooleanProperty()).start()) {
model = new BrowserFileSystemTabModel(this, store, selectionMode);
model.init();
// Prevent multiple calls from interfering with each other
synchronized (BrowserFileChooserSessionModel.this) {
selectedEntry.setValue(model);
sessionEntries.add(model);
}
if (path != null) {
model.initWithGivenDirectory(FileNames.toDirectory(path.apply(model)));
} else {
model.initWithDefaultDirectory();
}
}
});
}
}

View file

@ -0,0 +1,217 @@
package io.xpipe.app.browser;
import io.xpipe.app.browser.file.BrowserConnectionListComp;
import io.xpipe.app.browser.file.BrowserConnectionListFilterComp;
import io.xpipe.app.browser.file.BrowserTransferComp;
import io.xpipe.app.comp.Comp;
import io.xpipe.app.comp.CompStructure;
import io.xpipe.app.comp.SimpleComp;
import io.xpipe.app.comp.base.AnchorComp;
import io.xpipe.app.comp.base.LeftSplitPaneComp;
import io.xpipe.app.comp.base.LoadingOverlayComp;
import io.xpipe.app.comp.base.StackComp;
import io.xpipe.app.comp.base.VerticalComp;
import io.xpipe.app.comp.store.StoreEntryWrapper;
import io.xpipe.app.core.AppLayoutModel;
import io.xpipe.app.ext.ShellStore;
import io.xpipe.app.util.BindingsHelper;
import io.xpipe.app.util.PlatformThread;
import io.xpipe.app.util.ThreadHelper;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.geometry.Insets;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.scene.shape.Rectangle;
import java.util.HashMap;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
public class BrowserFullSessionComp extends SimpleComp {
private final BrowserFullSessionModel model;
public BrowserFullSessionComp(BrowserFullSessionModel model) {
this.model = model;
}
@Override
protected Region createSimple() {
var vertical = createLeftSide();
var leftSplit = new SimpleDoubleProperty();
var rightSplit = new SimpleDoubleProperty();
var tabs = new BrowserSessionTabsComp(model, leftSplit, rightSplit);
tabs.apply(struc -> {
struc.get().setViewOrder(1);
struc.get().setPickOnBounds(false);
AnchorPane.setTopAnchor(struc.get(), 0.0);
AnchorPane.setBottomAnchor(struc.get(), 0.0);
AnchorPane.setLeftAnchor(struc.get(), 0.0);
AnchorPane.setRightAnchor(struc.get(), 0.0);
});
vertical.apply(struc -> {
struc.get()
.paddingProperty()
.bind(Bindings.createObjectBinding(
() -> new Insets(tabs.getHeaderHeight().get(), 0, 0, 0), tabs.getHeaderHeight()));
});
var loadingIndicator = LoadingOverlayComp.noProgress(Comp.empty(), model.getBusy())
.apply(struc -> {
AnchorPane.setTopAnchor(struc.get(), 3.0);
AnchorPane.setRightAnchor(struc.get(), 0.0);
})
.styleClass("tab-loading-indicator");
var pinnedStack = createSplitStack(rightSplit, tabs);
var loadingStack = new AnchorComp(List.of(tabs, pinnedStack, loadingIndicator));
loadingStack.apply(struc -> struc.get().setPickOnBounds(false));
var splitPane = new LeftSplitPaneComp(vertical, loadingStack)
.withInitialWidth(AppLayoutModel.get().getSavedState().getBrowserConnectionsWidth())
.withOnDividerChange(d -> {
AppLayoutModel.get().getSavedState().setBrowserConnectionsWidth(d);
leftSplit.set(d);
});
splitPane.apply(struc -> {
struc.getLeft().setMinWidth(200);
struc.getLeft().setMaxWidth(500);
struc.get().setPickOnBounds(false);
});
splitPane.apply(struc -> {
struc.get().skinProperty().subscribe(newValue -> {
if (newValue != null) {
Platform.runLater(() -> {
struc.get().getChildrenUnmodifiable().forEach(node -> {
node.setClip(null);
node.setPickOnBounds(false);
});
struc.get().lookupAll(".split-pane-divider").forEach(node -> node.setViewOrder(-1));
});
}
});
});
splitPane.styleClass("browser");
var r = splitPane.createRegion();
return r;
}
private Comp<CompStructure<VBox>> createLeftSide() {
Predicate<StoreEntryWrapper> applicable = storeEntryWrapper -> {
if (!storeEntryWrapper.getEntry().getValidity().isUsable()) {
return false;
}
if (storeEntryWrapper.getEntry().getStore() instanceof ShellStore) {
return true;
}
return storeEntryWrapper.getEntry().getProvider().browserAction(model, storeEntryWrapper.getEntry(), null)
!= null;
};
BiConsumer<StoreEntryWrapper, BooleanProperty> action = (w, busy) -> {
ThreadHelper.runFailableAsync(() -> {
var entry = w.getEntry();
if (!entry.getValidity().isUsable()) {
return;
}
var a = entry.getProvider().browserAction(model, entry, busy);
if (a != null) {
a.execute();
}
});
};
var bookmarkTopBar = new BrowserConnectionListFilterComp();
var bookmarksList = new BrowserConnectionListComp(
BindingsHelper.map(
model.getSelectedEntry(),
v -> v instanceof BrowserStoreSessionTab<?> st
? st.getEntry().get()
: null),
applicable,
action,
bookmarkTopBar.getCategory(),
bookmarkTopBar.getFilter());
var bookmarksContainer = new StackComp(List.of(bookmarksList)).styleClass("bookmarks-container");
bookmarksContainer
.apply(struc -> {
var rec = new Rectangle();
rec.widthProperty().bind(struc.get().widthProperty());
rec.heightProperty().bind(struc.get().heightProperty());
rec.setArcHeight(11);
rec.setArcWidth(11);
struc.get().getChildren().getFirst().setClip(rec);
})
.vgrow();
var localDownloadStage = new BrowserTransferComp(model.getLocalTransfersStage())
.hide(PlatformThread.sync(Bindings.createBooleanBinding(
() -> {
if (model.getSessionEntries().size() == 0) {
return true;
}
return false;
},
model.getSessionEntries(),
model.getSelectedEntry())));
localDownloadStage.prefHeight(200);
localDownloadStage.maxHeight(200);
var vertical =
new VerticalComp(List.of(bookmarkTopBar, bookmarksContainer, localDownloadStage)).styleClass("left");
return vertical;
}
private StackComp createSplitStack(SimpleDoubleProperty rightSplit, BrowserSessionTabsComp tabs) {
var cache = new HashMap<BrowserSessionTab, Region>();
var splitStack = new StackComp(List.of());
splitStack.apply(struc -> struc.get().setPickOnBounds(false));
splitStack.apply(struc -> {
model.getEffectiveRightTab().subscribe((newValue) -> {
PlatformThread.runLaterIfNeeded(() -> {
var all = model.getAllTabs();
cache.keySet().removeIf(browserSessionTab -> !all.contains(browserSessionTab));
if (newValue == null) {
struc.get().getChildren().clear();
return;
}
var cached = cache.containsKey(newValue);
if (!cached) {
cache.put(newValue, newValue.comp().createRegion());
}
var r = cache.get(newValue);
struc.get().getChildren().clear();
struc.get().getChildren().add(r);
struc.get().setMinWidth(rightSplit.get());
struc.get().setPrefWidth(rightSplit.get());
struc.get().setMaxWidth(rightSplit.get());
});
});
rightSplit.addListener((observable, oldValue, newValue) -> {
struc.get().setMinWidth(newValue.doubleValue());
struc.get().setPrefWidth(newValue.doubleValue());
struc.get().setMaxWidth(newValue.doubleValue());
});
AnchorPane.setBottomAnchor(struc.get(), 0.0);
AnchorPane.setRightAnchor(struc.get(), 0.0);
tabs.getHeaderHeight().subscribe(number -> {
AnchorPane.setTopAnchor(struc.get(), number.doubleValue());
});
});
return splitStack;
}
}

View file

@ -0,0 +1,251 @@
package io.xpipe.app.browser;
import io.xpipe.app.browser.file.BrowserFileSystemTabModel;
import io.xpipe.app.browser.file.BrowserHistorySavedState;
import io.xpipe.app.browser.file.BrowserHistoryTabModel;
import io.xpipe.app.browser.file.BrowserTransferModel;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.app.util.BooleanScope;
import io.xpipe.app.util.ThreadHelper;
import io.xpipe.core.store.FileNames;
import io.xpipe.core.store.FileSystemStore;
import io.xpipe.core.util.FailableFunction;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableMap;
import lombok.Getter;
import lombok.SneakyThrows;
import java.util.*;
@Getter
public class BrowserFullSessionModel extends BrowserAbstractSessionModel<BrowserSessionTab> {
public static final BrowserFullSessionModel DEFAULT = new BrowserFullSessionModel();
@SneakyThrows
public static void init() {
DEFAULT.openSync(new BrowserHistoryTabModel(DEFAULT), null);
if (AppPrefs.get().pinLocalMachineOnStartup().get()) {
var tab = new BrowserFileSystemTabModel(
DEFAULT, DataStorage.get().local().ref(), BrowserFileSystemTabModel.SelectionMode.ALL);
DEFAULT.openSync(tab, null);
DEFAULT.pinTab(tab);
}
}
private final BrowserTransferModel localTransfersStage = new BrowserTransferModel(this);
private final Property<Boolean> draggingFiles = new SimpleBooleanProperty();
private final Property<BrowserSessionTab> globalPinnedTab = new SimpleObjectProperty<>();
private final ObservableMap<BrowserSessionTab, BrowserSessionTab> splits = FXCollections.observableHashMap();
private final ObservableValue<BrowserSessionTab> effectiveRightTab = createEffectiveRightTab();
private final SequencedSet<BrowserSessionTab> previousTabs = new LinkedHashSet<>();
private ObservableValue<BrowserSessionTab> createEffectiveRightTab() {
return Bindings.createObjectBinding(
() -> {
var current = selectedEntry.getValue();
if (current == null) {
return null;
}
if (!current.isCloseable()) {
return null;
}
var split = splits.get(current);
if (split != null) {
return split;
}
var global = globalPinnedTab.getValue();
if (global == null) {
return null;
}
if (global == selectedEntry.getValue()) {
return null;
}
return global;
},
globalPinnedTab,
selectedEntry,
splits);
}
public BrowserFullSessionModel() {
sessionEntries.addListener((ListChangeListener<? super BrowserSessionTab>) c -> {
var v = globalPinnedTab.getValue();
if (v != null && !c.getList().contains(v)) {
globalPinnedTab.setValue(null);
}
splits.keySet().removeIf(browserSessionTab -> !c.getList().contains(browserSessionTab));
});
selectedEntry.addListener((observable, oldValue, newValue) -> {
if (newValue != null) {
previousTabs.remove(newValue);
previousTabs.add(newValue);
}
});
}
public Set<BrowserSessionTab> getAllTabs() {
var set = new HashSet<BrowserSessionTab>();
set.addAll(sessionEntries);
set.addAll(splits.values());
if (globalPinnedTab.getValue() != null) {
set.add(globalPinnedTab.getValue());
}
return set;
}
public void splitTab(BrowserSessionTab tab, BrowserSessionTab split) {
if (splits.containsKey(tab)) {
return;
}
splits.put(tab, split);
ThreadHelper.runFailableAsync(() -> {
split.init();
});
}
public void unsplitTab(BrowserSessionTab tab) {
if (splits.values().remove(tab)) {
ThreadHelper.runFailableAsync(() -> {
tab.close();
});
}
}
public void pinTab(BrowserSessionTab tab) {
if (tab.equals(globalPinnedTab.getValue())) {
return;
}
globalPinnedTab.setValue(tab);
var previousOthers = previousTabs.stream()
.filter(browserSessionTab -> browserSessionTab != tab && browserSessionTab.isCloseable())
.toList();
if (previousOthers.size() > 0) {
var prev = previousOthers.getLast();
getSelectedEntry().setValue(prev);
}
}
public void unpinTab(BrowserSessionTab tab) {
ThreadHelper.runFailableAsync(() -> {
globalPinnedTab.setValue(null);
});
}
public void restoreState(BrowserHistorySavedState state) {
ThreadHelper.runAsync(() -> {
var l = new ArrayList<>(state.getEntries());
l.forEach(e -> {
restoreStateAsync(e, null);
// Don't try to run everything in parallel as that can be taxing
ThreadHelper.sleep(1000);
});
});
}
public void restoreStateAsync(BrowserHistorySavedState.Entry e, BooleanProperty busy) {
var storageEntry = DataStorage.get().getStoreEntryIfPresent(e.getUuid());
storageEntry.ifPresent(entry -> {
openFileSystemAsync(entry.ref(), model -> e.getPath(), busy);
});
}
public void reset() {
synchronized (BrowserFullSessionModel.this) {
if (globalPinnedTab.getValue() != null) {
globalPinnedTab.setValue(null);
}
var all = new ArrayList<>(sessionEntries);
for (var o : all) {
// Don't close busy connections gracefully
// as we otherwise might lock up
if (!o.canImmediatelyClose()) {
continue;
}
// Prevent blocking of shutdown
closeAsync(o);
}
if (all.size() > 0) {
ThreadHelper.sleep(1000);
}
}
// Delete all files
localTransfersStage.clear(true);
}
public void openFileSystemAsync(
DataStoreEntryRef<? extends FileSystemStore> store,
FailableFunction<BrowserFileSystemTabModel, String, Exception> path,
BooleanProperty externalBusy) {
if (store == null) {
return;
}
ThreadHelper.runFailableAsync(() -> {
openFileSystemSync(store, path, externalBusy, true);
});
}
public BrowserFileSystemTabModel openFileSystemSync(
DataStoreEntryRef<? extends FileSystemStore> store,
FailableFunction<BrowserFileSystemTabModel, String, Exception> path,
BooleanProperty externalBusy,
boolean select)
throws Exception {
BrowserFileSystemTabModel model;
try (var b = new BooleanScope(externalBusy != null ? externalBusy : new SimpleBooleanProperty()).start()) {
try (var sessionBusy = new BooleanScope(busy).exclusive().start()) {
model = new BrowserFileSystemTabModel(this, store, BrowserFileSystemTabModel.SelectionMode.ALL);
model.init();
// Prevent multiple calls from interfering with each other
synchronized (BrowserFullSessionModel.this) {
sessionEntries.add(model);
if (select) {
// The tab pane doesn't automatically select new tabs
selectedEntry.setValue(model);
}
}
}
}
if (path != null) {
model.initWithGivenDirectory(FileNames.toDirectory(path.apply(model)));
} else {
model.initWithDefaultDirectory();
}
return model;
}
@Override
public void closeSync(BrowserSessionTab e) {
var split = splits.get(e);
if (split != null) {
split.close();
}
previousTabs.remove(e);
super.closeSync(e);
}
}

View file

@ -0,0 +1,42 @@
package io.xpipe.app.browser;
import io.xpipe.app.comp.Comp;
import io.xpipe.app.storage.DataColor;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import lombok.Getter;
@Getter
public abstract class BrowserSessionTab {
protected final BooleanProperty busy = new SimpleBooleanProperty();
protected final BrowserAbstractSessionModel<?> browserModel;
protected final Property<BrowserSessionTab> splitTab = new SimpleObjectProperty<>();
public BrowserSessionTab(BrowserAbstractSessionModel<?> browserModel) {
this.browserModel = browserModel;
}
public abstract Comp<?> comp();
public abstract boolean canImmediatelyClose();
public abstract void init() throws Exception;
public abstract void close();
public abstract ObservableValue<String> getName();
public abstract String getIcon();
public abstract DataColor getColor();
public boolean isCloseable() {
return true;
}
}

View file

@ -0,0 +1,505 @@
package io.xpipe.app.browser;
import io.xpipe.app.comp.Comp;
import io.xpipe.app.comp.SimpleComp;
import io.xpipe.app.comp.base.PrettyImageHelper;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.util.BooleanScope;
import io.xpipe.app.util.ContextMenuHelper;
import io.xpipe.app.util.LabelGraphic;
import io.xpipe.app.util.PlatformThread;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.value.ObservableDoubleValue;
import javafx.collections.ListChangeListener;
import javafx.css.PseudoClass;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.*;
import javafx.scene.control.skin.TabPaneSkin;
import javafx.scene.input.*;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import atlantafx.base.controls.RingProgressIndicator;
import atlantafx.base.theme.Styles;
import lombok.Getter;
import java.util.*;
import static atlantafx.base.theme.Styles.DENSE;
import static atlantafx.base.theme.Styles.toggleStyleClass;
import static javafx.scene.control.TabPane.TabClosingPolicy.ALL_TABS;
public class BrowserSessionTabsComp extends SimpleComp {
private final BrowserFullSessionModel model;
private final ObservableDoubleValue leftPadding;
private final DoubleProperty rightPadding;
@Getter
private final DoubleProperty headerHeight;
public BrowserSessionTabsComp(
BrowserFullSessionModel model, ObservableDoubleValue leftPadding, DoubleProperty rightPadding) {
this.model = model;
this.leftPadding = leftPadding;
this.rightPadding = rightPadding;
this.headerHeight = new SimpleDoubleProperty();
}
public Region createSimple() {
var tabs = createTabPane();
var topBackground = Comp.hspacer().styleClass("top-spacer").createRegion();
leftPadding.subscribe(number -> {
StackPane.setMargin(topBackground, new Insets(0, 0, 0, -number.doubleValue() - 3));
});
var stack = new StackPane(topBackground, tabs);
stack.setAlignment(Pos.TOP_CENTER);
topBackground.prefHeightProperty().bind(headerHeight);
topBackground.maxHeightProperty().bind(topBackground.prefHeightProperty());
topBackground.prefWidthProperty().bind(tabs.widthProperty());
return stack;
}
private TabPane createTabPane() {
var tabs = new TabPane();
tabs.setTabDragPolicy(TabPane.TabDragPolicy.REORDER);
tabs.setTabMinWidth(Region.USE_PREF_SIZE);
tabs.setTabMaxWidth(400);
tabs.setTabClosingPolicy(ALL_TABS);
tabs.setSkin(new TabPaneSkin(tabs));
Styles.toggleStyleClass(tabs, TabPane.STYLE_CLASS_FLOATING);
toggleStyleClass(tabs, DENSE);
setupCustomStyle(tabs);
// Sync to guarantee that no external changes are made during this
synchronized (model) {
setupTabEntries(tabs);
}
setupKeyEvents(tabs);
return tabs;
}
private void setupTabEntries(TabPane tabs) {
var map = new HashMap<BrowserSessionTab, Tab>();
// Restore state
model.getSessionEntries().forEach(v -> {
var t = createTab(tabs, v);
map.put(v, t);
tabs.getTabs().add(t);
});
tabs.getSelectionModel()
.select(model.getSessionEntries()
.indexOf(model.getSelectedEntry().getValue()));
// Used for ignoring changes by the tabpane when new tabs are added. We want to perform the selections manually!
var addingTab = new SimpleBooleanProperty();
// Handle selection from platform
tabs.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
if (addingTab.get()) {
return;
}
if (newValue == null) {
model.getSelectedEntry().setValue(null);
return;
}
var source = map.entrySet().stream()
.filter(openFileSystemModelTabEntry ->
openFileSystemModelTabEntry.getValue().equals(newValue))
.findAny()
.map(Map.Entry::getKey)
.orElse(null);
model.getSelectedEntry().setValue(source);
});
// Handle selection from model
model.getSelectedEntry().addListener((observable, oldValue, newValue) -> {
PlatformThread.runLaterIfNeeded(() -> {
if (newValue == null) {
tabs.getSelectionModel().select(null);
return;
}
var toSelect = map.entrySet().stream()
.filter(openFileSystemModelTabEntry ->
openFileSystemModelTabEntry.getKey().equals(newValue))
.findAny()
.map(Map.Entry::getValue)
.orElse(null);
if (toSelect == null || !tabs.getTabs().contains(toSelect)) {
tabs.getSelectionModel().select(null);
return;
}
tabs.getSelectionModel().select(toSelect);
Platform.runLater(() -> {
toSelect.getContent().requestFocus();
});
});
});
model.getSessionEntries().addListener((ListChangeListener<? super BrowserSessionTab>) c -> {
while (c.next()) {
for (var r : c.getRemoved()) {
PlatformThread.runLaterIfNeeded(() -> {
var t = map.remove(r);
tabs.getTabs().remove(t);
});
}
for (var a : c.getAddedSubList()) {
PlatformThread.runLaterIfNeeded(() -> {
try (var b = new BooleanScope(addingTab).start()) {
var t = createTab(tabs, a);
map.put(a, t);
tabs.getTabs().add(t);
}
});
}
}
});
tabs.getTabs().addListener((ListChangeListener<? super Tab>) c -> {
while (c.next()) {
for (var r : c.getRemoved()) {
var source = map.entrySet().stream()
.filter(openFileSystemModelTabEntry ->
openFileSystemModelTabEntry.getValue().equals(r))
.findAny()
.orElse(null);
// Only handle close events that are triggered from the platform
if (source == null) {
continue;
}
model.closeAsync(source.getKey());
}
}
});
}
private void setupCustomStyle(TabPane tabs) {
tabs.skinProperty().subscribe(newValue -> {
if (newValue != null) {
Platform.runLater(() -> {
tabs.setClip(null);
tabs.setPickOnBounds(false);
tabs.lookupAll(".tab-header-area").forEach(node -> {
node.setClip(null);
node.setPickOnBounds(false);
var r = (Region) node;
r.prefHeightProperty().bind(r.maxHeightProperty());
r.setMinHeight(Region.USE_PREF_SIZE);
});
tabs.lookupAll(".headers-region").forEach(node -> {
node.setClip(null);
node.setPickOnBounds(false);
var r = (Region) node;
r.prefHeightProperty().bind(r.maxHeightProperty());
r.setMinHeight(Region.USE_PREF_SIZE);
});
Region headerArea = (Region) tabs.lookup(".tab-header-area");
headerArea
.paddingProperty()
.bind(Bindings.createObjectBinding(
() -> new Insets(2, 0, 4, -leftPadding.get() + 3), leftPadding));
tabs.setPadding(new Insets(0, 0, 0, -5));
headerHeight.bind(headerArea.heightProperty());
});
}
});
}
private static void setupKeyEvents(TabPane tabs) {
tabs.addEventHandler(KeyEvent.KEY_PRESSED, keyEvent -> {
var current = tabs.getSelectionModel().getSelectedItem();
if (current == null) {
return;
}
if (new KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN).match(keyEvent)) {
tabs.getTabs().remove(current);
keyEvent.consume();
return;
}
if (new KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN, KeyCombination.SHIFT_DOWN)
.match(keyEvent)) {
tabs.getTabs().clear();
keyEvent.consume();
}
if (keyEvent.getCode().isFunctionKey()) {
var start = KeyCode.F1.getCode();
var index = keyEvent.getCode().getCode() - start;
if (index < tabs.getTabs().size()) {
tabs.getSelectionModel().select(index);
keyEvent.consume();
return;
}
}
var forward = new KeyCodeCombination(KeyCode.TAB, KeyCombination.SHORTCUT_DOWN);
if (forward.match(keyEvent)) {
var next = (tabs.getSelectionModel().getSelectedIndex() + 1)
% tabs.getTabs().size();
tabs.getSelectionModel().select(next);
keyEvent.consume();
return;
}
var back = new KeyCodeCombination(KeyCode.TAB, KeyCombination.SHORTCUT_DOWN, KeyCombination.SHIFT_DOWN);
if (back.match(keyEvent)) {
var previous = (tabs.getTabs().size() + tabs.getSelectionModel().getSelectedIndex() - 1)
% tabs.getTabs().size();
tabs.getSelectionModel().select(previous);
keyEvent.consume();
}
});
}
private ContextMenu createContextMenu(TabPane tabs, Tab tab, BrowserSessionTab tabModel) {
var cm = ContextMenuHelper.create();
if (tabModel.isCloseable()) {
var unpin = ContextMenuHelper.item(LabelGraphic.none(), "unpinTab");
unpin.visibleProperty()
.bind(PlatformThread.sync(Bindings.createBooleanBinding(
() -> {
return model.getGlobalPinnedTab().getValue() != null
&& model.getGlobalPinnedTab().getValue().equals(tabModel);
},
model.getGlobalPinnedTab())));
unpin.setOnAction(event -> {
model.unpinTab(tabModel);
event.consume();
});
cm.getItems().add(unpin);
var pin = ContextMenuHelper.item(LabelGraphic.none(), "pinTab");
pin.visibleProperty()
.bind(PlatformThread.sync(Bindings.createBooleanBinding(
() -> {
return model.getGlobalPinnedTab().getValue() == null;
},
model.getGlobalPinnedTab())));
pin.setOnAction(event -> {
model.pinTab(tabModel);
event.consume();
});
cm.getItems().add(pin);
}
var select = ContextMenuHelper.item(LabelGraphic.none(), "selectTab");
select.acceleratorProperty()
.bind(Bindings.createObjectBinding(
() -> {
var start = KeyCode.F1.getCode();
var index = tabs.getTabs().indexOf(tab);
var keyCode = Arrays.stream(KeyCode.values())
.filter(code -> code.getCode() == start + index)
.findAny()
.orElse(null);
return keyCode != null ? new KeyCodeCombination(keyCode) : null;
},
tabs.getTabs()));
select.setOnAction(event -> {
tabs.getSelectionModel().select(tab);
event.consume();
});
cm.getItems().add(select);
cm.getItems().add(new SeparatorMenuItem());
var close = ContextMenuHelper.item(LabelGraphic.none(), "closeTab");
close.setAccelerator(new KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN));
close.setOnAction(event -> {
if (tab.isClosable()) {
tabs.getTabs().remove(tab);
}
event.consume();
});
cm.getItems().add(close);
var closeOthers = ContextMenuHelper.item(LabelGraphic.none(), "closeOtherTabs");
closeOthers.setOnAction(event -> {
tabs.getTabs()
.removeAll(tabs.getTabs().stream()
.filter(t -> t != tab && t.isClosable())
.toList());
event.consume();
});
cm.getItems().add(closeOthers);
var closeLeft = ContextMenuHelper.item(LabelGraphic.none(), "closeLeftTabs");
closeLeft.setOnAction(event -> {
var index = tabs.getTabs().indexOf(tab);
tabs.getTabs()
.removeAll(tabs.getTabs().stream()
.filter(t -> tabs.getTabs().indexOf(t) < index && t.isClosable())
.toList());
event.consume();
});
cm.getItems().add(closeLeft);
var closeRight = ContextMenuHelper.item(LabelGraphic.none(), "closeRightTabs");
closeRight.setOnAction(event -> {
var index = tabs.getTabs().indexOf(tab);
tabs.getTabs()
.removeAll(tabs.getTabs().stream()
.filter(t -> tabs.getTabs().indexOf(t) > index && t.isClosable())
.toList());
event.consume();
});
cm.getItems().add(closeRight);
var closeAll = ContextMenuHelper.item(LabelGraphic.none(), "closeAllTabs");
closeAll.setAccelerator(
new KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN, KeyCombination.SHIFT_DOWN));
closeAll.setOnAction(event -> {
tabs.getTabs()
.removeAll(
tabs.getTabs().stream().filter(t -> t.isClosable()).toList());
event.consume();
});
cm.getItems().add(closeAll);
return cm;
}
private Tab createTab(TabPane tabs, BrowserSessionTab tabModel) {
var tab = new Tab();
if (tabModel.isCloseable()) {
tab.setContextMenu(createContextMenu(tabs, tab, tabModel));
}
tab.setClosable(tabModel.isCloseable());
// Prevent closing while busy
tab.setOnCloseRequest(event -> {
if (!tabModel.canImmediatelyClose()) {
event.consume();
}
});
if (tabModel.getIcon() != null) {
var ring = new RingProgressIndicator(0, false);
ring.setMinSize(16, 16);
ring.setPrefSize(16, 16);
ring.setMaxSize(16, 16);
ring.progressProperty()
.bind(Bindings.createDoubleBinding(
() -> tabModel.getBusy().get()
&& !AppPrefs.get().performanceMode().get()
? -1d
: 0,
PlatformThread.sync(tabModel.getBusy()),
AppPrefs.get().performanceMode()));
var image = tabModel.getIcon();
var logo = PrettyImageHelper.ofFixedSizeSquare(image, 16).createRegion();
tab.graphicProperty()
.bind(Bindings.createObjectBinding(
() -> {
return tabModel.getBusy().get() ? ring : logo;
},
PlatformThread.sync(tabModel.getBusy())));
}
if (tabModel.getBrowserModel() instanceof BrowserFullSessionModel sessionModel) {
var global = PlatformThread.sync(sessionModel.getGlobalPinnedTab());
tab.textProperty()
.bind(Bindings.createStringBinding(
() -> {
var n = tabModel.getName().getValue();
return (AppPrefs.get().censorMode().get() ? "*".repeat(n.length()) : n)
+ (global.getValue() == tabModel ? " (" + AppI18n.get("pinned") + ")" : "");
},
tabModel.getName(),
global,
AppI18n.activeLanguage(),
AppPrefs.get().censorMode()));
} else {
tab.textProperty().bind(tabModel.getName());
}
Comp<?> comp = tabModel.comp();
var compRegion = comp.createRegion();
var empty = new StackPane();
empty.setMinWidth(450);
empty.widthProperty().addListener((observable, oldValue, newValue) -> {
if (tabModel.isCloseable() && tabs.getSelectionModel().getSelectedItem() == tab) {
rightPadding.setValue(newValue.doubleValue());
}
});
var split = new SplitPane(compRegion);
if (tabModel.isCloseable()) {
split.getItems().add(empty);
}
tabs.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
if (tabModel.isCloseable() && newValue == tab) {
rightPadding.setValue(empty.getWidth());
}
});
model.getEffectiveRightTab().subscribe(browserSessionTab -> {
PlatformThread.runLaterIfNeeded(() -> {
if (browserSessionTab != null && split.getItems().size() > 1) {
split.getItems().set(1, empty);
} else if (browserSessionTab != null && split.getItems().size() == 1) {
split.getItems().add(empty);
} else if (browserSessionTab == null && split.getItems().size() > 1) {
split.getItems().remove(1);
}
});
});
tab.setContent(split);
var id = UUID.randomUUID().toString();
tab.setId(id);
tabs.skinProperty().subscribe(newValue -> {
if (newValue != null) {
Platform.runLater(() -> {
Label l = (Label) tabs.lookup("#" + id + " .tab-label");
var w = l.maxWidthProperty();
l.minWidthProperty().bind(w);
l.prefWidthProperty().bind(w);
if (!tabModel.isCloseable()) {
l.pseudoClassStateChanged(PseudoClass.getPseudoClass("static"), true);
}
var close = (StackPane) tabs.lookup("#" + id + " .tab-close-button");
close.setPrefWidth(30);
StackPane c = (StackPane) tabs.lookup("#" + id + " .tab-container");
c.getStyleClass().add("color-box");
var color = tabModel.getColor();
if (color != null) {
c.getStyleClass().add(color.getId());
}
c.addEventHandler(
DragEvent.DRAG_ENTERED,
mouseEvent -> Platform.runLater(
() -> tabs.getSelectionModel().select(tab)));
});
}
});
return tab;
}
}

View file

@ -0,0 +1,48 @@
package io.xpipe.app.browser;
import io.xpipe.app.comp.Comp;
import io.xpipe.app.storage.DataColor;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.core.store.DataStore;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import lombok.Getter;
@Getter
public abstract class BrowserStoreSessionTab<T extends DataStore> extends BrowserSessionTab {
protected final DataStoreEntryRef<? extends T> entry;
private final String name;
public BrowserStoreSessionTab(BrowserAbstractSessionModel<?> browserModel, DataStoreEntryRef<? extends T> entry) {
super(browserModel);
this.entry = entry;
this.name = DataStorage.get().getStoreEntryDisplayName(entry.get());
}
@Override
public ObservableValue<String> getName() {
return new SimpleStringProperty(name);
}
public abstract Comp<?> comp();
public abstract boolean canImmediatelyClose();
public abstract void init() throws Exception;
public abstract void close();
@Override
public String getIcon() {
return entry.get().getEffectiveIconFile();
}
@Override
public DataColor getColor() {
return DataStorage.get().getEffectiveColor(entry.get());
}
}

View file

@ -0,0 +1,118 @@
package io.xpipe.app.browser.action;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.file.BrowserFileSystemTabModel;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.core.util.ModuleLayerLoader;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;
import javafx.scene.control.MenuItem;
import javafx.scene.input.KeyCombination;
import java.util.ArrayList;
import java.util.List;
import java.util.ServiceLoader;
public interface BrowserAction {
List<BrowserAction> ALL = new ArrayList<>();
static List<BrowserLeafAction> getFlattened(BrowserFileSystemTabModel model, List<BrowserEntry> entries) {
return ALL.stream()
.map(browserAction -> getFlattened(browserAction, model, entries))
.flatMap(List::stream)
.toList();
}
static List<BrowserLeafAction> getFlattened(
BrowserAction browserAction, BrowserFileSystemTabModel model, List<BrowserEntry> entries) {
return browserAction instanceof BrowserLeafAction
? List.of((BrowserLeafAction) browserAction)
: ((BrowserBranchAction) browserAction)
.getBranchingActions(model, entries).stream()
.map(action -> getFlattened(action, model, entries))
.flatMap(List::stream)
.toList();
}
static BrowserLeafAction byId(String id, BrowserFileSystemTabModel model, List<BrowserEntry> entries) {
return getFlattened(model, entries).stream()
.filter(browserAction -> id.equals(browserAction.getId()))
.findAny()
.orElseThrow();
}
default List<BrowserEntry> resolveFilesIfNeeded(List<BrowserEntry> selected) {
return automaticallyResolveLinks()
? selected.stream()
.map(browserEntry ->
new BrowserEntry(browserEntry.getRawFileEntry().resolved(), browserEntry.getModel()))
.toList()
: selected;
}
MenuItem toMenuItem(BrowserFileSystemTabModel model, List<BrowserEntry> selected);
default void init(BrowserFileSystemTabModel model) throws Exception {}
default String getProFeatureId() {
return null;
}
default Node getIcon(BrowserFileSystemTabModel model, List<BrowserEntry> entries) {
return null;
}
default Category getCategory() {
return null;
}
default KeyCombination getShortcut() {
return null;
}
default boolean acceptsEmptySelection() {
return false;
}
ObservableValue<String> getName(BrowserFileSystemTabModel model, List<BrowserEntry> entries);
default boolean isApplicable(BrowserFileSystemTabModel model, List<BrowserEntry> entries) {
return true;
}
default boolean automaticallyResolveLinks() {
return true;
}
default boolean isActive(BrowserFileSystemTabModel model, List<BrowserEntry> entries) {
return true;
}
enum Category {
CUSTOM,
OPEN,
NATIVE,
COPY_PASTE,
MUTATION
}
class Loader implements ModuleLayerLoader {
@Override
public void init(ModuleLayer layer) {
ALL.addAll(ServiceLoader.load(layer, BrowserAction.class).stream()
.map(actionProviderProvider -> actionProviderProvider.get())
.filter(provider -> {
try {
return true;
} catch (Throwable e) {
ErrorEvent.fromThrowable(e).handle();
return false;
}
})
.toList());
}
}
}

View file

@ -0,0 +1,25 @@
package io.xpipe.app.browser.action;
import io.xpipe.app.browser.file.BrowserEntry;
import java.util.List;
public class BrowserActionFormatter {
public static String filesArgument(List<BrowserEntry> entries) {
return entries.size() == 1 ? entries.getFirst().getFileName() : "(" + entries.size() + ")";
}
public static String centerEllipsis(String input, int length) {
if (input == null) {
return "";
}
if (input.length() <= length) {
return input;
}
var half = (length / 2) - 5;
return input.substring(0, half) + " ... " + input.substring(input.length() - half);
}
}

View file

@ -0,0 +1,22 @@
package io.xpipe.app.browser.action;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.file.BrowserFileSystemTabModel;
import java.util.List;
public interface BrowserApplicationPathAction extends BrowserAction {
String getExecutable();
@Override
default void init(BrowserFileSystemTabModel model) {
// Cache result for later calls
model.getCache().isApplicationInPath(getExecutable());
}
@Override
default boolean isActive(BrowserFileSystemTabModel model, List<BrowserEntry> entries) {
return model.getCache().isApplicationInPath(getExecutable());
}
}

View file

@ -0,0 +1,41 @@
package io.xpipe.app.browser.action;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.file.BrowserFileSystemTabModel;
import io.xpipe.app.util.LicenseProvider;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuItem;
import org.kordamp.ikonli.javafx.FontIcon;
import java.util.List;
public interface BrowserBranchAction extends BrowserAction {
default MenuItem toMenuItem(BrowserFileSystemTabModel model, List<BrowserEntry> selected) {
var m = new Menu(getName(model, selected).getValue() + " ...");
for (var sub : getBranchingActions(model, selected)) {
var subselected = resolveFilesIfNeeded(selected);
if (!sub.isApplicable(model, subselected)) {
continue;
}
m.getItems().add(sub.toMenuItem(model, subselected));
}
var graphic = getIcon(model, selected);
if (graphic != null) {
m.setGraphic(graphic);
}
m.setDisable(!isActive(model, selected));
if (getProFeatureId() != null
&& !LicenseProvider.get().getFeature(getProFeatureId()).isSupported()) {
m.setDisable(true);
m.setGraphic(new FontIcon("mdi2p-professional-hexagon"));
}
return m;
}
List<? extends BrowserAction> getBranchingActions(BrowserFileSystemTabModel model, List<BrowserEntry> entries);
}

View file

@ -0,0 +1,113 @@
package io.xpipe.app.browser.action;
import io.xpipe.app.browser.file.BrowserEntry;
import io.xpipe.app.browser.file.BrowserFileSystemTabModel;
import io.xpipe.app.comp.base.TooltipAugment;
import io.xpipe.app.util.BindingsHelper;
import io.xpipe.app.util.BooleanScope;
import io.xpipe.app.util.LicenseProvider;
import io.xpipe.app.util.ThreadHelper;
import javafx.scene.control.Button;
import javafx.scene.control.MenuItem;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Region;
import org.kordamp.ikonli.javafx.FontIcon;
import java.util.List;
public interface BrowserLeafAction extends BrowserAction {
void execute(BrowserFileSystemTabModel model, List<BrowserEntry> entries) throws Exception;
default Button toButton(Region root, BrowserFileSystemTabModel model, List<BrowserEntry> selected) {
var b = new Button();
b.setOnAction(event -> {
ThreadHelper.runFailableAsync(() -> {
BooleanScope.executeExclusive(model.getBusy(), () -> {
if (model.getFileSystem() == null) {
return;
}
// Start shell in case we exited
model.getFileSystem().getShell().orElseThrow().start();
execute(model, selected);
});
});
event.consume();
});
var name = getName(model, selected);
new TooltipAugment<>(name, getShortcut()).augment(b);
var graphic = getIcon(model, selected);
if (graphic != null) {
b.setGraphic(graphic);
}
b.setMnemonicParsing(false);
b.accessibleTextProperty().bind(name);
root.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
if (getShortcut() != null && getShortcut().match(event)) {
b.fire();
event.consume();
}
});
b.setDisable(!isActive(model, selected));
model.getCurrentPath().addListener((observable, oldValue, newValue) -> {
b.setDisable(!isActive(model, selected));
});
if (getProFeatureId() != null
&& !LicenseProvider.get().getFeature(getProFeatureId()).isSupported()) {
b.setDisable(true);
b.setGraphic(new FontIcon("mdi2p-professional-hexagon"));
}
return b;
}
default MenuItem toMenuItem(BrowserFileSystemTabModel model, List<BrowserEntry> selected) {
var name = getName(model, selected);
var mi = new MenuItem();
mi.textProperty().bind(BindingsHelper.map(name, s -> {
if (getProFeatureId() != null) {
return LicenseProvider.get().getFeature(getProFeatureId()).suffix(s);
}
return s;
}));
mi.setOnAction(event -> {
ThreadHelper.runFailableAsync(() -> {
BooleanScope.executeExclusive(model.getBusy(), () -> {
if (model.getFileSystem() == null) {
return;
}
// Start shell in case we exited
model.getFileSystem().getShell().orElseThrow().start();
execute(model, selected);
});
});
event.consume();
});
if (getShortcut() != null) {
mi.setAccelerator(getShortcut());
}
var graphic = getIcon(model, selected);
if (graphic != null) {
mi.setGraphic(graphic);
}
mi.setMnemonicParsing(false);
mi.setDisable(!isActive(model, selected));
if (getProFeatureId() != null
&& !LicenseProvider.get().getFeature(getProFeatureId()).isSupported()) {
mi.setDisable(true);
}
return mi;
}
default String getId() {
return null;
}
}

View file

@ -0,0 +1,116 @@
package io.xpipe.app.browser.file;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.core.window.AppWindowHelper;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.core.store.FileEntry;
import io.xpipe.core.store.FileKind;
import io.xpipe.core.store.FilePath;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonBar;
import javafx.scene.control.ButtonType;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.stream.Collectors;
public class BrowserAlerts {
public static FileConflictChoice showFileConflictAlert(String file, boolean multiple) {
var map = new LinkedHashMap<ButtonType, FileConflictChoice>();
map.put(new ButtonType(AppI18n.get("cancel"), ButtonBar.ButtonData.CANCEL_CLOSE), FileConflictChoice.CANCEL);
if (multiple) {
map.put(new ButtonType(AppI18n.get("skip"), ButtonBar.ButtonData.OTHER), FileConflictChoice.SKIP);
map.put(new ButtonType(AppI18n.get("skipAll"), ButtonBar.ButtonData.OTHER), FileConflictChoice.SKIP_ALL);
}
map.put(new ButtonType(AppI18n.get("replace"), ButtonBar.ButtonData.OTHER), FileConflictChoice.REPLACE);
if (multiple) {
map.put(
new ButtonType(AppI18n.get("replaceAll"), ButtonBar.ButtonData.OTHER),
FileConflictChoice.REPLACE_ALL);
}
map.put(new ButtonType(AppI18n.get("rename"), ButtonBar.ButtonData.OTHER), FileConflictChoice.RENAME);
if (multiple) {
map.put(
new ButtonType(AppI18n.get("renameAll"), ButtonBar.ButtonData.OTHER),
FileConflictChoice.RENAME_ALL);
}
var w = multiple ? 700 : 400;
return AppWindowHelper.showBlockingAlert(alert -> {
alert.setTitle(AppI18n.get("fileConflictAlertTitle"));
alert.setHeaderText(AppI18n.get("fileConflictAlertHeader"));
alert.setAlertType(Alert.AlertType.CONFIRMATION);
alert.getButtonTypes().clear();
alert.getDialogPane()
.setContent(AppWindowHelper.alertContentText(
AppI18n.get(
multiple ? "fileConflictAlertContentMultiple" : "fileConflictAlertContent",
file),
w - 50));
alert.getDialogPane().setMinWidth(w);
alert.getDialogPane().setPrefWidth(w);
alert.getDialogPane().setMaxWidth(w);
map.sequencedKeySet()
.forEach(buttonType -> alert.getButtonTypes().add(buttonType));
})
.map(map::get)
.orElse(FileConflictChoice.CANCEL);
}
public static boolean showMoveAlert(List<FileEntry> source, FileEntry target) {
if (source.stream().noneMatch(entry -> entry.getKind() == FileKind.DIRECTORY)) {
return true;
}
return AppWindowHelper.showBlockingAlert(alert -> {
alert.setTitle(AppI18n.get("moveAlertTitle"));
alert.setHeaderText(AppI18n.get("moveAlertHeader", source.size(), target.getPath()));
alert.getDialogPane()
.setContent(AppWindowHelper.alertContentText(getSelectedElementsString(source)));
alert.setAlertType(Alert.AlertType.CONFIRMATION);
})
.map(b -> b.getButtonData().isDefaultButton())
.orElse(false);
}
public static boolean showDeleteAlert(List<FileEntry> source) {
if (!AppPrefs.get().confirmDeletions().get()
&& source.stream().noneMatch(entry -> entry.getKind() == FileKind.DIRECTORY)) {
return true;
}
return AppWindowHelper.showBlockingAlert(alert -> {
alert.setTitle(AppI18n.get("deleteAlertTitle"));
alert.setHeaderText(AppI18n.get("deleteAlertHeader", source.size()));
alert.getDialogPane()
.setContent(AppWindowHelper.alertContentText(getSelectedElementsString(source)));
alert.setAlertType(Alert.AlertType.CONFIRMATION);
})
.map(b -> b.getButtonData().isDefaultButton())
.orElse(false);
}
private static String getSelectedElementsString(List<FileEntry> source) {
var namesHeader = AppI18n.get("selectedElements");
var names = namesHeader + "\n"
+ source.stream()
.limit(10)
.map(entry -> "- " + new FilePath(entry.getPath()).getFileName())
.collect(Collectors.joining("\n"));
if (source.size() > 10) {
names += "\n+ " + (source.size() - 10) + " ...";
}
return names;
}
public enum FileConflictChoice {
CANCEL,
SKIP,
SKIP_ALL,
REPLACE,
REPLACE_ALL,
RENAME,
RENAME_ALL
}
}

View file

@ -0,0 +1,90 @@
package io.xpipe.app.browser.file;
import io.xpipe.app.comp.SimpleComp;
import io.xpipe.app.util.PlatformThread;
import io.xpipe.core.store.FileNames;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBase;
import javafx.scene.control.Label;
import javafx.scene.layout.Region;
import javafx.util.Callback;
import atlantafx.base.controls.Breadcrumbs;
import java.util.ArrayList;
public class BrowserBreadcrumbBar extends SimpleComp {
private final BrowserFileSystemTabModel model;
public BrowserBreadcrumbBar(BrowserFileSystemTabModel model) {
this.model = model;
}
@Override
protected Region createSimple() {
Callback<Breadcrumbs.BreadCrumbItem<String>, ButtonBase> crumbFactory = crumb -> {
var name = crumb.getValue().equals("/") ? "/" : FileNames.getFileName(crumb.getValue());
var btn = new Button(name, null);
btn.setMnemonicParsing(false);
btn.setFocusTraversable(false);
return btn;
};
return createBreadcrumbs(crumbFactory, null);
}
private Region createBreadcrumbs(
Callback<Breadcrumbs.BreadCrumbItem<String>, ButtonBase> crumbFactory,
Callback<Breadcrumbs.BreadCrumbItem<String>, ? extends Node> dividerFactory) {
var breadcrumbs = new Breadcrumbs<String>();
breadcrumbs.setMinWidth(0);
PlatformThread.sync(model.getCurrentPath()).subscribe(val -> {
if (val == null) {
breadcrumbs.setSelectedCrumb(null);
return;
}
var sc = model.getFileSystem().getShell();
if (sc.isEmpty()) {
breadcrumbs.setDividerFactory(item -> item != null && !item.isLast() ? new Label("/") : null);
} else {
breadcrumbs.setDividerFactory(item -> {
if (item == null) {
return null;
}
if (item.isFirst() && item.getValue().equals("/")) {
return new Label("");
}
return new Label(sc.get().getOsType().getFileSystemSeparator());
});
}
var elements = FileNames.splitHierarchy(val);
var modifiedElements = new ArrayList<>(elements);
if (val.startsWith("/")) {
modifiedElements.addFirst("/");
}
Breadcrumbs.BreadCrumbItem<String> items =
Breadcrumbs.buildTreeModel(modifiedElements.toArray(String[]::new));
breadcrumbs.setSelectedCrumb(items);
});
if (crumbFactory != null) {
breadcrumbs.setCrumbFactory(crumbFactory);
}
if (dividerFactory != null) {
breadcrumbs.setDividerFactory(dividerFactory);
}
breadcrumbs.selectedCrumbProperty().addListener((obs, old, val) -> {
model.cdAsync(val != null ? val.getValue() : null);
});
return breadcrumbs;
}
}

View file

@ -0,0 +1,136 @@
package io.xpipe.app.browser.file;
import io.xpipe.app.ext.ProcessControlProvider;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.util.ThreadHelper;
import io.xpipe.core.store.FileEntry;
import io.xpipe.core.util.FailableRunnable;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DataFormat;
import javafx.scene.input.Dragboard;
import lombok.SneakyThrows;
import lombok.Value;
import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.io.File;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
public class BrowserClipboard {
public static final Property<Instance> currentCopyClipboard = new SimpleObjectProperty<>();
public static Instance currentDragClipboard;
private static final DataFormat DATA_FORMAT = new DataFormat("application/xpipe-file-list");
static {
Toolkit.getDefaultToolkit()
.getSystemClipboard()
.addFlavorListener(e -> ThreadHelper.runFailableAsync(new FailableRunnable<>() {
@Override
@SuppressWarnings("unchecked")
public void run() {
Clipboard clipboard = (Clipboard) e.getSource();
try {
if (!clipboard.isDataFlavorAvailable(DataFlavor.javaFileListFlavor)) {
return;
}
List<File> data = (List<File>) clipboard.getData(DataFlavor.javaFileListFlavor);
// Sometimes file data can contain invalid chars. Why?
var files = data.stream()
.filter(file ->
file.toString().chars().noneMatch(value -> Character.isISOControl(value)))
.map(f -> f.toPath())
.toList();
if (files.size() == 0) {
return;
}
var entries = new ArrayList<BrowserEntry>();
for (Path file : files) {
entries.add(BrowserLocalFileSystem.getLocalBrowserEntry(file));
}
currentCopyClipboard.setValue(
new Instance(UUID.randomUUID(), null, entries, BrowserFileTransferMode.COPY));
} catch (Exception e) {
ErrorEvent.fromThrowable(e).expected().omit().handle();
}
}
}));
}
@SneakyThrows
public static ClipboardContent startDrag(
FileEntry base, List<BrowserEntry> selected, BrowserFileTransferMode mode) {
if (selected.isEmpty()) {
return null;
}
var content = new ClipboardContent();
var id = UUID.randomUUID();
currentDragClipboard = new Instance(id, base, new ArrayList<>(selected), mode);
content.put(DATA_FORMAT, currentDragClipboard.toClipboardString());
return content;
}
@SneakyThrows
public static void startCopy(FileEntry base, List<BrowserEntry> selected) {
if (selected.isEmpty()) {
currentCopyClipboard.setValue(null);
return;
}
var id = UUID.randomUUID();
currentCopyClipboard.setValue(new Instance(id, base, new ArrayList<>(selected), BrowserFileTransferMode.COPY));
}
public static Instance retrieveCopy() {
return currentCopyClipboard.getValue();
}
public static Instance retrieveDrag(Dragboard dragboard) {
if (currentDragClipboard == null) {
return null;
}
try {
var s = dragboard.getContent(DATA_FORMAT);
if (s != null && s.equals(currentDragClipboard.toClipboardString())) {
var current = currentDragClipboard;
currentDragClipboard = null;
return current;
}
} catch (Exception ex) {
return null;
}
return null;
}
@Value
public static class Instance {
UUID uuid;
FileEntry baseDirectory;
List<BrowserEntry> entries;
BrowserFileTransferMode mode;
public String toClipboardString() {
return entries.stream()
.map(fileEntry -> "\"" + fileEntry.getRawFileEntry().getPath() + "\"")
.collect(Collectors.joining(ProcessControlProvider.get()
.getEffectiveLocalDialect()
.getNewLine()
.getNewLineString()));
}
}
}

View file

@ -0,0 +1,95 @@
package io.xpipe.app.browser.file;
import io.xpipe.app.comp.Comp;
import io.xpipe.app.comp.CompStructure;
import io.xpipe.app.comp.SimpleComp;
import io.xpipe.app.comp.store.*;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.app.util.PlatformThread;
import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.css.PseudoClass;
import javafx.scene.control.Button;
import javafx.scene.layout.Region;
import java.util.HashSet;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
public final class BrowserConnectionListComp extends SimpleComp {
private static final PseudoClass SELECTED = PseudoClass.getPseudoClass("selected");
private final ObservableValue<DataStoreEntry> selected;
private final Predicate<StoreEntryWrapper> applicable;
private final BiConsumer<StoreEntryWrapper, BooleanProperty> action;
private final Property<StoreCategoryWrapper> category;
private final Property<String> filter;
public BrowserConnectionListComp(
ObservableValue<DataStoreEntry> selected,
Predicate<StoreEntryWrapper> applicable,
BiConsumer<StoreEntryWrapper, BooleanProperty> action,
Property<StoreCategoryWrapper> category,
Property<String> filter) {
this.selected = selected;
this.applicable = applicable;
this.action = action;
this.category = category;
this.filter = filter;
}
@Override
protected Region createSimple() {
var busyEntries = FXCollections.<StoreSection>observableSet(new HashSet<>());
BiConsumer<StoreSection, Comp<CompStructure<Button>>> augment = (s, comp) -> {
comp.disable(Bindings.createBooleanBinding(
() -> {
return busyEntries.contains(s) || !applicable.test(s.getWrapper());
},
busyEntries));
comp.apply(struc -> {
selected.addListener((observable, oldValue, newValue) -> {
PlatformThread.runLaterIfNeeded(() -> {
struc.get()
.pseudoClassStateChanged(
SELECTED,
newValue != null
&& newValue.equals(
s.getWrapper().getEntry()));
});
});
});
};
var section = new StoreSectionMiniComp(
StoreSection.createTopLevel(
StoreViewState.get().getAllEntries(),
this::filter,
filter,
category,
StoreViewState.get().getEntriesListUpdateObservable()),
augment,
selectedAction -> {
BooleanProperty busy = new SimpleBooleanProperty(false);
action.accept(selectedAction.getWrapper(), busy);
busy.addListener((observable, oldValue, newValue) -> {
if (newValue) {
busyEntries.add(selectedAction);
} else {
busyEntries.remove(selectedAction);
}
});
});
var r = section.vgrow().createRegion();
r.getStyleClass().add("bookmark-list");
return r;
}
private boolean filter(StoreEntryWrapper w) {
return applicable.test(w);
}
}

View file

@ -0,0 +1,60 @@
package io.xpipe.app.browser.file;
import io.xpipe.app.comp.SimpleComp;
import io.xpipe.app.comp.base.FilterComp;
import io.xpipe.app.comp.base.HorizontalComp;
import io.xpipe.app.comp.store.StoreCategoryWrapper;
import io.xpipe.app.comp.store.StoreViewState;
import io.xpipe.app.core.AppFontSizes;
import io.xpipe.app.util.DataStoreCategoryChoiceComp;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.scene.layout.Region;
import atlantafx.base.theme.Styles;
import lombok.Getter;
import java.util.List;
@Getter
public final class BrowserConnectionListFilterComp extends SimpleComp {
private final Property<StoreCategoryWrapper> category =
new SimpleObjectProperty<>(StoreViewState.get().getActiveCategory().getValue());
private final Property<String> filter = new SimpleStringProperty();
@Override
protected Region createSimple() {
var category = new DataStoreCategoryChoiceComp(
StoreViewState.get().getAllConnectionsCategory(),
StoreViewState.get().getActiveCategory(),
this.category)
.styleClass(Styles.LEFT_PILL)
.apply(struc -> {
AppFontSizes.base(struc.get());
});
var filter = new FilterComp(this.filter)
.styleClass(Styles.RIGHT_PILL)
.minWidth(0)
.hgrow()
.apply(struc -> {
AppFontSizes.base(struc.get());
});
var top = new HorizontalComp(List.of(category, filter))
.apply(struc -> struc.get().setFillHeight(true))
.apply(struc -> {
var first = ((Region) struc.get().getChildren().get(0));
var second = ((Region) struc.get().getChildren().get(1));
first.prefHeightProperty().bind(second.heightProperty());
first.minHeightProperty().bind(second.heightProperty());
first.maxHeightProperty().bind(second.heightProperty());
AppFontSizes.xl(struc.get());
})
.styleClass("bookmarks-header")
.createRegion();
return top;
}
}

View file

@ -0,0 +1,85 @@
package io.xpipe.app.browser.file;
import io.xpipe.app.browser.action.BrowserAction;
import io.xpipe.app.core.AppFontSizes;
import io.xpipe.app.util.InputHelper;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.SeparatorMenuItem;
import java.util.ArrayList;
import java.util.List;
public final class BrowserContextMenu extends ContextMenu {
private final BrowserFileSystemTabModel model;
private final BrowserEntry source;
private final boolean quickAccess;
public BrowserContextMenu(BrowserFileSystemTabModel model, BrowserEntry source, boolean quickAccess) {
this.model = model;
this.source = source;
this.quickAccess = quickAccess;
createMenu();
}
private void createMenu() {
AppFontSizes.lg(getStyleableNode());
InputHelper.onLeft(this, false, e -> {
hide();
e.consume();
});
var empty = source == null;
var selected = new ArrayList<>(
empty
? List.of(new BrowserEntry(model.getCurrentDirectory(), model.getFileList()))
: quickAccess ? List.of() : model.getFileList().getSelection());
if (source != null && !selected.contains(source)) {
selected.add(source);
}
if (model.isClosed()) {
return;
}
for (BrowserAction.Category cat : BrowserAction.Category.values()) {
var all = BrowserAction.ALL.stream()
.filter(browserAction -> browserAction.getCategory() == cat)
.filter(browserAction -> {
if (model.isClosed()) {
return false;
}
var used = browserAction.resolveFilesIfNeeded(selected);
if (!browserAction.isApplicable(model, used)) {
return false;
}
if (!browserAction.acceptsEmptySelection() && empty) {
return false;
}
return true;
})
.toList();
if (all.size() == 0) {
continue;
}
if (getItems().size() > 0) {
getItems().add(new SeparatorMenuItem());
}
for (BrowserAction a : all) {
if (model.isClosed()) {
return;
}
var used = a.resolveFilesIfNeeded(selected);
getItems().add(a.toMenuItem(model, used));
}
}
}
}

View file

@ -0,0 +1,79 @@
package io.xpipe.app.browser.file;
import io.xpipe.app.browser.icon.BrowserIconDirectoryType;
import io.xpipe.app.browser.icon.BrowserIconFileType;
import io.xpipe.core.store.FileEntry;
import io.xpipe.core.store.FileKind;
import io.xpipe.core.store.FileNames;
import lombok.Getter;
@Getter
public class BrowserEntry {
private final BrowserFileListModel model;
private final FileEntry rawFileEntry;
private final BrowserIconFileType fileType;
private final BrowserIconDirectoryType directoryType;
public BrowserEntry(FileEntry rawFileEntry, BrowserFileListModel model) {
this.rawFileEntry = rawFileEntry;
this.model = model;
this.fileType = fileType(rawFileEntry);
this.directoryType = directoryType(rawFileEntry);
}
private static BrowserIconFileType fileType(FileEntry rawFileEntry) {
if (rawFileEntry == null) {
return null;
}
rawFileEntry = rawFileEntry.resolved();
if (rawFileEntry.getKind() != FileKind.FILE) {
return null;
}
for (var f : BrowserIconFileType.getAll()) {
if (f.matches(rawFileEntry)) {
return f;
}
}
return null;
}
private static BrowserIconDirectoryType directoryType(FileEntry rawFileEntry) {
if (rawFileEntry == null) {
return null;
}
rawFileEntry = rawFileEntry.resolved();
if (rawFileEntry.getKind() != FileKind.DIRECTORY) {
return null;
}
for (var f : BrowserIconDirectoryType.getAll()) {
if (f.matches(rawFileEntry)) {
return f;
}
}
return null;
}
public String getIcon() {
if (fileType != null) {
return fileType.getIcon();
} else if (directoryType != null) {
return directoryType.getIcon(rawFileEntry);
} else {
return rawFileEntry != null && rawFileEntry.resolved().getKind() == FileKind.DIRECTORY
? "browser/default_folder.svg"
: "browser/default_file.svg";
}
}
public String getFileName() {
return FileNames.getFileName(getRawFileEntry().getPath());
}
}

View file

@ -0,0 +1,639 @@
package io.xpipe.app.browser.file;
import io.xpipe.app.browser.action.BrowserAction;
import io.xpipe.app.comp.SimpleComp;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.util.*;
import io.xpipe.core.process.OsType;
import io.xpipe.core.store.FileEntry;
import io.xpipe.core.store.FileInfo;
import io.xpipe.core.store.FileKind;
import io.xpipe.core.store.FileNames;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.css.PseudoClass;
import javafx.geometry.Bounds;
import javafx.scene.control.*;
import javafx.scene.control.skin.TableViewSkin;
import javafx.scene.control.skin.VirtualFlow;
import javafx.scene.input.*;
import javafx.scene.layout.Region;
import atlantafx.base.theme.Styles;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import static io.xpipe.app.util.HumanReadableFormat.byteCount;
import static javafx.scene.control.TableColumn.SortType.ASCENDING;
public final class BrowserFileListComp extends SimpleComp {
private static final PseudoClass HIDDEN = PseudoClass.getPseudoClass("hidden");
private static final PseudoClass EMPTY = PseudoClass.getPseudoClass("empty");
private static final PseudoClass FILE = PseudoClass.getPseudoClass("file");
private static final PseudoClass FOLDER = PseudoClass.getPseudoClass("folder");
private static final PseudoClass DRAG = PseudoClass.getPseudoClass("drag");
private static final PseudoClass DRAG_OVER = PseudoClass.getPseudoClass("drag-over");
private static final PseudoClass DRAG_INTO_CURRENT = PseudoClass.getPseudoClass("drag-into-current");
private final BrowserFileListModel fileList;
private final StringProperty typedSelection = new SimpleStringProperty("");
private final DoubleProperty ownerWidth = new SimpleDoubleProperty();
public BrowserFileListComp(BrowserFileListModel fileList) {
this.fileList = fileList;
}
@Override
protected Region createSimple() {
return createTable();
}
@SuppressWarnings("unchecked")
private TableView<BrowserEntry> createTable() {
var filenameCol = new TableColumn<BrowserEntry, String>();
filenameCol.textProperty().bind(AppI18n.observable("name"));
filenameCol.setCellValueFactory(param -> new SimpleStringProperty(
param.getValue() != null
? FileNames.getFileName(
param.getValue().getRawFileEntry().getPath())
: null));
filenameCol.setComparator(Comparator.comparing(String::toLowerCase));
filenameCol.setSortType(ASCENDING);
filenameCol.setCellFactory(col ->
new BrowserFileListNameCell(fileList, typedSelection, fileList.getEditing(), col.getTableView()));
filenameCol.setReorderable(false);
filenameCol.setResizable(false);
var sizeCol = new TableColumn<BrowserEntry, Number>();
sizeCol.textProperty().bind(AppI18n.observable("size"));
sizeCol.setCellValueFactory(param -> new SimpleLongProperty(
param.getValue().getRawFileEntry().resolved().getSize()));
sizeCol.setCellFactory(col -> new FileSizeCell());
sizeCol.setResizable(false);
sizeCol.setPrefWidth(120);
sizeCol.setReorderable(false);
var mtimeCol = new TableColumn<BrowserEntry, Instant>();
mtimeCol.textProperty().bind(AppI18n.observable("modified"));
mtimeCol.setCellValueFactory(param -> new SimpleObjectProperty<>(
param.getValue().getRawFileEntry().resolved().getDate()));
mtimeCol.setCellFactory(col -> new FileTimeCell());
mtimeCol.setResizable(false);
mtimeCol.setPrefWidth(150);
mtimeCol.setReorderable(false);
var modeCol = new TableColumn<BrowserEntry, String>();
modeCol.textProperty().bind(AppI18n.observable("attributes"));
modeCol.setCellValueFactory(param -> new SimpleObjectProperty<>(
param.getValue().getRawFileEntry().resolved().getInfo() instanceof FileInfo.Unix u
? u.getPermissions()
: null));
modeCol.setCellFactory(col -> new FileModeCell());
modeCol.setResizable(false);
modeCol.setPrefWidth(120);
modeCol.setSortable(false);
modeCol.setReorderable(false);
var ownerCol = new TableColumn<BrowserEntry, String>();
ownerCol.textProperty().bind(AppI18n.observable("owner"));
ownerCol.setCellValueFactory(param -> {
return new SimpleObjectProperty<>(formatOwner(param.getValue()));
});
ownerCol.setCellFactory(col -> new FileOwnerCell());
ownerCol.setSortable(false);
ownerCol.setReorderable(false);
ownerCol.setResizable(false);
var table = new TableView<BrowserEntry>();
table.setSkin(new TableViewSkin<>(table));
table.setAccessibleText("Directory contents");
table.setPlaceholder(new Region());
table.getStyleClass().add(Styles.STRIPED);
table.getColumns().setAll(filenameCol, mtimeCol, modeCol, ownerCol, sizeCol);
table.getSortOrder().add(filenameCol);
table.setFocusTraversable(true);
table.setSortPolicy(param -> {
fileList.setComparator(table.getComparator());
return true;
});
table.setFixedCellSize(30.0);
prepareColumnVisibility(table, ownerCol, filenameCol);
prepareTableScrollFix(table);
prepareTableSelectionModel(table);
prepareTableShortcuts(table);
prepareTableEntries(table);
prepareTableChanges(table, filenameCol, mtimeCol, modeCol, ownerCol);
prepareTypedSelectionModel(table);
return table;
}
private static void prepareTableScrollFix(TableView<BrowserEntry> table) {
table.lookupAll(".scroll-bar").stream()
.filter(node -> node.getPseudoClassStates().contains(PseudoClass.getPseudoClass("horizontal")))
.findFirst()
.ifPresent(node -> {
Region region = (Region) node;
region.setMinHeight(0);
region.setPrefHeight(0);
region.setMaxHeight(0);
});
}
private void prepareColumnVisibility(
TableView<BrowserEntry> table,
TableColumn<BrowserEntry, String> ownerCol,
TableColumn<BrowserEntry, String> filenameCol) {
var os = fileList.getFileSystemModel()
.getFileSystem()
.getShell()
.map(shellControl -> shellControl.getOsType())
.orElse(null);
table.widthProperty().subscribe((newValue) -> {
if (os != OsType.WINDOWS && os != OsType.MACOS) {
ownerCol.setVisible(newValue.doubleValue() > 1000);
}
var width = getFilenameWidth(table);
filenameCol.setPrefWidth(width);
});
}
private double getFilenameWidth(TableView<?> tableView) {
var sum = tableView.getColumns().stream()
.filter(tableColumn -> tableColumn.isVisible()
&& tableView.getColumns().indexOf(tableColumn) != 0)
.mapToDouble(value -> value.getPrefWidth())
.sum()
+ 7;
return tableView.getWidth() - sum;
}
private String formatOwner(BrowserEntry param) {
FileInfo.Unix unix = param.getRawFileEntry().resolved().getInfo() instanceof FileInfo.Unix u ? u : null;
if (unix == null) {
return null;
}
var m = fileList.getFileSystemModel();
var user = unix.getUser() != null
? unix.getUser()
: m.getCache().getUsers().getOrDefault(unix.getUid(), "?");
var group = unix.getGroup() != null
? unix.getGroup()
: m.getCache().getGroups().getOrDefault(unix.getGid(), "?");
var uid = String.valueOf(
unix.getUid() != null ? unix.getUid() : m.getCache().getUidForUser(user));
var gid = String.valueOf(
unix.getGid() != null ? unix.getGid() : m.getCache().getGidForGroup(group));
if (uid.equals(gid) && user.equals(group)) {
return user + " [" + uid + "]";
}
return user + " [" + uid + "] / " + group + " [" + gid + "]";
}
private void prepareTypedSelectionModel(TableView<BrowserEntry> table) {
AtomicReference<Instant> lastFail = new AtomicReference<>();
table.addEventHandler(KeyEvent.KEY_PRESSED, event -> {
updateTypedSelection(table, lastFail, event, false);
});
table.addEventFilter(MouseEvent.MOUSE_PRESSED, event -> {
typedSelection.set("");
lastFail.set(null);
});
fileList.getFileSystemModel().getCurrentPath().addListener((observable, oldValue, newValue) -> {
typedSelection.set("");
lastFail.set(null);
});
table.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
if (event.getCode() == KeyCode.ESCAPE) {
typedSelection.set("");
lastFail.set(null);
}
});
}
private void updateTypedSelection(
TableView<BrowserEntry> table, AtomicReference<Instant> lastType, KeyEvent event, boolean recursive) {
var typed = event.getText();
if (typed.isEmpty()) {
return;
}
var updated = typedSelection.get() + typed;
var found = fileList.getShown().getValue().stream()
.filter(browserEntry -> browserEntry.getFileName().toLowerCase().startsWith(updated.toLowerCase()))
.findFirst();
if (found.isEmpty()) {
if (typedSelection.get().isEmpty()) {
return;
}
var inCooldown = lastType.get() != null
&& Duration.between(lastType.get(), Instant.now()).toMillis() < 1000;
if (inCooldown) {
lastType.set(Instant.now());
event.consume();
} else {
lastType.set(null);
typedSelection.set("");
table.getSelectionModel().clearSelection();
if (!recursive) {
updateTypedSelection(table, lastType, event, true);
}
}
return;
}
lastType.set(Instant.now());
typedSelection.set(updated);
table.scrollTo(found.get());
table.getSelectionModel().clearAndSelect(fileList.getShown().getValue().indexOf(found.get()));
event.consume();
}
private void prepareTableSelectionModel(TableView<BrowserEntry> table) {
if (!fileList.getSelectionMode().isMultiple()) {
table.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
} else {
table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
}
table.getSelectionModel().setCellSelectionEnabled(false);
var updateFromModel = new BooleanScope(new SimpleBooleanProperty());
table.getSelectionModel().getSelectedItems().addListener((ListChangeListener<? super BrowserEntry>) c -> {
if (updateFromModel.get()) {
return;
}
try (var ignored = updateFromModel) {
// Attempt to preserve ordering. Works at least when selecting single entries
var existing = new HashSet<>(fileList.getSelection());
c.getList().forEach(browserEntry -> {
if (!existing.contains(browserEntry)) {
fileList.getSelection().add(browserEntry);
}
});
fileList.getSelection().removeIf(browserEntry -> !c.getList().contains(browserEntry));
}
});
fileList.getSelection().addListener((ListChangeListener<? super BrowserEntry>) c -> {
var existing = new HashSet<>(table.getSelectionModel().getSelectedItems());
var toApply = new HashSet<>(c.getList());
if (existing.equals(toApply)) {
return;
}
Platform.runLater(() -> {
var tableIndices = table.getSelectionModel().getSelectedItems().stream()
.mapToInt(entry -> table.getItems().indexOf(entry))
.toArray();
var indices = c.getList().stream()
.mapToInt(entry -> table.getItems().indexOf(entry))
.toArray();
if (Arrays.equals(indices, tableIndices)) {
return;
}
if (indices.length == 0) {
table.getSelectionModel().clearSelection();
return;
}
if (indices.length == 1) {
table.getSelectionModel().clearAndSelect(indices[0]);
} else {
table.getSelectionModel().clearSelection();
table.getSelectionModel().selectIndices(indices[0], indices);
}
});
});
}
private void prepareTableShortcuts(TableView<BrowserEntry> table) {
table.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
// Prevent post close events
if (fileList.getFileSystemModel().isClosed()) {
return;
}
var selected = fileList.getSelection();
var action = BrowserAction.getFlattened(fileList.getFileSystemModel(), selected).stream()
.filter(browserAction -> browserAction.isApplicable(fileList.getFileSystemModel(), selected)
&& browserAction.isActive(fileList.getFileSystemModel(), selected))
.filter(browserAction -> browserAction.getShortcut() != null)
.filter(browserAction -> browserAction.getShortcut().match(event))
.findAny();
action.ifPresent(browserAction -> {
// Prevent concurrent modification by creating copy on platform thread
var selectionCopy = new ArrayList<>(selected);
ThreadHelper.runFailableAsync(() -> {
browserAction.execute(fileList.getFileSystemModel(), selectionCopy);
});
event.consume();
});
if (action.isPresent()) {
return;
}
if (event.getCode() == KeyCode.ESCAPE) {
table.getSelectionModel().clearSelection();
event.consume();
}
});
}
private void prepareTableEntries(TableView<BrowserEntry> table) {
var emptyEntry = new BrowserFileListCompEntry(table, table, null, fileList);
table.setOnMouseClicked(e -> {
emptyEntry.onMouseClick(e);
});
table.setOnMouseDragEntered(event -> {
emptyEntry.onMouseDragEntered(event);
});
table.setOnDragOver(event -> {
emptyEntry.onDragOver(event);
});
table.setOnDragEntered(event -> {
emptyEntry.onDragEntered(event);
});
table.setOnDragDetected(event -> {
emptyEntry.startDrag(event);
});
table.setOnDragExited(event -> {
emptyEntry.onDragExited(event);
});
table.setOnDragDropped(event -> {
emptyEntry.onDragDrop(event);
});
table.setOnDragDone(event -> {
emptyEntry.onDragDone(event);
});
// Don't let the list view see this event
// otherwise it unselects everything as it doesn't understand shift clicks
table.addEventFilter(MouseEvent.MOUSE_CLICKED, t -> {
if (t.getButton() == MouseButton.PRIMARY && t.isShiftDown() && t.getClickCount() == 1) {
t.consume();
}
});
table.setRowFactory(param -> {
TableRow<BrowserEntry> row = new TableRow<>();
row.accessibleTextProperty()
.bind(Bindings.createStringBinding(
() -> {
return row.getItem() != null ? row.getItem().getFileName() : null;
},
row.itemProperty()));
row.focusTraversableProperty()
.bind(Bindings.createBooleanBinding(
() -> {
return row.getItem() != null;
},
row.itemProperty()));
var listEntry = Bindings.createObjectBinding(
() -> new BrowserFileListCompEntry(table, row, row.getItem(), fileList), row.itemProperty());
// Don't let the list view see this event
// otherwise it unselects everything as it doesn't understand shift clicks
row.addEventFilter(MouseEvent.MOUSE_PRESSED, t -> {
if (t.getButton() == MouseButton.PRIMARY && t.isShiftDown()) {
listEntry.get().onMouseShiftClick(t);
}
});
row.itemProperty().addListener((observable, oldValue, newValue) -> {
row.pseudoClassStateChanged(DRAG, false);
row.pseudoClassStateChanged(DRAG_OVER, false);
});
row.itemProperty().addListener((observable, oldValue, newValue) -> {
row.pseudoClassStateChanged(EMPTY, newValue == null);
row.pseudoClassStateChanged(
FILE, newValue != null && newValue.getRawFileEntry().getKind() != FileKind.DIRECTORY);
row.pseudoClassStateChanged(
FOLDER, newValue != null && newValue.getRawFileEntry().getKind() == FileKind.DIRECTORY);
});
fileList.getDraggedOverDirectory().addListener((observable, oldValue, newValue) -> {
row.pseudoClassStateChanged(DRAG_OVER, newValue != null && newValue == row.getItem());
});
fileList.getDraggedOverEmpty().addListener((observable, oldValue, newValue) -> {
table.pseudoClassStateChanged(DRAG_INTO_CURRENT, newValue);
});
row.setOnMouseClicked(e -> {
listEntry.get().onMouseClick(e);
});
row.setOnMouseDragEntered(event -> {
listEntry.get().onMouseDragEntered(event);
});
row.setOnDragEntered(event -> {
listEntry.get().onDragEntered(event);
});
row.setOnDragOver(event -> {
borderScroll(table, event);
listEntry.get().onDragOver(event);
});
row.setOnDragDetected(event -> {
listEntry.get().startDrag(event);
});
row.setOnDragExited(event -> {
listEntry.get().onDragExited(event);
});
row.setOnDragDropped(event -> {
listEntry.get().onDragDrop(event);
});
row.setOnDragDone(event -> {
listEntry.get().onDragDone(event);
});
return row;
});
}
private void prepareTableChanges(
TableView<BrowserEntry> table,
TableColumn<BrowserEntry, String> filenameCol,
TableColumn<BrowserEntry, Instant> mtimeCol,
TableColumn<BrowserEntry, String> modeCol,
TableColumn<BrowserEntry, String> ownerCol) {
var lastDir = new SimpleObjectProperty<FileEntry>();
Runnable updateHandler = () -> {
PlatformThread.runLaterIfNeeded(() -> {
var newItems = new ArrayList<>(fileList.getShown().getValue());
table.getItems().clear();
var hasModifiedDate = newItems.size() == 0
|| newItems.stream()
.anyMatch(entry -> entry.getRawFileEntry().getDate() != null);
if (!hasModifiedDate) {
mtimeCol.setVisible(false);
} else {
mtimeCol.setVisible(true);
}
ownerWidth.set(fileList.getAll().getValue().stream()
.map(browserEntry -> formatOwner(browserEntry))
.map(s -> s != null ? s.length() * 9 : 0)
.max(Comparator.naturalOrder())
.orElse(150));
ownerCol.setPrefWidth(ownerWidth.get());
if (fileList.getFileSystemModel().getFileSystem() != null) {
var shell = fileList.getFileSystemModel()
.getFileSystem()
.getShell()
.orElseThrow();
if (OsType.WINDOWS.equals(shell.getOsType()) || OsType.MACOS.equals(shell.getOsType())) {
modeCol.setVisible(false);
ownerCol.setVisible(false);
} else {
modeCol.setVisible(true);
if (table.getWidth() > 1000) {
ownerCol.setVisible(true);
}
}
}
// Sort the list ourselves as sorting the table would incur a lot of cell updates
var obs = FXCollections.observableList(newItems);
table.getItems().setAll(obs);
var width = getFilenameWidth(table);
filenameCol.setPrefWidth(width);
TableViewSkin<?> skin = (TableViewSkin<?>) table.getSkin();
var currentDirectory = fileList.getFileSystemModel().getCurrentDirectory();
if (skin != null && !Objects.equals(lastDir.get(), currentDirectory)) {
VirtualFlow<?> flow = (VirtualFlow<?>) skin.getChildren().get(1);
ScrollBar vbar = (ScrollBar) flow.getChildrenUnmodifiable().get(2);
if (vbar.getValue() != 0.0) {
table.scrollTo(0);
}
}
lastDir.setValue(currentDirectory);
});
};
updateHandler.run();
fileList.getShown().addListener((observable, oldValue, newValue) -> {
// Delay to prevent internal tableview exceptions when sorting
Platform.runLater(updateHandler);
});
fileList.getFileSystemModel().getCurrentPath().addListener((observable, oldValue, newValue) -> {
if (oldValue == null) {
updateHandler.run();
}
});
}
private void borderScroll(TableView<?> tableView, DragEvent event) {
TableViewSkin<?> skin = (TableViewSkin<?>) tableView.getSkin();
if (skin == null) {
return;
}
VirtualFlow<?> flow = (VirtualFlow<?>) skin.getChildren().get(1);
ScrollBar vbar = (ScrollBar) flow.getChildrenUnmodifiable().get(2);
if (!vbar.isVisible()) {
return;
}
double proximity = 100;
Bounds tableBounds = tableView.localToScene(tableView.getBoundsInLocal());
double dragY = event.getSceneY();
// Include table header as well in calculations
double topYProximity = tableBounds.getMinY() + proximity + 20;
double bottomYProximity = tableBounds.getMaxY() - proximity;
// clamp new values between 0 and 1 to prevent scrollbar flicking around at the edges
if (dragY < topYProximity) {
var scrollValue = Math.min(topYProximity - dragY, 100) / 10000.0;
vbar.setValue(Math.max(vbar.getValue() - scrollValue, 0));
} else if (dragY > bottomYProximity) {
var scrollValue = Math.min(dragY - bottomYProximity, 100) / 10000.0;
vbar.setValue(Math.min(vbar.getValue() + scrollValue, 1.0));
}
}
private static class FileSizeCell extends TableCell<BrowserEntry, Number> {
@Override
protected void updateItem(Number fileSize, boolean empty) {
super.updateItem(fileSize, empty);
if (empty || getTableRow() == null || getTableRow().getItem() == null) {
setText(null);
} else {
var path = getTableRow().getItem();
if (path.getRawFileEntry().resolved().getKind() == FileKind.DIRECTORY) {
setText("");
} else {
setText(byteCount(fileSize.longValue()));
}
}
}
}
private static class FileModeCell extends TableCell<BrowserEntry, String> {
@Override
protected void updateItem(String mode, boolean empty) {
super.updateItem(mode, empty);
if (empty || getTableRow() == null || getTableRow().getItem() == null) {
setText(null);
} else {
setText(mode);
}
}
}
private static class FileOwnerCell extends TableCell<BrowserEntry, String> {
public FileOwnerCell() {
setTextOverrun(OverrunStyle.CENTER_ELLIPSIS);
}
@Override
protected void updateItem(String owner, boolean empty) {
super.updateItem(owner, empty);
if (empty || getTableRow() == null || getTableRow().getItem() == null) {
setText(null);
} else {
setText(owner);
}
}
}
private static class FileTimeCell extends TableCell<BrowserEntry, Instant> {
@Override
protected void updateItem(Instant fileTime, boolean empty) {
super.updateItem(fileTime, empty);
if (empty) {
setText(null);
} else {
setText(
fileTime != null
? HumanReadableFormat.date(
fileTime.atZone(ZoneId.systemDefault()).toLocalDateTime())
: "");
}
}
}
}

View file

@ -0,0 +1,338 @@
package io.xpipe.app.browser.file;
import io.xpipe.app.browser.BrowserFullSessionModel;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.util.BooleanScope;
import io.xpipe.app.util.ThreadHelper;
import io.xpipe.core.store.FileKind;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.TableView;
import javafx.scene.image.Image;
import javafx.scene.input.*;
import lombok.Getter;
import java.io.File;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Timer;
import java.util.TimerTask;
@Getter
public class BrowserFileListCompEntry {
public static final Timer DROP_TIMER = new Timer("dnd", true);
private final TableView<BrowserEntry> tv;
private final Node row;
private final BrowserEntry item;
private final BrowserFileListModel model;
private Point2D lastOver = new Point2D(-1, -1);
private TimerTask activeTask;
private ContextMenu lastContextMenu;
public BrowserFileListCompEntry(
TableView<BrowserEntry> tv, Node row, BrowserEntry item, BrowserFileListModel model) {
this.tv = tv;
this.row = row;
this.item = item;
this.model = model;
}
public void onMouseClick(MouseEvent t) {
if (lastContextMenu != null) {
lastContextMenu.hide();
lastContextMenu = null;
}
if (showContextMenu(t)) {
var cm = new BrowserContextMenu(model.getFileSystemModel(), item, false);
cm.show(row, t.getScreenX(), t.getScreenY());
lastContextMenu = cm;
t.consume();
return;
}
if (t.getButton() == MouseButton.BACK) {
ThreadHelper.runFailableAsync(() -> {
BooleanScope.executeExclusive(model.getFileSystemModel().getBusy(), () -> {
model.getFileSystemModel().backSync(1);
});
});
t.consume();
return;
}
if (t.getButton() == MouseButton.FORWARD) {
ThreadHelper.runFailableAsync(() -> {
BooleanScope.executeExclusive(model.getFileSystemModel().getBusy(), () -> {
model.getFileSystemModel().forthSync(1);
});
});
t.consume();
return;
}
if (item == null) {
// Only clear for normal clicks
if (t.isStillSincePress()) {
model.getSelection().clear();
if (tv != null) {
tv.requestFocus();
}
}
t.consume();
return;
}
row.requestFocus();
if (t.getClickCount() == 2 && t.getButton() == MouseButton.PRIMARY) {
model.onDoubleClick(item);
t.consume();
}
t.consume();
}
private boolean showContextMenu(MouseEvent event) {
if (item == null) {
return event.getButton() == MouseButton.SECONDARY;
}
if (item.getRawFileEntry().resolved().getKind() == FileKind.DIRECTORY) {
return event.getButton() == MouseButton.SECONDARY;
}
if (item.getRawFileEntry().resolved().getKind() != FileKind.DIRECTORY) {
return event.getButton() == MouseButton.SECONDARY
|| !AppPrefs.get().editFilesWithDoubleClick().get()
&& event.getButton() == MouseButton.PRIMARY
&& event.getClickCount() == 2;
}
return false;
}
public void onMouseShiftClick(MouseEvent t) {
if (t.getButton() != MouseButton.PRIMARY) {
return;
}
var all = tv.getItems();
var index = item != null ? all.indexOf(item) : all.size() - 1;
var min = Math.min(
index,
tv.getSelectionModel().getSelectedIndices().stream()
.mapToInt(value -> value)
.min()
.orElse(1));
var max = Math.max(
index,
tv.getSelectionModel().getSelectedIndices().stream()
.mapToInt(value -> value)
.max()
.orElse(all.indexOf(item)));
var toSelect = new ArrayList<BrowserEntry>();
for (int i = min; i <= max; i++) {
if (!model.getSelection().contains(model.getShown().getValue().get(i))) {
toSelect.add(model.getShown().getValue().get(i));
}
}
model.getSelection().addAll(toSelect);
t.consume();
}
private boolean acceptsDrop(DragEvent event) {
// Accept drops from outside the app window
if (event.getGestureSource() == null) {
return true;
}
BrowserClipboard.Instance cb = BrowserClipboard.currentDragClipboard;
if (cb == null) {
return false;
}
if (model.getFileSystemModel().getCurrentDirectory() == null) {
return false;
}
if (!Objects.equals(
model.getFileSystemModel().getFileSystem(),
cb.getEntries().getFirst().getRawFileEntry().getFileSystem())) {
return true;
}
// Prevent drag and drops of files into the current directory
if (cb.getBaseDirectory() != null
&& cb.getBaseDirectory()
.getPath()
.equals(model.getFileSystemModel().getCurrentDirectory().getPath())
&& (item == null || item.getRawFileEntry().getKind() != FileKind.DIRECTORY)) {
return false;
}
// Prevent dropping items onto themselves
if (item != null && cb.getEntries().contains(item)) {
return false;
}
return true;
}
public void onDragDrop(DragEvent event) {
model.getDraggedOverEmpty().setValue(false);
model.getDraggedOverDirectory().setValue(null);
// Accept drops from outside the app window
if (event.getGestureSource() == null && event.getDragboard().hasFiles()) {
Dragboard db = event.getDragboard();
var list = db.getFiles().stream().map(File::toPath).toList();
var target = item != null && item.getRawFileEntry().getKind() == FileKind.DIRECTORY
? item.getRawFileEntry()
: model.getFileSystemModel().getCurrentDirectory();
model.getFileSystemModel().dropLocalFilesIntoAsync(target, list);
event.setDropCompleted(true);
event.consume();
}
// Accept drops from inside the app window
if (event.getGestureSource() != null) {
var db = BrowserClipboard.retrieveDrag(event.getDragboard());
if (db == null) {
return;
}
var files = db.getEntries();
var target = item != null && item.getRawFileEntry().getKind() == FileKind.DIRECTORY
? item.getRawFileEntry()
: model.getFileSystemModel().getCurrentDirectory();
model.getFileSystemModel()
.dropFilesIntoAsync(
target,
files.stream()
.map(browserEntry -> browserEntry.getRawFileEntry())
.toList(),
db.getMode());
event.setDropCompleted(true);
event.consume();
}
}
public void onDragExited(DragEvent event) {
if (item != null && item.getRawFileEntry().getKind() == FileKind.DIRECTORY) {
model.getDraggedOverDirectory().setValue(null);
} else {
model.getDraggedOverEmpty().setValue(false);
}
event.consume();
}
public void startDrag(MouseEvent event) {
if (item == null) {
return;
}
if (event.getButton() != MouseButton.PRIMARY) {
return;
}
if (model.getFileSystemModel().getBrowserModel() instanceof BrowserFullSessionModel sessionModel) {
sessionModel.getDraggingFiles().setValue(true);
}
var selected = model.getSelection();
Dragboard db = row.startDragAndDrop(TransferMode.COPY);
db.setContent(BrowserClipboard.startDrag(
model.getFileSystemModel().getCurrentDirectory(),
selected,
event.isAltDown() ? BrowserFileTransferMode.MOVE : BrowserFileTransferMode.NORMAL));
Image image = BrowserFileSelectionListComp.snapshot(selected);
db.setDragView(image, -20, 15);
event.setDragDetect(true);
event.consume();
}
public void onDragDone(DragEvent event) {
if (model.getFileSystemModel().getBrowserModel() instanceof BrowserFullSessionModel sessionModel) {
sessionModel.getDraggingFiles().setValue(false);
event.consume();
}
}
private void acceptDrag(DragEvent event) {
model.getDraggedOverEmpty()
.setValue(item == null || item.getRawFileEntry().getKind() != FileKind.DIRECTORY);
model.getDraggedOverDirectory().setValue(item);
event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
}
private void handleHoverTimer(DragEvent event) {
if (item == null || item.getRawFileEntry().getKind() != FileKind.DIRECTORY) {
return;
}
if (lastOver.getX() == event.getX() && lastOver.getY() == event.getY()) {
return;
}
lastOver = (new Point2D(event.getX(), event.getY()));
activeTask = new TimerTask() {
@Override
public void run() {
if (activeTask != this) {
return;
}
if (item != model.getDraggedOverDirectory().getValue()) {
return;
}
model.getFileSystemModel().cdAsync(item.getRawFileEntry().getPath());
}
};
DROP_TIMER.schedule(activeTask, 1200);
}
public void onDragEntered(DragEvent event) {
event.consume();
if (!acceptsDrop(event)) {
return;
}
acceptDrag(event);
}
@SuppressWarnings("unchecked")
public void onMouseDragEntered(MouseDragEvent event) {
event.consume();
if (model.getFileSystemModel().getCurrentDirectory() == null) {
return;
}
if (item == null) {
return;
}
var tv = ((TableView<BrowserEntry>)
row.getParent().getParent().getParent().getParent());
tv.getSelectionModel().select(item);
}
public void onDragOver(DragEvent event) {
event.consume();
if (!acceptsDrop(event)) {
return;
}
acceptDrag(event);
handleHoverTimer(event);
}
}

View file

@ -0,0 +1,129 @@
package io.xpipe.app.browser.file;
import io.xpipe.app.comp.Comp;
import io.xpipe.app.comp.CompStructure;
import io.xpipe.app.comp.base.TextFieldComp;
import io.xpipe.app.comp.base.TooltipAugment;
import io.xpipe.app.util.InputHelper;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.scene.layout.HBox;
import atlantafx.base.theme.Styles;
import org.kordamp.ikonli.javafx.FontIcon;
public class BrowserFileListFilterComp extends Comp<BrowserFileListFilterComp.Structure> {
private final BrowserFileSystemTabModel model;
private final Property<String> filterString;
public BrowserFileListFilterComp(BrowserFileSystemTabModel model, Property<String> filterString) {
this.model = model;
this.filterString = filterString;
}
@Override
public Structure createBase() {
var expanded = new SimpleBooleanProperty();
var text = new TextFieldComp(filterString, false).createStructure().get();
var button = new Button();
button.minWidthProperty().bind(button.heightProperty());
button.setFocusTraversable(true);
InputHelper.onExactKeyCode(text, KeyCode.ESCAPE, true, keyEvent -> {
if (!expanded.get()) {
return;
}
text.clear();
button.fire();
keyEvent.consume();
});
new TooltipAugment<>("app.search", new KeyCodeCombination(KeyCode.F, KeyCombination.SHORTCUT_DOWN))
.augment(button);
text.focusedProperty().addListener((observable, oldValue, newValue) -> {
if (!newValue && filterString.getValue() == null) {
if (button.isFocused()) {
return;
}
expanded.set(false);
}
});
filterString.addListener((observable, oldValue, newValue) -> {
if (newValue == null && !text.isFocused()) {
expanded.set(false);
}
});
text.setMinWidth(0);
Styles.toggleStyleClass(text, Styles.LEFT_PILL);
filterString.subscribe(val -> {
if (val == null) {
text.getStyleClass().remove(Styles.SUCCESS);
} else {
text.getStyleClass().add(Styles.SUCCESS);
}
});
var fi = new FontIcon("mdi2m-magnify");
button.setGraphic(fi);
button.setOnAction(event -> {
if (model.getCurrentDirectory() == null) {
return;
}
if (expanded.get()) {
if (filterString.getValue() == null) {
expanded.set(false);
}
event.consume();
} else {
expanded.set(true);
text.requestFocus();
event.consume();
}
});
var box = new HBox(text, button);
box.getStyleClass().add("browser-filter");
box.setAlignment(Pos.CENTER);
text.setPrefWidth(0);
text.setFocusTraversable(false);
button.getStyleClass().add(Styles.FLAT);
button.disableProperty().bind(model.getInOverview());
expanded.addListener((observable, oldValue, val) -> {
if (val) {
text.setPrefWidth(250);
text.setFocusTraversable(true);
button.getStyleClass().add(Styles.RIGHT_PILL);
button.getStyleClass().remove(Styles.FLAT);
} else {
text.setPrefWidth(0);
text.setFocusTraversable(false);
button.getStyleClass().remove(Styles.RIGHT_PILL);
button.getStyleClass().add(Styles.FLAT);
}
});
button.minHeightProperty().bind(text.heightProperty());
button.minWidthProperty().bind(text.heightProperty());
button.maxHeightProperty().bind(text.heightProperty());
button.maxWidthProperty().bind(text.heightProperty());
return new Structure(box, text, button);
}
public record Structure(HBox box, TextField textField, Button toggleButton) implements CompStructure<HBox> {
@Override
public HBox get() {
return box;
}
}
}

View file

@ -0,0 +1,158 @@
package io.xpipe.app.browser.file;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.core.process.OsType;
import io.xpipe.core.store.FileEntry;
import io.xpipe.core.store.FileKind;
import io.xpipe.core.store.FileNames;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import lombok.Getter;
import java.util.*;
import java.util.stream.Stream;
@Getter
public final class BrowserFileListModel {
static final Comparator<BrowserEntry> FILE_TYPE_COMPARATOR =
Comparator.comparing(path -> path.getRawFileEntry().resolved().getKind() != FileKind.DIRECTORY);
private final BrowserFileSystemTabModel.SelectionMode selectionMode;
private final BrowserFileSystemTabModel fileSystemModel;
private final Property<Comparator<BrowserEntry>> comparatorProperty =
new SimpleObjectProperty<>(FILE_TYPE_COMPARATOR);
private final Property<List<BrowserEntry>> all = new SimpleObjectProperty<>(new ArrayList<>());
private final Property<List<BrowserEntry>> shown = new SimpleObjectProperty<>(new ArrayList<>());
private final ObservableList<BrowserEntry> selection = FXCollections.observableArrayList();
private final Property<BrowserEntry> draggedOverDirectory = new SimpleObjectProperty<>();
private final Property<Boolean> draggedOverEmpty = new SimpleBooleanProperty();
private final Property<BrowserEntry> editing = new SimpleObjectProperty<>();
public BrowserFileListModel(
BrowserFileSystemTabModel.SelectionMode selectionMode, BrowserFileSystemTabModel fileSystemModel) {
this.selectionMode = selectionMode;
this.fileSystemModel = fileSystemModel;
fileSystemModel.getFilter().addListener((observable, oldValue, newValue) -> {
refreshShown();
});
}
public void setAll(Stream<FileEntry> newFiles) {
try (var s = newFiles) {
var l = s.filter(entry -> entry != null)
.map(entry -> new BrowserEntry(entry, this))
.toList();
all.setValue(l);
refreshShown();
}
}
public void setComparator(Comparator<BrowserEntry> comparator) {
comparatorProperty.setValue(comparator);
refreshShown();
}
private void refreshShown() {
List<BrowserEntry> filtered = fileSystemModel.getFilter().getValue() != null
? all.getValue().stream()
.filter(entry -> {
var name = FileNames.getFileName(
entry.getRawFileEntry().getPath())
.toLowerCase(Locale.ROOT);
var filterString =
fileSystemModel.getFilter().getValue().toLowerCase(Locale.ROOT);
return name.contains(filterString);
})
.toList()
: all.getValue();
var listCopy = new ArrayList<>(filtered);
listCopy.sort(order());
shown.setValue(listCopy);
}
public Comparator<BrowserEntry> order() {
var dirsFirst = Comparator.<BrowserEntry, Boolean>comparing(
path -> path.getRawFileEntry().resolved().getKind() != FileKind.DIRECTORY);
var comp = comparatorProperty.getValue();
Comparator<BrowserEntry> us = comp != null ? dirsFirst.thenComparing(comp) : dirsFirst;
return us;
}
public BrowserEntry rename(BrowserEntry old, String newName) {
if (old == null
|| newName == null
|| fileSystemModel == null
|| fileSystemModel.isClosed()
|| fileSystemModel.getCurrentPath().get() == null) {
return old;
}
var fullPath = FileNames.join(fileSystemModel.getCurrentPath().get(), old.getFileName());
var newFullPath = FileNames.join(fileSystemModel.getCurrentPath().get(), newName);
// This check will fail on case-insensitive file systems when changing the case of the file
// So skip it in this case
var skipExistCheck =
fileSystemModel.getFileSystem().getShell().orElseThrow().getOsType() == OsType.WINDOWS
&& old.getFileName().equalsIgnoreCase(newName);
if (!skipExistCheck) {
boolean exists;
try {
exists = fileSystemModel.getFileSystem().fileExists(newFullPath)
|| fileSystemModel.getFileSystem().directoryExists(newFullPath);
} catch (Exception e) {
ErrorEvent.fromThrowable(e).handle();
return old;
}
if (exists) {
ErrorEvent.fromMessage("Target " + newFullPath + " does already exist")
.expected()
.handle();
fileSystemModel.refresh();
return old;
}
}
try {
fileSystemModel.getFileSystem().move(fullPath, newFullPath);
fileSystemModel.refresh();
var b = all.getValue().stream()
.filter(browserEntry ->
browserEntry.getRawFileEntry().getPath().equals(newFullPath))
.findFirst()
.orElse(old);
return b;
} catch (Exception e) {
ErrorEvent.fromThrowable(e).handle();
return old;
}
}
public void onDoubleClick(BrowserEntry entry) {
var r = entry.getRawFileEntry().resolved();
if (r.getKind() == FileKind.DIRECTORY) {
fileSystemModel.cdAsync(r.getPath());
}
if (AppPrefs.get().editFilesWithDoubleClick().get() && r.getKind() == FileKind.FILE) {
var selection = new LinkedHashSet<>(getSelection());
selection.add(entry);
for (BrowserEntry e : selection) {
BrowserFileOpener.openInTextEditor(getFileSystemModel(), e.getRawFileEntry());
}
}
}
}

View file

@ -0,0 +1,202 @@
package io.xpipe.app.browser.file;
import io.xpipe.app.comp.base.LazyTextFieldComp;
import io.xpipe.app.comp.base.PrettyImageHelper;
import io.xpipe.app.util.BooleanScope;
import io.xpipe.app.util.ContextMenuHelper;
import io.xpipe.app.util.InputHelper;
import io.xpipe.app.util.PlatformThread;
import io.xpipe.app.util.ThreadHelper;
import io.xpipe.core.store.FileKind;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableStringValue;
import javafx.css.PseudoClass;
import javafx.geometry.Pos;
import javafx.geometry.Side;
import javafx.scene.AccessibleRole;
import javafx.scene.Node;
import javafx.scene.control.ButtonBase;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import atlantafx.base.controls.Spacer;
class BrowserFileListNameCell extends TableCell<BrowserEntry, String> {
private final BrowserFileListModel fileList;
private final ObservableStringValue typedSelection;
private final StringProperty img = new SimpleStringProperty();
private final StringProperty text = new SimpleStringProperty();
private final BooleanProperty updating = new SimpleBooleanProperty();
public BrowserFileListNameCell(
BrowserFileListModel fileList,
ObservableStringValue typedSelection,
Property<BrowserEntry> editing,
TableView<BrowserEntry> tableView) {
this.fileList = fileList;
this.typedSelection = typedSelection;
accessibleTextProperty()
.bind(Bindings.createStringBinding(
() -> {
return getItem() != null ? getItem() : null;
},
itemProperty()));
setAccessibleRole(AccessibleRole.TEXT);
var textField = new LazyTextFieldComp(text)
.minWidth(USE_PREF_SIZE)
.createStructure()
.get();
var quickAccess = createQuickAccessButton();
setupShortcuts(tableView, (ButtonBase) quickAccess);
setupRename(fileList, textField, editing);
Node imageView = PrettyImageHelper.ofFixedSize(img, 24, 24).createRegion();
HBox graphic = new HBox(imageView, new Spacer(5), quickAccess, new Spacer(1), textField);
quickAccess.prefHeightProperty().bind(graphic.heightProperty());
graphic.setAlignment(Pos.CENTER_LEFT);
graphic.setPrefHeight(34);
HBox.setHgrow(textField, Priority.ALWAYS);
graphic.setAlignment(Pos.CENTER_LEFT);
setGraphic(graphic);
}
private Region createQuickAccessButton() {
var quickAccess = new BrowserQuickAccessButtonComp(() -> getTableRow().getItem(), fileList.getFileSystemModel())
.hide(Bindings.createBooleanBinding(
() -> {
if (getTableRow() == null) {
return true;
}
var item = getTableRow().getItem();
var notDir = item.getRawFileEntry().resolved().getKind() != FileKind.DIRECTORY;
var isParentLink = item.getRawFileEntry()
.equals(fileList.getFileSystemModel().getCurrentParentDirectory());
return notDir || isParentLink;
},
itemProperty()))
.focusTraversable(false)
.createRegion();
return quickAccess;
}
private void setupShortcuts(TableView<BrowserEntry> tableView, ButtonBase quickAccess) {
InputHelper.onExactKeyCode(tableView, KeyCode.RIGHT, false, event -> {
var selected = fileList.getSelection();
if (selected.size() == 1 && selected.getFirst() == getTableRow().getItem()) {
quickAccess.fire();
event.consume();
}
});
InputHelper.onExactKeyCode(tableView, KeyCode.SPACE, true, event -> {
var selection = typedSelection.get() + " ";
var found = fileList.getShown().getValue().stream()
.filter(browserEntry ->
browserEntry.getFileName().toLowerCase().startsWith(selection))
.findFirst();
// Ugly fix to prevent space from showing the menu when there is a file matching
// Due to the table view input map, these events always get sent and consumed, not allowing us to
// differentiate between these cases
if (found.isPresent()) {
return;
}
var selected = fileList.getSelection();
// Only show one menu across all selected entries
if (selected.size() > 0 && selected.getLast() == getTableRow().getItem()) {
var cm = new BrowserContextMenu(
fileList.getFileSystemModel(), getTableRow().getItem(), false);
ContextMenuHelper.toggleShow(cm, this, Side.RIGHT);
event.consume();
}
});
}
private void setupRename(BrowserFileListModel fileList, TextField textField, Property<BrowserEntry> editing) {
ChangeListener<String> listener = (observable, oldValue, newValue) -> {
if (updating.get()) {
return;
}
getTableRow().requestFocus();
var it = getTableRow().getItem();
editing.setValue(null);
ThreadHelper.runAsync(() -> {
if (it == null) {
return;
}
var r = fileList.rename(it, newValue);
Platform.runLater(() -> {
updateItem(getItem(), isEmpty());
fileList.getSelection().setAll(r);
getTableView().scrollTo(r);
});
});
};
text.addListener(listener);
editing.addListener((observable, oldValue, newValue) -> {
if (getTableRow().getItem() != null && getTableRow().getItem().equals(newValue)) {
PlatformThread.runLaterIfNeeded(() -> {
textField.setDisable(false);
textField.requestFocus();
});
}
});
}
@Override
protected void updateItem(String newName, boolean empty) {
if (updating.get()) {
super.updateItem(newName, empty);
return;
}
try (var ignored = new BooleanScope(updating).start()) {
super.updateItem(newName, empty);
if (empty || newName == null || getTableRow().getItem() == null) {
// Don't set image as that would trigger image comp update
// and cells are emptied on each change, leading to unnecessary changes
// img.set(null);
// Visibility seems to be bugged, so use opacity
setOpacity(0.0);
} else {
img.set(getTableRow().getItem().getIcon());
var isDirectory = getTableRow().getItem().getRawFileEntry().getKind() == FileKind.DIRECTORY;
pseudoClassStateChanged(PseudoClass.getPseudoClass("folder"), isDirectory);
var normalName = getTableRow().getItem().getRawFileEntry().getKind() == FileKind.LINK
? getTableRow().getItem().getFileName() + " -> "
+ getTableRow()
.getItem()
.getRawFileEntry()
.resolved()
.getPath()
: getTableRow().getItem().getFileName();
var fileName = normalName;
var hidden = getTableRow().getItem().getRawFileEntry().getInfo().explicitlyHidden()
|| fileName.startsWith(".");
getTableRow().pseudoClassStateChanged(PseudoClass.getPseudoClass("hidden"), hidden);
text.set(fileName);
// Visibility seems to be bugged, so use opacity
setOpacity(1.0);
}
}
}
}

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