diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..9dcf0bc --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "third_party/termux-app"] + path = third_party/termux-app + url = https://github.com/SagerNet/termux-app.git diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 49ec10c..84293ab 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -328,6 +328,10 @@ dependencies { implementation("com.github.jeziellago:compose-markdown:0.5.8") implementation("org.kodein.emoji:emoji-kt:2.3.0") + // Terminal emulator + implementation(project(":terminal-emulator")) + implementation(project(":terminal-view")) + // Xposed API for self-hooking VPN hide module compileOnly("de.robv.android.xposed:api:82") compileOnly(project(":libxposed-api")) diff --git a/app/src/main/assets/termux-colors/argonaut.properties b/app/src/main/assets/termux-colors/argonaut.properties new file mode 100644 index 0000000..9f1f4cf --- /dev/null +++ b/app/src/main/assets/termux-colors/argonaut.properties @@ -0,0 +1,21 @@ +# https://github.com/Mayccoll/Gogh/blob/master/themes/argonaut.sh +background=#0e1019 +foreground=#fffaf4 +cursor=#fffaf4 + +color0=#232323 +color1=#ff000f +color2=#8ce10b +color3=#ffb900 +color4=#008df8 +color5=#6d43a6 +color6=#00d8eb +color7=#ffffff +color8=#444444 +color9=#ff2740 +color10=#abe15b +color11=#ffd242 +color12=#0092ff +color13=#9a5feb +color14=#67fff0 +color15=#ffffff diff --git a/app/src/main/assets/termux-colors/base16-3024-dark.properties b/app/src/main/assets/termux-colors/base16-3024-dark.properties new file mode 100644 index 0000000..eaee909 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-3024-dark.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-3024.dark.256.xresources +# Base16 3024 +# Scheme: Jan T. Sott (http://github.com/idleberg) +foreground=#a5a2a2 +background=#090300 +cursor=#a5a2a2 + +color0=#090300 +color1=#db2d20 +color2=#01a252 +color3=#fded02 +color4=#01a0e4 +color5=#a16a94 +color6=#b5e4f4 +color7=#a5a2a2 +color8=#5c5855 +color9=#db2d20 +color10=#01a252 +color11=#fded02 +color12=#01a0e4 +color13=#a16a94 +color14=#b5e4f4 +color15=#f7f7f7 + +color16=#e8bbd0 +color17=#cdab53 +color18=#3a3432 +color19=#4a4543 +color20=#807d7c +color21=#d6d5d4 diff --git a/app/src/main/assets/termux-colors/base16-3024-light.properties b/app/src/main/assets/termux-colors/base16-3024-light.properties new file mode 100644 index 0000000..1f4dfe3 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-3024-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-3024.light.256.xresources +# Base16 3024 +# Scheme: Jan T. Sott (http://github.com/idleberg) +foreground=#4a4543 +background=#f7f7f7 +cursor=#4a4543 + +color0=#090300 +color1=#db2d20 +color2=#01a252 +color3=#fded02 +color4=#01a0e4 +color5=#a16a94 +color6=#b5e4f4 +color7=#a5a2a2 +color8=#5c5855 +color9=#db2d20 +color10=#01a252 +color11=#fded02 +color12=#01a0e4 +color13=#a16a94 +color14=#b5e4f4 +color15=#f7f7f7 + +color16=#e8bbd0 +color17=#cdab53 +color18=#3a3432 +color19=#4a4543 +color20=#807d7c +color21=#d6d5d4 diff --git a/app/src/main/assets/termux-colors/base16-apathy-dark.properties b/app/src/main/assets/termux-colors/base16-apathy-dark.properties new file mode 100644 index 0000000..37839cc --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-apathy-dark.properties @@ -0,0 +1,28 @@ +foreground= #81B5AC +background= #031A16 +cursor= #81B5AC + +color0= #031A16 +color1= #3E9688 +color2= #883E96 +color3= #3E4C96 +color4= #96883E +color5= #4C963E +color6= #963E4C +color7= #81B5AC + +color8= #2B685E +color9= #3E9688 +color10= #883E96 +color11= #3E4C96 +color12= #96883E +color13= #4C963E +color14= #963E4C +color15= #D2E7E4 + +color16= #3E7996 +color17= #3E965B +color18= #0B342D +color19= #184E45 +color20= #5F9C92 +color21= #A7CEC8 \ No newline at end of file diff --git a/app/src/main/assets/termux-colors/base16-apathy-light.properties b/app/src/main/assets/termux-colors/base16-apathy-light.properties new file mode 100644 index 0000000..4bd16ea --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-apathy-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-apathy.light.256.xresources +# Base16 Apathy +# Scheme: Jannik Siebert (https://github.com/janniks) +foreground=#184E45 +background=#D2E7E4 +cursor=#184E45 + +color0=#031A16 +color1=#3E9688 +color2=#883E96 +color3=#3E4C96 +color4=#96883E +color5=#4C963E +color6=#963E4C +color7=#81B5AC +color8=#2B685E +color9=#3E9688 +color10=#883E96 +color11=#3E4C96 +color12=#96883E +color13=#4C963E +color14=#963E4C +color15=#D2E7E4 + +color16=#3E7996 +color17=#3E965B +color18=#0B342D +color19=#184E45 +color20=#5F9C92 +color21=#A7CEC8 diff --git a/app/src/main/assets/termux-colors/base16-ashes-dark.properties b/app/src/main/assets/termux-colors/base16-ashes-dark.properties new file mode 100644 index 0000000..9518bcb --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-ashes-dark.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-ashes.dark.256.xresources +# Base16 Ashes +# Scheme: Jannik Siebert (https://github.com/janniks) +foreground=#C7CCD1 +background=#1C2023 +cursor=#C7CCD1 + +color0=#1C2023 +color1=#C7AE95 +color2=#95C7AE +color3=#AEC795 +color4=#AE95C7 +color5=#C795AE +color6=#95AEC7 +color7=#C7CCD1 +color8=#747C84 +color9=#C7AE95 +color10=#95C7AE +color11=#AEC795 +color12=#AE95C7 +color13=#C795AE +color14=#95AEC7 +color15=#F3F4F5 + +color16=#C7C795 +color17=#C79595 +color18=#393F45 +color19=#565E65 +color20=#ADB3BA +color21=#DFE2E5 diff --git a/app/src/main/assets/termux-colors/base16-ashes-light.properties b/app/src/main/assets/termux-colors/base16-ashes-light.properties new file mode 100644 index 0000000..d168e65 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-ashes-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-ashes.light.256.xresources +# Base16 Ashes +# Scheme: Jannik Siebert (https://github.com/janniks) +foreground=#565E65 +background=#F3F4F5 +cursor=#565E65 + +color0=#1C2023 +color1=#C7AE95 +color2=#95C7AE +color3=#AEC795 +color4=#AE95C7 +color5=#C795AE +color6=#95AEC7 +color7=#C7CCD1 +color8=#747C84 +color9=#C7AE95 +color10=#95C7AE +color11=#AEC795 +color12=#AE95C7 +color13=#C795AE +color14=#95AEC7 +color15=#F3F4F5 + +color16=#C7C795 +color17=#C79595 +color18=#393F45 +color19=#565E65 +color20=#ADB3BA +color21=#DFE2E5 diff --git a/app/src/main/assets/termux-colors/base16-atelierdune-dark.properties b/app/src/main/assets/termux-colors/base16-atelierdune-dark.properties new file mode 100644 index 0000000..1d1a72e --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-atelierdune-dark.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-atelierdune.dark.256.xresources +# Base16 Atelier Dune +# Scheme: Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune) +foreground=#a6a28c +background=#20201d +cursor=#a6a28c + +color0=#20201d +color1=#d73737 +color2=#60ac39 +color3=#cfb017 +color4=#6684e1 +color5=#b854d4 +color6=#1fad83 +color7=#a6a28c +color8=#7d7a68 +color9=#d73737 +color10=#60ac39 +color11=#cfb017 +color12=#6684e1 +color13=#b854d4 +color14=#1fad83 +color15=#fefbec + +color16=#b65611 +color17=#d43552 +color18=#292824 +color19=#6e6b5e +color20=#999580 +color21=#e8e4cf diff --git a/app/src/main/assets/termux-colors/base16-atelierdune-light.properties b/app/src/main/assets/termux-colors/base16-atelierdune-light.properties new file mode 100644 index 0000000..686e871 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-atelierdune-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-atelierdune.light.256.xresources +# Base16 Atelier Dune +# Scheme: Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune) +foreground=#6e6b5e +background=#fefbec +cursor=#6e6b5e + +color0=#20201d +color1=#d73737 +color2=#60ac39 +color3=#cfb017 +color4=#6684e1 +color5=#b854d4 +color6=#1fad83 +color7=#a6a28c +color8=#7d7a68 +color9=#d73737 +color10=#60ac39 +color11=#cfb017 +color12=#6684e1 +color13=#b854d4 +color14=#1fad83 +color15=#fefbec + +color16=#b65611 +color17=#d43552 +color18=#292824 +color19=#6e6b5e +color20=#999580 +color21=#e8e4cf diff --git a/app/src/main/assets/termux-colors/base16-atelierforest-dark.properties b/app/src/main/assets/termux-colors/base16-atelierforest-dark.properties new file mode 100644 index 0000000..74ebb8a --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-atelierforest-dark.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-atelierforest.dark.256.xresources +# Base16 Atelier Forest +# Scheme: Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/forest) +foreground=#a8a19f +background=#1b1918 +cursor=#a8a19f + +color0=#1b1918 +color1=#f22c40 +color2=#5ab738 +color3=#d5911a +color4=#407ee7 +color5=#6666ea +color6=#00ad9c +color7=#a8a19f +color8=#766e6b +color9=#f22c40 +color10=#5ab738 +color11=#d5911a +color12=#407ee7 +color13=#6666ea +color14=#00ad9c +color15=#f1efee + +color16=#df5320 +color17=#c33ff3 +color18=#2c2421 +color19=#68615e +color20=#9c9491 +color21=#e6e2e0 diff --git a/app/src/main/assets/termux-colors/base16-atelierforest-light.properties b/app/src/main/assets/termux-colors/base16-atelierforest-light.properties new file mode 100644 index 0000000..f653fc7 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-atelierforest-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-atelierforest.light.256.xresources +# Base16 Atelier Forest +# Scheme: Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/forest) +foreground=#68615e +background=#f1efee +cursor=#68615e + +color0=#1b1918 +color1=#f22c40 +color2=#5ab738 +color3=#d5911a +color4=#407ee7 +color5=#6666ea +color6=#00ad9c +color7=#a8a19f +color8=#766e6b +color9=#f22c40 +color10=#5ab738 +color11=#d5911a +color12=#407ee7 +color13=#6666ea +color14=#00ad9c +color15=#f1efee + +color16=#df5320 +color17=#c33ff3 +color18=#2c2421 +color19=#68615e +color20=#9c9491 +color21=#e6e2e0 diff --git a/app/src/main/assets/termux-colors/base16-atelierheath-dark.properties b/app/src/main/assets/termux-colors/base16-atelierheath-dark.properties new file mode 100644 index 0000000..963c906 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-atelierheath-dark.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-atelierheath.dark.256.xresources +# Base16 Atelier Heath +# Scheme: Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/heath) +foreground=#ab9bab +background=#1b181b +cursor=#ab9bab + +color0=#1b181b +color1=#ca402b +color2=#379a37 +color3=#bb8a35 +color4=#516aec +color5=#7b59c0 +color6=#159393 +color7=#ab9bab +color8=#776977 +color9=#ca402b +color10=#379a37 +color11=#bb8a35 +color12=#516aec +color13=#7b59c0 +color14=#159393 +color15=#f7f3f7 + +color16=#a65926 +color17=#cc33cc +color18=#292329 +color19=#695d69 +color20=#9e8f9e +color21=#d8cad8 diff --git a/app/src/main/assets/termux-colors/base16-atelierheath-light.properties b/app/src/main/assets/termux-colors/base16-atelierheath-light.properties new file mode 100644 index 0000000..2423c79 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-atelierheath-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-atelierheath.light.256.xresources +# Base16 Atelier Heath +# Scheme: Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/heath) +foreground=#695d69 +background=#f7f3f7 +cursor=#695d69 + +color0=#1b181b +color1=#ca402b +color2=#379a37 +color3=#bb8a35 +color4=#516aec +color5=#7b59c0 +color6=#159393 +color7=#ab9bab +color8=#776977 +color9=#ca402b +color10=#379a37 +color11=#bb8a35 +color12=#516aec +color13=#7b59c0 +color14=#159393 +color15=#f7f3f7 + +color16=#a65926 +color17=#cc33cc +color18=#292329 +color19=#695d69 +color20=#9e8f9e +color21=#d8cad8 diff --git a/app/src/main/assets/termux-colors/base16-atelierlakeside-dark.properties b/app/src/main/assets/termux-colors/base16-atelierlakeside-dark.properties new file mode 100644 index 0000000..ab50889 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-atelierlakeside-dark.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-atelierlakeside.dark.256.xresources +# Base16 Atelier Lakeside +# Scheme: Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/lakeside/) +foreground=#7ea2b4 +background=#161b1d +cursor=#7ea2b4 + +color0=#161b1d +color1=#d22d72 +color2=#568c3b +color3=#8a8a0f +color4=#257fad +color5=#5d5db1 +color6=#2d8f6f +color7=#7ea2b4 +color8=#5a7b8c +color9=#d22d72 +color10=#568c3b +color11=#8a8a0f +color12=#257fad +color13=#5d5db1 +color14=#2d8f6f +color15=#ebf8ff + +color16=#935c25 +color17=#b72dd2 +color18=#1f292e +color19=#516d7b +color20=#7195a8 +color21=#c1e4f6 diff --git a/app/src/main/assets/termux-colors/base16-atelierlakeside-light.properties b/app/src/main/assets/termux-colors/base16-atelierlakeside-light.properties new file mode 100644 index 0000000..29f4e9f --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-atelierlakeside-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-atelierlakeside.light.256.xresources +# Base16 Atelier Lakeside +# Scheme: Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/lakeside/) +foreground=#516d7b +background=#ebf8ff +cursor=#516d7b + +color0=#161b1d +color1=#d22d72 +color2=#568c3b +color3=#8a8a0f +color4=#257fad +color5=#5d5db1 +color6=#2d8f6f +color7=#7ea2b4 +color8=#5a7b8c +color9=#d22d72 +color10=#568c3b +color11=#8a8a0f +color12=#257fad +color13=#5d5db1 +color14=#2d8f6f +color15=#ebf8ff + +color16=#935c25 +color17=#b72dd2 +color18=#1f292e +color19=#516d7b +color20=#7195a8 +color21=#c1e4f6 diff --git a/app/src/main/assets/termux-colors/base16-atelierseaside-dark.properties b/app/src/main/assets/termux-colors/base16-atelierseaside-dark.properties new file mode 100644 index 0000000..9073901 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-atelierseaside-dark.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-atelierseaside.dark.256.xresources +# Base16 Atelier Seaside +# Scheme: Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/seaside/) +foreground=#8ca68c +background=#131513 +cursor=#8ca68c + +color0=#131513 +color1=#e6193c +color2=#29a329 +color3=#c3c322 +color4=#3d62f5 +color5=#ad2bee +color6=#1999b3 +color7=#8ca68c +color8=#687d68 +color9=#e6193c +color10=#29a329 +color11=#c3c322 +color12=#3d62f5 +color13=#ad2bee +color14=#1999b3 +color15=#f0fff0 + +color16=#87711d +color17=#e619c3 +color18=#242924 +color19=#5e6e5e +color20=#809980 +color21=#cfe8cf diff --git a/app/src/main/assets/termux-colors/base16-atelierseaside-light.properties b/app/src/main/assets/termux-colors/base16-atelierseaside-light.properties new file mode 100644 index 0000000..30f1db6 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-atelierseaside-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-atelierseaside.light.256.xresources +# Base16 Atelier Seaside +# Scheme: Bram de Haan (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/seaside/) +foreground=#5e6e5e +background=#f0fff0 +cursor=#5e6e5e + +color0=#131513 +color1=#e6193c +color2=#29a329 +color3=#c3c322 +color4=#3d62f5 +color5=#ad2bee +color6=#1999b3 +color7=#8ca68c +color8=#687d68 +color9=#e6193c +color10=#29a329 +color11=#c3c322 +color12=#3d62f5 +color13=#ad2bee +color14=#1999b3 +color15=#f0fff0 + +color16=#87711d +color17=#e619c3 +color18=#242924 +color19=#5e6e5e +color20=#809980 +color21=#cfe8cf diff --git a/app/src/main/assets/termux-colors/base16-bespin-dark.properties b/app/src/main/assets/termux-colors/base16-bespin-dark.properties new file mode 100644 index 0000000..44e2902 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-bespin-dark.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-bespin.dark.256.xresources +# Base16 Bespin +# Scheme: Jan T. Sott +foreground=#8a8986 +background=#28211c +cursor=#8a8986 + +color0=#28211c +color1=#cf6a4c +color2=#54be0d +color3=#f9ee98 +color4=#5ea6ea +color5=#9b859d +color6=#afc4db +color7=#8a8986 +color8=#666666 +color9=#cf6a4c +color10=#54be0d +color11=#f9ee98 +color12=#5ea6ea +color13=#9b859d +color14=#afc4db +color15=#baae9e + +color16=#cf7d34 +color17=#937121 +color18=#36312e +color19=#5e5d5c +color20=#797977 +color21=#9d9b97 diff --git a/app/src/main/assets/termux-colors/base16-bespin-light.properties b/app/src/main/assets/termux-colors/base16-bespin-light.properties new file mode 100644 index 0000000..fe4a050 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-bespin-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-bespin.light.256.xresources +# Base16 Bespin +# Scheme: Jan T. Sott +foreground=#5e5d5c +background=#baae9e +cursor=#5e5d5c + +color0=#28211c +color1=#cf6a4c +color2=#54be0d +color3=#f9ee98 +color4=#5ea6ea +color5=#9b859d +color6=#afc4db +color7=#8a8986 +color8=#666666 +color9=#cf6a4c +color10=#54be0d +color11=#f9ee98 +color12=#5ea6ea +color13=#9b859d +color14=#afc4db +color15=#baae9e + +color16=#cf7d34 +color17=#937121 +color18=#36312e +color19=#5e5d5c +color20=#797977 +color21=#9d9b97 diff --git a/app/src/main/assets/termux-colors/base16-brewer-dark.properties b/app/src/main/assets/termux-colors/base16-brewer-dark.properties new file mode 100644 index 0000000..bcbca4f --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-brewer-dark.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-brewer.dark.256.xresources +# Base16 Brewer +# Scheme: Timothée Poisot (http://github.com/tpoisot) +foreground=#b7b8b9 +background=#0c0d0e +cursor=#b7b8b9 + +color0=#0c0d0e +color1=#e31a1c +color2=#31a354 +color3=#dca060 +color4=#3182bd +color5=#756bb1 +color6=#80b1d3 +color7=#b7b8b9 +color8=#737475 +color9=#e31a1c +color10=#31a354 +color11=#dca060 +color12=#3182bd +color13=#756bb1 +color14=#80b1d3 +color15=#fcfdfe + +color16=#e6550d +color17=#b15928 +color18=#2e2f30 +color19=#515253 +color20=#959697 +color21=#dadbdc diff --git a/app/src/main/assets/termux-colors/base16-brewer-light.properties b/app/src/main/assets/termux-colors/base16-brewer-light.properties new file mode 100644 index 0000000..bff9832 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-brewer-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-brewer.light.256.xresources +# Base16 Brewer +# Scheme: Timothée Poisot (http://github.com/tpoisot) +foreground=#515253 +background=#fcfdfe +cursor=#515253 + +color0=#0c0d0e +color1=#e31a1c +color2=#31a354 +color3=#dca060 +color4=#3182bd +color5=#756bb1 +color6=#80b1d3 +color7=#b7b8b9 +color8=#737475 +color9=#e31a1c +color10=#31a354 +color11=#dca060 +color12=#3182bd +color13=#756bb1 +color14=#80b1d3 +color15=#fcfdfe + +color16=#e6550d +color17=#b15928 +color18=#2e2f30 +color19=#515253 +color20=#959697 +color21=#dadbdc diff --git a/app/src/main/assets/termux-colors/base16-bright-dark.properties b/app/src/main/assets/termux-colors/base16-bright-dark.properties new file mode 100644 index 0000000..6dd80cd --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-bright-dark.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-bright.dark.256.xresources +# Base16 Bright +# Scheme: Chris Kempson (http://chriskempson.com) +foreground=#e0e0e0 +background=#000000 +cursor=#e0e0e0 + +color0=#000000 +color1=#fb0120 +color2=#a1c659 +color3=#fda331 +color4=#6fb3d2 +color5=#d381c3 +color6=#76c7b7 +color7=#e0e0e0 +color8=#b0b0b0 +color9=#fb0120 +color10=#a1c659 +color11=#fda331 +color12=#6fb3d2 +color13=#d381c3 +color14=#76c7b7 +color15=#ffffff + +color16=#fc6d24 +color17=#be643c +color18=#303030 +color19=#505050 +color20=#d0d0d0 +color21=#f5f5f5 diff --git a/app/src/main/assets/termux-colors/base16-bright-light.properties b/app/src/main/assets/termux-colors/base16-bright-light.properties new file mode 100644 index 0000000..1d5b41f --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-bright-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-bright.light.256.xresources +# Base16 Bright +# Scheme: Chris Kempson (http://chriskempson.com) +foreground=#505050 +background=#ffffff +cursor=#505050 + +color0=#000000 +color1=#fb0120 +color2=#a1c659 +color3=#fda331 +color4=#6fb3d2 +color5=#d381c3 +color6=#76c7b7 +color7=#e0e0e0 +color8=#b0b0b0 +color9=#fb0120 +color10=#a1c659 +color11=#fda331 +color12=#6fb3d2 +color13=#d381c3 +color14=#76c7b7 +color15=#ffffff + +color16=#fc6d24 +color17=#be643c +color18=#303030 +color19=#505050 +color20=#d0d0d0 +color21=#f5f5f5 diff --git a/app/src/main/assets/termux-colors/base16-chalk-dark.properties b/app/src/main/assets/termux-colors/base16-chalk-dark.properties new file mode 100644 index 0000000..28eb668 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-chalk-dark.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-chalk.dark.256.xresources +# Base16 Chalk +# Scheme: Chris Kempson (http://chriskempson.com) +foreground=#d0d0d0 +background=#151515 +cursor=#d0d0d0 + +color0=#151515 +color1=#fb9fb1 +color2=#acc267 +color3=#ddb26f +color4=#6fc2ef +color5=#e1a3ee +color6=#12cfc0 +color7=#d0d0d0 +color8=#505050 +color9=#fb9fb1 +color10=#acc267 +color11=#ddb26f +color12=#6fc2ef +color13=#e1a3ee +color14=#12cfc0 +color15=#f5f5f5 + +color16=#eda987 +color17=#deaf8f +color18=#202020 +color19=#303030 +color20=#b0b0b0 +color21=#e0e0e0 diff --git a/app/src/main/assets/termux-colors/base16-chalk-light.properties b/app/src/main/assets/termux-colors/base16-chalk-light.properties new file mode 100644 index 0000000..155cb54 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-chalk-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-chalk.light.256.xresources +# Base16 Chalk +# Scheme: Chris Kempson (http://chriskempson.com) +foreground=#303030 +background=#f5f5f5 +cursor=#303030 + +color0=#151515 +color1=#fb9fb1 +color2=#acc267 +color3=#ddb26f +color4=#6fc2ef +color5=#e1a3ee +color6=#12cfc0 +color7=#d0d0d0 +color8=#505050 +color9=#fb9fb1 +color10=#acc267 +color11=#ddb26f +color12=#6fc2ef +color13=#e1a3ee +color14=#12cfc0 +color15=#f5f5f5 + +color16=#eda987 +color17=#deaf8f +color18=#202020 +color19=#303030 +color20=#b0b0b0 +color21=#e0e0e0 diff --git a/app/src/main/assets/termux-colors/base16-codeschool-dark.properties b/app/src/main/assets/termux-colors/base16-codeschool-dark.properties new file mode 100644 index 0000000..a06fbd7 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-codeschool-dark.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-codeschool.dark.256.xresources +# Base16 Codeschool +# Scheme: brettof86 +foreground=#9ea7a6 +background=#232c31 +cursor=#9ea7a6 + +color0=#232c31 +color1=#2a5491 +color2=#237986 +color3=#a03b1e +color4=#484d79 +color5=#c59820 +color6=#b02f30 +color7=#9ea7a6 +color8=#3f4944 +color9=#2a5491 +color10=#237986 +color11=#a03b1e +color12=#484d79 +color13=#c59820 +color14=#b02f30 +color15=#b5d8f6 + +color16=#43820d +color17=#c98344 +color18=#1c3657 +color19=#2a343a +color20=#84898c +color21=#a7cfa3 diff --git a/app/src/main/assets/termux-colors/base16-codeschool-light.properties b/app/src/main/assets/termux-colors/base16-codeschool-light.properties new file mode 100644 index 0000000..080a882 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-codeschool-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-codeschool.light.256.xresources +# Base16 Codeschool +# Scheme: brettof86 +foreground=#2a343a +background=#b5d8f6 +cursor=#2a343a + +color0=#232c31 +color1=#2a5491 +color2=#237986 +color3=#a03b1e +color4=#484d79 +color5=#c59820 +color6=#b02f30 +color7=#9ea7a6 +color8=#3f4944 +color9=#2a5491 +color10=#237986 +color11=#a03b1e +color12=#484d79 +color13=#c59820 +color14=#b02f30 +color15=#b5d8f6 + +color16=#43820d +color17=#c98344 +color18=#1c3657 +color19=#2a343a +color20=#84898c +color21=#a7cfa3 diff --git a/app/src/main/assets/termux-colors/base16-colors-dark.properties b/app/src/main/assets/termux-colors/base16-colors-dark.properties new file mode 100644 index 0000000..46ae2d5 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-colors-dark.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-colors.dark.256.xresources +# Base16 Colors +# Scheme: mrmrs (http://clrs.cc) +foreground=#bbbbbb +background=#111111 +cursor=#bbbbbb + +color0=#111111 +color1=#ff4136 +color2=#2ecc40 +color3=#ffdc00 +color4=#0074d9 +color5=#b10dc9 +color6=#7fdbff +color7=#bbbbbb +color8=#777777 +color9=#ff4136 +color10=#2ecc40 +color11=#ffdc00 +color12=#0074d9 +color13=#b10dc9 +color14=#7fdbff +color15=#ffffff + +color16=#ff851b +color17=#85144b +color18=#333333 +color19=#555555 +color20=#999999 +color21=#dddddd diff --git a/app/src/main/assets/termux-colors/base16-colors-light.properties b/app/src/main/assets/termux-colors/base16-colors-light.properties new file mode 100644 index 0000000..95a2eee --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-colors-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-colors.light.256.xresources +# Base16 Colors +# Scheme: mrmrs (http://clrs.cc) +foreground=#555555 +background=#ffffff +cursor=#555555 + +color0=#111111 +color1=#ff4136 +color2=#2ecc40 +color3=#ffdc00 +color4=#0074d9 +color5=#b10dc9 +color6=#7fdbff +color7=#bbbbbb +color8=#777777 +color9=#ff4136 +color10=#2ecc40 +color11=#ffdc00 +color12=#0074d9 +color13=#b10dc9 +color14=#7fdbff +color15=#ffffff + +color16=#ff851b +color17=#85144b +color18=#333333 +color19=#555555 +color20=#999999 +color21=#dddddd diff --git a/app/src/main/assets/termux-colors/base16-default-dark.properties b/app/src/main/assets/termux-colors/base16-default-dark.properties new file mode 100644 index 0000000..47bc182 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-default-dark.properties @@ -0,0 +1,32 @@ +# https=//github.com/chriskempson/base16-xresources/blob/master/base16-default.dark.256.xresources +# Base16 Default +# Scheme=Chris Kempson (http=//chriskempson.com) + +foreground=#d8d8d8 +background=#181818 +cursor=#d8d8d8 + +color0=#181818 +color1=#ab4642 +color2=#a1b56c +color3=#f7ca88 +color4=#7cafc2 +color5=#ba8baf +color6=#86c1b9 +color7=#d8d8d8 + +color8=#585858 +color9=#ab4642 +color10=#a1b56c +color11=#f7ca88 +color12=#7cafc2 +color13=#ba8baf +color14=#86c1b9 +color15=#f8f8f8 + +color16=#dc9656 +color17=#a16946 +color18=#282828 +color19=#383838 +color20=#b8b8b8 +color21=#e8e8e8 \ No newline at end of file diff --git a/app/src/main/assets/termux-colors/base16-default-light.properties b/app/src/main/assets/termux-colors/base16-default-light.properties new file mode 100644 index 0000000..f854463 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-default-light.properties @@ -0,0 +1,28 @@ +foreground= #383838 +background= #f8f8f8 +cursor= #383838 + +color0= #181818 +color1= #ab4642 +color2= #a1b56c +color3= #f7ca88 +color4= #7cafc2 +color5= #ba8baf +color6= #86c1b9 +color7= #d8d8d8 + +color8= #585858 +color9= #ab4642 +color10= #a1b56c +color11= #f7ca88 +color12= #7cafc2 +color13= #ba8baf +color14= #86c1b9 +color15= #f8f8f8 + +color16= #dc9656 +color17= #a16946 +color18= #282828 +color19= #383838 +color20= #b8b8b8 +color21= #e8e8e8 \ No newline at end of file diff --git a/app/src/main/assets/termux-colors/base16-eighties-dark.properties b/app/src/main/assets/termux-colors/base16-eighties-dark.properties new file mode 100644 index 0000000..90b6ff0 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-eighties-dark.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-eighties.dark.256.xresources +# Base16 Eighties +# Scheme: Chris Kempson (http://chriskempson.com) +foreground=#d3d0c8 +background=#2d2d2d +cursor=#d3d0c8 + +color0=#2d2d2d +color1=#f2777a +color2=#99cc99 +color3=#ffcc66 +color4=#6699cc +color5=#cc99cc +color6=#66cccc +color7=#d3d0c8 +color8=#747369 +color9=#f2777a +color10=#99cc99 +color11=#ffcc66 +color12=#6699cc +color13=#cc99cc +color14=#66cccc +color15=#f2f0ec + +color16=#f99157 +color17=#d27b53 +color18=#393939 +color19=#515151 +color20=#a09f93 +color21=#e8e6df diff --git a/app/src/main/assets/termux-colors/base16-eighties-light.properties b/app/src/main/assets/termux-colors/base16-eighties-light.properties new file mode 100644 index 0000000..8e5f29b --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-eighties-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-eighties.light.256.xresources +# Base16 Eighties +# Scheme: Chris Kempson (http://chriskempson.com) +foreground=#515151 +background=#f2f0ec +cursor=#515151 + +color0=#2d2d2d +color1=#f2777a +color2=#99cc99 +color3=#ffcc66 +color4=#6699cc +color5=#cc99cc +color6=#66cccc +color7=#d3d0c8 +color8=#747369 +color9=#f2777a +color10=#99cc99 +color11=#ffcc66 +color12=#6699cc +color13=#cc99cc +color14=#66cccc +color15=#f2f0ec + +color16=#f99157 +color17=#d27b53 +color18=#393939 +color19=#515151 +color20=#a09f93 +color21=#e8e6df diff --git a/app/src/main/assets/termux-colors/base16-embers-dark.properties b/app/src/main/assets/termux-colors/base16-embers-dark.properties new file mode 100644 index 0000000..8372901 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-embers-dark.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-embers.dark.256.xresources +# Base16 Embers +# Scheme: Jannik Siebert (https://github.com/janniks) +foreground=#A39A90 +background=#16130F +cursor=#A39A90 + +color0=#16130F +color1=#826D57 +color2=#57826D +color3=#6D8257 +color4=#6D5782 +color5=#82576D +color6=#576D82 +color7=#A39A90 +color8=#5A5047 +color9=#826D57 +color10=#57826D +color11=#6D8257 +color12=#6D5782 +color13=#82576D +color14=#576D82 +color15=#DBD6D1 + +color16=#828257 +color17=#825757 +color18=#2C2620 +color19=#433B32 +color20=#8A8075 +color21=#BEB6AE diff --git a/app/src/main/assets/termux-colors/base16-embers-light.properties b/app/src/main/assets/termux-colors/base16-embers-light.properties new file mode 100644 index 0000000..d60ac4a --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-embers-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-embers.light.256.xresources +# Base16 Embers +# Scheme: Jannik Siebert (https://github.com/janniks) +foreground=#433B32 +background=#DBD6D1 +cursor=#433B32 + +color0=#16130F +color1=#826D57 +color2=#57826D +color3=#6D8257 +color4=#6D5782 +color5=#82576D +color6=#576D82 +color7=#A39A90 +color8=#5A5047 +color9=#826D57 +color10=#57826D +color11=#6D8257 +color12=#6D5782 +color13=#82576D +color14=#576D82 +color15=#DBD6D1 + +color16=#828257 +color17=#825757 +color18=#2C2620 +color19=#433B32 +color20=#8A8075 +color21=#BEB6AE diff --git a/app/src/main/assets/termux-colors/base16-flat-dark.properties b/app/src/main/assets/termux-colors/base16-flat-dark.properties new file mode 100644 index 0000000..3e63f3d --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-flat-dark.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-flat.dark.256.xresources +# Base16 Flat +# Scheme: Chris Kempson (http://chriskempson.com) +foreground=#e0e0e0 +background=#2C3E50 +cursor=#e0e0e0 + +color0=#2C3E50 +color1=#E74C3C +color2=#2ECC71 +color3=#F1C40F +color4=#3498DB +color5=#9B59B6 +color6=#1ABC9C +color7=#e0e0e0 +color8=#95A5A6 +color9=#E74C3C +color10=#2ECC71 +color11=#F1C40F +color12=#3498DB +color13=#9B59B6 +color14=#1ABC9C +color15=#ECF0F1 + +color16=#E67E22 +color17=#be643c +color18=#34495E +color19=#7F8C8D +color20=#BDC3C7 +color21=#f5f5f5 diff --git a/app/src/main/assets/termux-colors/base16-flat-light.properties b/app/src/main/assets/termux-colors/base16-flat-light.properties new file mode 100644 index 0000000..040cd23 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-flat-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-flat.light.256.xresources +# Base16 Flat +# Scheme: Chris Kempson (http://chriskempson.com) +foreground=#7F8C8D +background=#ECF0F1 +cursor=#7F8C8D + +color0=#2C3E50 +color1=#E74C3C +color2=#2ECC71 +color3=#F1C40F +color4=#3498DB +color5=#9B59B6 +color6=#1ABC9C +color7=#e0e0e0 +color8=#95A5A6 +color9=#E74C3C +color10=#2ECC71 +color11=#F1C40F +color12=#3498DB +color13=#9B59B6 +color14=#1ABC9C +color15=#ECF0F1 + +color16=#E67E22 +color17=#be643c +color18=#34495E +color19=#7F8C8D +color20=#BDC3C7 +color21=#f5f5f5 diff --git a/app/src/main/assets/termux-colors/base16-google-dark.properties b/app/src/main/assets/termux-colors/base16-google-dark.properties new file mode 100644 index 0000000..659b2cd --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-google-dark.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-google.dark.256.xresources +# Base16 Google +# Scheme: Seth Wright (http://sethawright.com) +foreground=#c5c8c6 +background=#1d1f21 +cursor=#c5c8c6 + +color0=#1d1f21 +color1=#CC342B +color2=#198844 +color3=#FBA922 +color4=#3971ED +color5=#A36AC7 +color6=#3971ED +color7=#c5c8c6 +color8=#969896 +color9=#CC342B +color10=#198844 +color11=#FBA922 +color12=#3971ED +color13=#A36AC7 +color14=#3971ED +color15=#ffffff + +color16=#F96A38 +color17=#3971ED +color18=#282a2e +color19=#373b41 +color20=#b4b7b4 +color21=#e0e0e0 diff --git a/app/src/main/assets/termux-colors/base16-google-light.properties b/app/src/main/assets/termux-colors/base16-google-light.properties new file mode 100644 index 0000000..36e96b8 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-google-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-google.light.256.xresources +# Base16 Google +# Scheme: Seth Wright (http://sethawright.com) +foreground=#373b41 +background=#ffffff +cursor=#373b41 + +color0=#1d1f21 +color1=#CC342B +color2=#198844 +color3=#FBA922 +color4=#3971ED +color5=#A36AC7 +color6=#3971ED +color7=#c5c8c6 +color8=#969896 +color9=#CC342B +color10=#198844 +color11=#FBA922 +color12=#3971ED +color13=#A36AC7 +color14=#3971ED +color15=#ffffff + +color16=#F96A38 +color17=#3971ED +color18=#282a2e +color19=#373b41 +color20=#b4b7b4 +color21=#e0e0e0 diff --git a/app/src/main/assets/termux-colors/base16-grayscale-dark.properties b/app/src/main/assets/termux-colors/base16-grayscale-dark.properties new file mode 100644 index 0000000..3d0d6c2 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-grayscale-dark.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-grayscale.dark.256.xresources +# Base16 Grayscale +# Scheme: Alexandre Gavioli (https://github.com/Alexx2/) +foreground=#b9b9b9 +background=#101010 +cursor=#b9b9b9 + +color0=#101010 +color1=#7c7c7c +color2=#8e8e8e +color3=#a0a0a0 +color4=#686868 +color5=#747474 +color6=#868686 +color7=#b9b9b9 +color8=#525252 +color9=#7c7c7c +color10=#8e8e8e +color11=#a0a0a0 +color12=#686868 +color13=#747474 +color14=#868686 +color15=#f7f7f7 + +color16=#999999 +color17=#5e5e5e +color18=#252525 +color19=#464646 +color20=#ababab +color21=#e3e3e3 diff --git a/app/src/main/assets/termux-colors/base16-grayscale-light.properties b/app/src/main/assets/termux-colors/base16-grayscale-light.properties new file mode 100644 index 0000000..6512c41 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-grayscale-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-grayscale.light.256.xresources +# Base16 Grayscale +# Scheme: Alexandre Gavioli (https://github.com/Alexx2/) +foreground=#464646 +background=#f7f7f7 +cursor=#464646 + +color0=#101010 +color1=#7c7c7c +color2=#8e8e8e +color3=#a0a0a0 +color4=#686868 +color5=#747474 +color6=#868686 +color7=#b9b9b9 +color8=#525252 +color9=#7c7c7c +color10=#8e8e8e +color11=#a0a0a0 +color12=#686868 +color13=#747474 +color14=#868686 +color15=#f7f7f7 + +color16=#999999 +color17=#5e5e5e +color18=#252525 +color19=#464646 +color20=#ababab +color21=#e3e3e3 diff --git a/app/src/main/assets/termux-colors/base16-greenscreen-dark.properties b/app/src/main/assets/termux-colors/base16-greenscreen-dark.properties new file mode 100644 index 0000000..09212b4 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-greenscreen-dark.properties @@ -0,0 +1,28 @@ +foreground: #00bb00 +background: #001100 +cursor: #00bb00 + +color0: #001100 +color1: #007700 +color2: #00bb00 +color3: #007700 +color4: #009900 +color5: #00bb00 +color6: #005500 +color7: #00bb00 + +color8: #007700 +color9: #007700 +color10: #00bb00 +color11: #007700 +color12: #009900 +color13: #00bb00 +color14: #005500 +color15: #00ff00 + +color16: #009900 +color17: #005500 +color18: #003300 +color19: #005500 +color20: #009900 +color21: #00dd00 diff --git a/app/src/main/assets/termux-colors/base16-greenscreen-light.properties b/app/src/main/assets/termux-colors/base16-greenscreen-light.properties new file mode 100644 index 0000000..7196747 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-greenscreen-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-greenscreen.light.256.xresources +# Base16 Green Screen +# Scheme: Chris Kempson (http://chriskempson.com) +foreground=#005500 +background=#00ff00 +cursor=#005500 + +color0=#001100 +color1=#007700 +color2=#00bb00 +color3=#007700 +color4=#009900 +color5=#00bb00 +color6=#005500 +color7=#00bb00 +color8=#007700 +color9=#007700 +color10=#00bb00 +color11=#007700 +color12=#009900 +color13=#00bb00 +color14=#005500 +color15=#00ff00 + +color16=#009900 +color17=#005500 +color18=#003300 +color19=#005500 +color20=#009900 +color21=#00dd00 diff --git a/app/src/main/assets/termux-colors/base16-harmonic16-dark.properties b/app/src/main/assets/termux-colors/base16-harmonic16-dark.properties new file mode 100644 index 0000000..14e150d --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-harmonic16-dark.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-harmonic16.dark.256.xresources +# Base16 harmonic16 +# Scheme: Jannik Siebert (https://github.com/janniks) +foreground=#cbd6e2 +background=#0b1c2c +cursor=#cbd6e2 + +color0=#0b1c2c +color1=#bf8b56 +color2=#56bf8b +color3=#8bbf56 +color4=#8b56bf +color5=#bf568b +color6=#568bbf +color7=#cbd6e2 +color8=#627e99 +color9=#bf8b56 +color10=#56bf8b +color11=#8bbf56 +color12=#8b56bf +color13=#bf568b +color14=#568bbf +color15=#f7f9fb + +color16=#bfbf56 +color17=#bf5656 +color18=#223b54 +color19=#405c79 +color20=#aabcce +color21=#e5ebf1 diff --git a/app/src/main/assets/termux-colors/base16-harmonic16-light.properties b/app/src/main/assets/termux-colors/base16-harmonic16-light.properties new file mode 100644 index 0000000..64c26ce --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-harmonic16-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-harmonic16.light.256.xresources +# Base16 harmonic16 +# Scheme: Jannik Siebert (https://github.com/janniks) +foreground=#405c79 +background=#f7f9fb +cursor=#405c79 + +color0=#0b1c2c +color1=#bf8b56 +color2=#56bf8b +color3=#8bbf56 +color4=#8b56bf +color5=#bf568b +color6=#568bbf +color7=#cbd6e2 +color8=#627e99 +color9=#bf8b56 +color10=#56bf8b +color11=#8bbf56 +color12=#8b56bf +color13=#bf568b +color14=#568bbf +color15=#f7f9fb + +color16=#bfbf56 +color17=#bf5656 +color18=#223b54 +color19=#405c79 +color20=#aabcce +color21=#e5ebf1 diff --git a/app/src/main/assets/termux-colors/base16-isotope-dark.properties b/app/src/main/assets/termux-colors/base16-isotope-dark.properties new file mode 100644 index 0000000..fe61d97 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-isotope-dark.properties @@ -0,0 +1,32 @@ +# Base16 Isotope +# Scheme: Jan T. Sott + +foreground: #d0d0d0 +background: #000000 +cursor: #d0d0d0 + +color0: #000000 +color1: #ff0000 +color2: #33ff00 +color3: #ff0099 +color4: #0066ff +color5: #cc00ff +color6: #00ffff +color7: #d0d0d0 + +color8: #808080 +color9: #ff0000 +color10: #33ff00 +color11: #ff0099 +color12: #0066ff +color13: #cc00ff +color14: #00ffff +color15: #ffffff + +color16: #ff9900 +color17: #3300ff +color18: #404040 +color19: #606060 +color20: #c0c0c0 +color21: #e0e0e0 + diff --git a/app/src/main/assets/termux-colors/base16-isotope-light.properties b/app/src/main/assets/termux-colors/base16-isotope-light.properties new file mode 100644 index 0000000..3856f3f --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-isotope-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-isotope.light.256.xresources +# Base16 Isotope +# Scheme: Jan T. Sott +foreground=#606060 +background=#ffffff +cursor=#606060 + +color0=#000000 +color1=#ff0000 +color2=#33ff00 +color3=#ff0099 +color4=#0066ff +color5=#cc00ff +color6=#00ffff +color7=#d0d0d0 +color8=#808080 +color9=#ff0000 +color10=#33ff00 +color11=#ff0099 +color12=#0066ff +color13=#cc00ff +color14=#00ffff +color15=#ffffff + +color16=#ff9900 +color17=#3300ff +color18=#404040 +color19=#606060 +color20=#c0c0c0 +color21=#e0e0e0 diff --git a/app/src/main/assets/termux-colors/base16-londontube-dark.properties b/app/src/main/assets/termux-colors/base16-londontube-dark.properties new file mode 100644 index 0000000..a437649 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-londontube-dark.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-londontube.dark.256.xresources +# Base16 London Tube +# Scheme: Jan T. Sott +foreground=#d9d8d8 +background=#231f20 +cursor=#d9d8d8 + +color0=#231f20 +color1=#ee2e24 +color2=#00853e +color3=#ffd204 +color4=#009ddc +color5=#98005d +color6=#85cebc +color7=#d9d8d8 +color8=#737171 +color9=#ee2e24 +color10=#00853e +color11=#ffd204 +color12=#009ddc +color13=#98005d +color14=#85cebc +color15=#ffffff + +color16=#f386a1 +color17=#b06110 +color18=#1c3f95 +color19=#5a5758 +color20=#959ca1 +color21=#e7e7e8 diff --git a/app/src/main/assets/termux-colors/base16-londontube-light.properties b/app/src/main/assets/termux-colors/base16-londontube-light.properties new file mode 100644 index 0000000..0c27029 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-londontube-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-londontube.light.256.xresources +# Base16 London Tube +# Scheme: Jan T. Sott +foreground=#5a5758 +background=#ffffff +cursor=#5a5758 + +color0=#231f20 +color1=#ee2e24 +color2=#00853e +color3=#ffd204 +color4=#009ddc +color5=#98005d +color6=#85cebc +color7=#d9d8d8 +color8=#737171 +color9=#ee2e24 +color10=#00853e +color11=#ffd204 +color12=#009ddc +color13=#98005d +color14=#85cebc +color15=#ffffff + +color16=#f386a1 +color17=#b06110 +color18=#1c3f95 +color19=#5a5758 +color20=#959ca1 +color21=#e7e7e8 diff --git a/app/src/main/assets/termux-colors/base16-marrakesh-dark.properties b/app/src/main/assets/termux-colors/base16-marrakesh-dark.properties new file mode 100644 index 0000000..115eae5 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-marrakesh-dark.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-marrakesh.dark.256.xresources +# Base16 Marrakesh +# Scheme: Alexandre Gavioli (http://github.com/Alexx2/) +foreground=#948e48 +background=#201602 +cursor=#948e48 + +color0=#201602 +color1=#c35359 +color2=#18974e +color3=#a88339 +color4=#477ca1 +color5=#8868b3 +color6=#75a738 +color7=#948e48 +color8=#6c6823 +color9=#c35359 +color10=#18974e +color11=#a88339 +color12=#477ca1 +color13=#8868b3 +color14=#75a738 +color15=#faf0a5 + +color16=#b36144 +color17=#b3588e +color18=#302e00 +color19=#5f5b17 +color20=#86813b +color21=#ccc37a diff --git a/app/src/main/assets/termux-colors/base16-marrakesh-light.properties b/app/src/main/assets/termux-colors/base16-marrakesh-light.properties new file mode 100644 index 0000000..bf68121 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-marrakesh-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-marrakesh.light.256.xresources +# Base16 Marrakesh +# Scheme: Alexandre Gavioli (http://github.com/Alexx2/) +foreground=#5f5b17 +background=#faf0a5 +cursor=#5f5b17 + +color0=#201602 +color1=#c35359 +color2=#18974e +color3=#a88339 +color4=#477ca1 +color5=#8868b3 +color6=#75a738 +color7=#948e48 +color8=#6c6823 +color9=#c35359 +color10=#18974e +color11=#a88339 +color12=#477ca1 +color13=#8868b3 +color14=#75a738 +color15=#faf0a5 + +color16=#b36144 +color17=#b3588e +color18=#302e00 +color19=#5f5b17 +color20=#86813b +color21=#ccc37a diff --git a/app/src/main/assets/termux-colors/base16-materia.properties b/app/src/main/assets/termux-colors/base16-materia.properties new file mode 100644 index 0000000..37644be --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-materia.properties @@ -0,0 +1,21 @@ +foreground=#c9ccd3 +background=#263238 +cursor=#c9ccd3 + +color0=#2c393f +color1=#8bd649 +color2=#82aaff +color3=#89ddff +color4=#ea9560 +color5=#ec5f67 +color6=#ec5f67 +color7=#cdd3de + +color8=#707880 +color9=#8bd649 +color10=#82aaff +color11=#89ddff +color12=#ea9560 +color13=#ec5f67 +color14=#ffffff +color15=#d5dbe6 diff --git a/app/src/main/assets/termux-colors/base16-mocha-dark.properties b/app/src/main/assets/termux-colors/base16-mocha-dark.properties new file mode 100644 index 0000000..2103903 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-mocha-dark.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-mocha.dark.256.xresources +# Base16 Mocha +# Scheme: Chris Kempson (http://chriskempson.com) +foreground=#d0c8c6 +background=#3B3228 +cursor=#d0c8c6 + +color0=#3B3228 +color1=#cb6077 +color2=#beb55b +color3=#f4bc87 +color4=#8ab3b5 +color5=#a89bb9 +color6=#7bbda4 +color7=#d0c8c6 +color8=#7e705a +color9=#cb6077 +color10=#beb55b +color11=#f4bc87 +color12=#8ab3b5 +color13=#a89bb9 +color14=#7bbda4 +color15=#f5eeeb + +color16=#d28b71 +color17=#bb9584 +color18=#534636 +color19=#645240 +color20=#b8afad +color21=#e9e1dd diff --git a/app/src/main/assets/termux-colors/base16-mocha-light.properties b/app/src/main/assets/termux-colors/base16-mocha-light.properties new file mode 100644 index 0000000..9ad25a6 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-mocha-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-mocha.light.256.xresources +# Base16 Mocha +# Scheme: Chris Kempson (http://chriskempson.com) +foreground=#645240 +background=#f5eeeb +cursor=#645240 + +color0=#3B3228 +color1=#cb6077 +color2=#beb55b +color3=#f4bc87 +color4=#8ab3b5 +color5=#a89bb9 +color6=#7bbda4 +color7=#d0c8c6 +color8=#7e705a +color9=#cb6077 +color10=#beb55b +color11=#f4bc87 +color12=#8ab3b5 +color13=#a89bb9 +color14=#7bbda4 +color15=#f5eeeb + +color16=#d28b71 +color17=#bb9584 +color18=#534636 +color19=#645240 +color20=#b8afad +color21=#e9e1dd diff --git a/app/src/main/assets/termux-colors/base16-monokai-dark.properties b/app/src/main/assets/termux-colors/base16-monokai-dark.properties new file mode 100644 index 0000000..553df1a --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-monokai-dark.properties @@ -0,0 +1,29 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/xresources/base16-monokai-256.Xresources +foreground= #f8f8f2 +background= #272822 +cursor= #f8f8f2 + +color0= #272822 +color1= #f92672 +color2= #a6e22e +color3= #f4bf75 +color4= #66d9ef +color5= #ae81ff +color6= #a1efe4 +color7= #f8f8f2 + +color8= #75715e +color9= #f92672 +color10= #a6e22e +color11= #f4bf75 +color12= #66d9ef +color13= #ae81ff +color14= #a1efe4 +color15= #f9f8f5 + +color16= #fd971f +color17= #cc6633 +color18= #383830 +color19= #49483e +color20= #a59f85 +color21= #f5f4f1 diff --git a/app/src/main/assets/termux-colors/base16-monokai-light.properties b/app/src/main/assets/termux-colors/base16-monokai-light.properties new file mode 100644 index 0000000..4b69392 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-monokai-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-monokai.light.256.xresources +# Base16 Monokai +# Scheme: Wimer Hazenberg (http://www.monokai.nl) +foreground=#49483e +background=#f9f8f5 +cursor=#49483e + +color0=#272822 +color1=#f92672 +color2=#a6e22e +color3=#f4bf75 +color4=#66d9ef +color5=#ae81ff +color6=#a1efe4 +color7=#f8f8f2 +color8=#75715e +color9=#f92672 +color10=#a6e22e +color11=#f4bf75 +color12=#66d9ef +color13=#ae81ff +color14=#a1efe4 +color15=#f9f8f5 + +color16=#fd971f +color17=#cc6633 +color18=#383830 +color19=#49483e +color20=#a59f85 +color21=#f5f4f1 diff --git a/app/src/main/assets/termux-colors/base16-ocean-dark.properties b/app/src/main/assets/termux-colors/base16-ocean-dark.properties new file mode 100644 index 0000000..b94d312 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-ocean-dark.properties @@ -0,0 +1,28 @@ +foreground= #c0c5ce +background= #2b303b +cursor= #c0c5ce + +color0= #2b303b +color1= #bf616a +color2= #a3be8c +color3= #ebcb8b +color4= #8fa1b3 +color5= #b48ead +color6= #96b5b4 +color7= #c0c5ce + +color8= #65737e +color9= #bf616a +color10= #a3be8c +color11= #ebcb8b +color12= #8fa1b3 +color13= #b48ead +color14= #96b5b4 +color15= #eff1f5 + +color16= #d08770 +color17= #ab7967 +color18= #343d46 +color19= #4f5b66 +color20= #a7adba +color21= #dfe1e8 \ No newline at end of file diff --git a/app/src/main/assets/termux-colors/base16-ocean-light.properties b/app/src/main/assets/termux-colors/base16-ocean-light.properties new file mode 100644 index 0000000..c6a2f36 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-ocean-light.properties @@ -0,0 +1,30 @@ +foreground: #4f5b66 +background: #eff1f5 +cursor: #4f5b66 + +color0: #2b303b +color1: #bf616a +color2: #a3be8c +color3: #ebcb8b +color4: #8fa1b3 +color5: #b48ead +color6: #96b5b4 +color7: #c0c5ce + +color8: #65737e +color9: #bf616a +color10: #a3be8c +color11: #ebcb8b +color12: #8fa1b3 +color13: #b48ead +color14: #96b5b4 +color15: #eff1f5 + +! Note: colors beyond 15 might not be loaded (e.g., xterm, urxvt), +! use 'shell' template to set these if necessary +color16: #d08770 +color17: #ab7967 +color18: #343d46 +color19: #4f5b66 +color20: #a7adba +color21: #dfe1e8 diff --git a/app/src/main/assets/termux-colors/base16-one-dark.properties b/app/src/main/assets/termux-colors/base16-one-dark.properties new file mode 100644 index 0000000..b32ba5a --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-one-dark.properties @@ -0,0 +1,31 @@ +# https://github.com/aaron-williamson/base16-alacritty/blob/master/colors/base16-onedark-256.yml +# Base16 One dark +# Daniel Pfeifer (http://github.com/purpleKarrot) +foreground=#abb2bf +background=#282c34 +cursor=#abb2bf + +color0=#282c34 +color1=#e06c75 +color2=#98c379 +color3=#e5c07b +color4=#61afef +color5=#c678dd +color6=#56b6c2 +color7=#abb2bf + +color8=#545862 +color9=#e06c75 +color10=#98c379 +color11=#e5c07b +color12=#61afef +color13=#c678dd +color14=#56b6c2 +color15=#c8ccd4 + +color16=#d19a66 +color17=#be5046 +color18=#353b45 +color19=#3e4451 +color20=#565c64 +color21=#b6bdca diff --git a/app/src/main/assets/termux-colors/base16-one-light.properties b/app/src/main/assets/termux-colors/base16-one-light.properties new file mode 100644 index 0000000..fc0f64b --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-one-light.properties @@ -0,0 +1,31 @@ +# https://github.com/aaron-williamson/base16-alacritty/blob/master/colors/base16-one-light-256.yml +# Base16 One light +# Daniel Pfeifer (http://github.com/purpleKarrot) +foreground=#383a42 +background=#fafafa +cursor=#383a42 + +color0=#fafafa +color1=#ca1243 +color2=#50a14f +color3=#c18401 +color4=#4078f2 +color5=#a626a4 +color6=#0184bc +color7=#383a42 + +color8=#a0a1a7 +color9=#ca1243 +color10=#50a14f +color11=#c18401 +color12=#4078f2 +color13=#a626a4 +color14=#0184bc +color15=#090a0b + +color16=#d75f00 +color17=#986801 +color18=#f0f0f1 +color19=#e5e5e6 +color20=#696c77 +color21=#202227 diff --git a/app/src/main/assets/termux-colors/base16-paraiso-dark.properties b/app/src/main/assets/termux-colors/base16-paraiso-dark.properties new file mode 100644 index 0000000..ed1c833 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-paraiso-dark.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-paraiso.dark.256.xresources +# Base16 Paraiso +# Scheme: Jan T. Sott +foreground=#a39e9b +background=#2f1e2e +cursor=#a39e9b + +color0=#2f1e2e +color1=#ef6155 +color2=#48b685 +color3=#fec418 +color4=#06b6ef +color5=#815ba4 +color6=#5bc4bf +color7=#a39e9b +color8=#776e71 +color9=#ef6155 +color10=#48b685 +color11=#fec418 +color12=#06b6ef +color13=#815ba4 +color14=#5bc4bf +color15=#e7e9db + +color16=#f99b15 +color17=#e96ba8 +color18=#41323f +color19=#4f424c +color20=#8d8687 +color21=#b9b6b0 diff --git a/app/src/main/assets/termux-colors/base16-paraiso-light.properties b/app/src/main/assets/termux-colors/base16-paraiso-light.properties new file mode 100644 index 0000000..a1e43d7 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-paraiso-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-paraiso.light.256.xresources +# Base16 Paraiso +# Scheme: Jan T. Sott +foreground=#4f424c +background=#e7e9db +cursor=#4f424c + +color0=#2f1e2e +color1=#ef6155 +color2=#48b685 +color3=#fec418 +color4=#06b6ef +color5=#815ba4 +color6=#5bc4bf +color7=#a39e9b +color8=#776e71 +color9=#ef6155 +color10=#48b685 +color11=#fec418 +color12=#06b6ef +color13=#815ba4 +color14=#5bc4bf +color15=#e7e9db + +color16=#f99b15 +color17=#e96ba8 +color18=#41323f +color19=#4f424c +color20=#8d8687 +color21=#b9b6b0 diff --git a/app/src/main/assets/termux-colors/base16-railscasts-dark.properties b/app/src/main/assets/termux-colors/base16-railscasts-dark.properties new file mode 100644 index 0000000..f194dfa --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-railscasts-dark.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-railscasts.dark.256.xresources +# Base16 Railscasts +# Scheme: Ryan Bates (http://railscasts.com) +foreground=#e6e1dc +background=#2b2b2b +cursor=#e6e1dc + +color0=#2b2b2b +color1=#da4939 +color2=#a5c261 +color3=#ffc66d +color4=#6d9cbe +color5=#b6b3eb +color6=#519f50 +color7=#e6e1dc +color8=#5a647e +color9=#da4939 +color10=#a5c261 +color11=#ffc66d +color12=#6d9cbe +color13=#b6b3eb +color14=#519f50 +color15=#f9f7f3 + +color16=#cc7833 +color17=#bc9458 +color18=#272935 +color19=#3a4055 +color20=#d4cfc9 +color21=#f4f1ed diff --git a/app/src/main/assets/termux-colors/base16-railscasts-light.properties b/app/src/main/assets/termux-colors/base16-railscasts-light.properties new file mode 100644 index 0000000..55fc912 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-railscasts-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-railscasts.light.256.xresources +# Base16 Railscasts +# Scheme: Ryan Bates (http://railscasts.com) +foreground=#3a4055 +background=#f9f7f3 +cursor=#3a4055 + +color0=#2b2b2b +color1=#da4939 +color2=#a5c261 +color3=#ffc66d +color4=#6d9cbe +color5=#b6b3eb +color6=#519f50 +color7=#e6e1dc +color8=#5a647e +color9=#da4939 +color10=#a5c261 +color11=#ffc66d +color12=#6d9cbe +color13=#b6b3eb +color14=#519f50 +color15=#f9f7f3 + +color16=#cc7833 +color17=#bc9458 +color18=#272935 +color19=#3a4055 +color20=#d4cfc9 +color21=#f4f1ed diff --git a/app/src/main/assets/termux-colors/base16-shapeshifter-dark.properties b/app/src/main/assets/termux-colors/base16-shapeshifter-dark.properties new file mode 100644 index 0000000..0a1ced3 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-shapeshifter-dark.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-shapeshifter.dark.256.xresources +# Base16 shapeshifter +# Scheme: Tyler Benziger (http://tybenz.com) +foreground=#ababab +background=#000000 +cursor=#ababab + +color0=#000000 +color1=#e92f2f +color2=#0ed839 +color3=#dddd13 +color4=#3b48e3 +color5=#f996e2 +color6=#23edda +color7=#ababab +color8=#343434 +color9=#e92f2f +color10=#0ed839 +color11=#dddd13 +color12=#3b48e3 +color13=#f996e2 +color14=#23edda +color15=#f9f9f9 + +color16=#e09448 +color17=#69542d +color18=#040404 +color19=#102015 +color20=#555555 +color21=#e0e0e0 diff --git a/app/src/main/assets/termux-colors/base16-shapeshifter-light.properties b/app/src/main/assets/termux-colors/base16-shapeshifter-light.properties new file mode 100644 index 0000000..0d14d1b --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-shapeshifter-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-shapeshifter.light.256.xresources +# Base16 shapeshifter +# Scheme: Tyler Benziger (http://tybenz.com) +foreground=#102015 +background=#f9f9f9 +cursor=#102015 + +color0=#000000 +color1=#e92f2f +color2=#0ed839 +color3=#dddd13 +color4=#3b48e3 +color5=#f996e2 +color6=#23edda +color7=#ababab +color8=#343434 +color9=#e92f2f +color10=#0ed839 +color11=#dddd13 +color12=#3b48e3 +color13=#f996e2 +color14=#23edda +color15=#f9f9f9 + +color16=#e09448 +color17=#69542d +color18=#040404 +color19=#102015 +color20=#555555 +color21=#e0e0e0 diff --git a/app/src/main/assets/termux-colors/base16-snazzy.properties b/app/src/main/assets/termux-colors/base16-snazzy.properties new file mode 100644 index 0000000..aff68cc --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-snazzy.properties @@ -0,0 +1,30 @@ +# Base16 Snazzy +# Based on https://github.com/aaron-williamson/base16-alacritty/blob/025688c7158eb8a3421cb2b3612e77915dce8c2a/colors/base16-snazzy-256.yml +foreground=#e2e4e5 +background=#282a36 +cursor=#e2e4e5 + +color0=#282a36 +color1=#ff5c57 +color2=#5af78e +color3=#f3f99d +color4=#57c7ff +color5=#ff6ac1 +color6=#9aedfe +color7=#e2e4e5 + +color8=#78787e +color9=#ff5c57 +color10=#5af78e +color11=#f3f99d +color12=#57c7ff +color13=#ff6ac1 +color14=#9aedfe +color15=#f1f1f0 + +color16=#ff9f43 +color17=#b2643c +color18=#34353e +color19=#43454f +color20=#a5a5a9 +color21=#eff0eb diff --git a/app/src/main/assets/termux-colors/base16-solarized-dark.properties b/app/src/main/assets/termux-colors/base16-solarized-dark.properties new file mode 100644 index 0000000..80d27be --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-solarized-dark.properties @@ -0,0 +1,31 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-solarized.dark.xresources +# Base16 Solarized +# Scheme: Ethan Schoonover (http=//ethanschoonover.com/solarized) +background=#002b36 +foreground=#93a1a1 +cursor=#93a1a1 + +color0=#002b36 +color1=#dc322f +color2=#859900 +color3=#b58900 +color4=#268bd2 +color5=#6c71c4 +color6=#2aa198 +color7=#93a1a1 +color8=#657b83 +color9=#dc322f +color10=#859900 +color11=#b58900 +color12=#268bd2 +color13=#6c71c4 +color14=#2aa198 +color15=#fdf6e3 + +color16=#cb4b16 +color17=#d33682 +color18=#073642 +color19=#586e75 +color20=#839496 +color21=#eee8d5 + diff --git a/app/src/main/assets/termux-colors/base16-solarized-light.properties b/app/src/main/assets/termux-colors/base16-solarized-light.properties new file mode 100644 index 0000000..4e629f4 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-solarized-light.properties @@ -0,0 +1,31 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-solarized.light.xresources +# Base16 Solarized +# Scheme: Ethan Schoonover (http://ethanschoonover.com/solarized) +foreground=#586e75 +background=#fdf6e3 +cursor=#586e75 + +color0=#002b36 +color1=#dc322f +color2=#859900 +color3=#b58900 +color4=#268bd2 +color5=#6c71c4 +color6=#2aa198 +color7=#93a1a1 +color8=#657b83 +color9=#cb4b16 +color10=#073642 +color11=#586e75 +color12=#839496 +color13=#eee8d5 +color14=#d33682 +color15=#fdf6e3 + +color16=#cb4b16 +color17=#d33682 +color18=#073642 +color19=#586e75 +color20=#839496 +color21=#eee8d5 + diff --git a/app/src/main/assets/termux-colors/base16-summerfruit-dark.properties b/app/src/main/assets/termux-colors/base16-summerfruit-dark.properties new file mode 100644 index 0000000..02cac9b --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-summerfruit-dark.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-summerfruit.dark.256.xresources +# Base16 Summerfruit +# Scheme: Christopher Corley (http://cscorley.github.io/) +foreground=#D0D0D0 +background=#151515 +cursor=#D0D0D0 + +color0=#151515 +color1=#FF0086 +color2=#00C918 +color3=#ABA800 +color4=#3777E6 +color5=#AD00A1 +color6=#1faaaa +color7=#D0D0D0 +color8=#505050 +color9=#FF0086 +color10=#00C918 +color11=#ABA800 +color12=#3777E6 +color13=#AD00A1 +color14=#1faaaa +color15=#FFFFFF + +color16=#FD8900 +color17=#cc6633 +color18=#202020 +color19=#303030 +color20=#B0B0B0 +color21=#E0E0E0 diff --git a/app/src/main/assets/termux-colors/base16-summerfruit-light.properties b/app/src/main/assets/termux-colors/base16-summerfruit-light.properties new file mode 100644 index 0000000..36fff33 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-summerfruit-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-summerfruit.light.256.xresources +# Base16 Summerfruit +# Scheme: Christopher Corley (http://cscorley.github.io/) +foreground=#303030 +background=#FFFFFF +cursor=#303030 + +color0=#151515 +color1=#FF0086 +color2=#00C918 +color3=#ABA800 +color4=#3777E6 +color5=#AD00A1 +color6=#1faaaa +color7=#D0D0D0 +color8=#505050 +color9=#FF0086 +color10=#00C918 +color11=#ABA800 +color12=#3777E6 +color13=#AD00A1 +color14=#1faaaa +color15=#FFFFFF + +color16=#FD8900 +color17=#cc6633 +color18=#202020 +color19=#303030 +color20=#B0B0B0 +color21=#E0E0E0 diff --git a/app/src/main/assets/termux-colors/base16-tomorrow-dark.properties b/app/src/main/assets/termux-colors/base16-tomorrow-dark.properties new file mode 100644 index 0000000..0aadabe --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-tomorrow-dark.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-tomorrow.dark.256.xresources +# Base16 Tomorrow +# Scheme: Chris Kempson (http://chriskempson.com) +foreground=#c5c8c6 +background=#1d1f21 +cursor=#c5c8c6 + +color0=#1d1f21 +color1=#cc6666 +color2=#b5bd68 +color3=#f0c674 +color4=#81a2be +color5=#b294bb +color6=#8abeb7 +color7=#c5c8c6 +color8=#969896 +color9=#cc6666 +color10=#b5bd68 +color11=#f0c674 +color12=#81a2be +color13=#b294bb +color14=#8abeb7 +color15=#ffffff + +color16=#de935f +color17=#a3685a +color18=#282a2e +color19=#373b41 +color20=#b4b7b4 +color21=#e0e0e0 diff --git a/app/src/main/assets/termux-colors/base16-tomorrow-light.properties b/app/src/main/assets/termux-colors/base16-tomorrow-light.properties new file mode 100644 index 0000000..8b09191 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-tomorrow-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-tomorrow.light.256.xresources +# Base16 Tomorrow +# Scheme: Chris Kempson (http://chriskempson.com) +foreground=#373b41 +background=#ffffff +cursor=#373b41 + +color0=#1d1f21 +color1=#cc6666 +color2=#b5bd68 +color3=#f0c674 +color4=#81a2be +color5=#b294bb +color6=#8abeb7 +color7=#c5c8c6 +color8=#969896 +color9=#cc6666 +color10=#b5bd68 +color11=#f0c674 +color12=#81a2be +color13=#b294bb +color14=#8abeb7 +color15=#ffffff + +color16=#de935f +color17=#a3685a +color18=#282a2e +color19=#373b41 +color20=#b4b7b4 +color21=#e0e0e0 diff --git a/app/src/main/assets/termux-colors/base16-twilight-dark.properties b/app/src/main/assets/termux-colors/base16-twilight-dark.properties new file mode 100644 index 0000000..9678a13 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-twilight-dark.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-twilight.dark.256.xresources +# Base16 Twilight +# Scheme: David Hart (http://hart-dev.com) +foreground=#a7a7a7 +background=#1e1e1e +cursor=#a7a7a7 + +color0=#1e1e1e +color1=#cf6a4c +color2=#8f9d6a +color3=#f9ee98 +color4=#7587a6 +color5=#9b859d +color6=#afc4db +color7=#a7a7a7 +color8=#5f5a60 +color9=#cf6a4c +color10=#8f9d6a +color11=#f9ee98 +color12=#7587a6 +color13=#9b859d +color14=#afc4db +color15=#ffffff + +color16=#cda869 +color17=#9b703f +color18=#323537 +color19=#464b50 +color20=#838184 +color21=#c3c3c3 diff --git a/app/src/main/assets/termux-colors/base16-twilight-light.properties b/app/src/main/assets/termux-colors/base16-twilight-light.properties new file mode 100644 index 0000000..e2f38d2 --- /dev/null +++ b/app/src/main/assets/termux-colors/base16-twilight-light.properties @@ -0,0 +1,30 @@ +# https://github.com/chriskempson/base16-xresources/blob/master/base16-twilight.light.256.xresources +# Base16 Twilight +# Scheme: David Hart (http://hart-dev.com) +foreground=#464b50 +background=#ffffff +cursor=#464b50 + +color0=#1e1e1e +color1=#cf6a4c +color2=#8f9d6a +color3=#f9ee98 +color4=#7587a6 +color5=#9b859d +color6=#afc4db +color7=#a7a7a7 +color8=#5f5a60 +color9=#cf6a4c +color10=#8f9d6a +color11=#f9ee98 +color12=#7587a6 +color13=#9b859d +color14=#afc4db +color15=#ffffff + +color16=#cda869 +color17=#9b703f +color18=#323537 +color19=#464b50 +color20=#838184 +color21=#c3c3c3 diff --git a/app/src/main/assets/termux-colors/black-on-white.properties b/app/src/main/assets/termux-colors/black-on-white.properties new file mode 100644 index 0000000..4532f90 --- /dev/null +++ b/app/src/main/assets/termux-colors/black-on-white.properties @@ -0,0 +1,259 @@ +background: #FFFFFF +foreground: #000000 + +color0:#000000 +color1:#000000 +color2:#000000 +color3:#000000 +color4:#000000 +color5:#000000 +color6:#000000 +color7:#000000 +color8:#000000 +color9:#000000 +color10:#000000 +color11:#000000 +color12:#000000 +color13:#000000 +color14:#000000 +color15:#000000 +color16:#000000 +color17:#000000 +color18:#000000 +color19:#000000 +color20:#000000 +color21:#000000 +color22:#000000 +color23:#000000 +color24:#000000 +color25:#000000 +color26:#000000 +color27:#000000 +color28:#000000 +color29:#000000 +color30:#000000 +color31:#000000 +color32:#000000 +color33:#000000 +color34:#000000 +color35:#000000 +color36:#000000 +color37:#000000 +color38:#000000 +color39:#000000 +color40:#000000 +color41:#000000 +color42:#000000 +color43:#000000 +color44:#000000 +color45:#000000 +color46:#000000 +color47:#000000 +color48:#000000 +color49:#000000 +color50:#000000 +color51:#000000 +color52:#000000 +color53:#000000 +color54:#000000 +color55:#000000 +color56:#000000 +color57:#000000 +color58:#000000 +color59:#000000 +color60:#000000 +color61:#000000 +color62:#000000 +color63:#000000 +color64:#000000 +color65:#000000 +color66:#000000 +color67:#000000 +color68:#000000 +color69:#000000 +color70:#000000 +color71:#000000 +color72:#000000 +color73:#000000 +color74:#000000 +color75:#000000 +color76:#000000 +color77:#000000 +color78:#000000 +color79:#000000 +color80:#000000 +color81:#000000 +color82:#000000 +color83:#000000 +color84:#000000 +color85:#000000 +color86:#000000 +color87:#000000 +color88:#000000 +color89:#000000 +color90:#000000 +color91:#000000 +color92:#000000 +color93:#000000 +color94:#000000 +color95:#000000 +color96:#000000 +color97:#000000 +color98:#000000 +color99:#000000 +color100:#000000 +color101:#000000 +color102:#000000 +color103:#000000 +color104:#000000 +color105:#000000 +color106:#000000 +color107:#000000 +color108:#000000 +color109:#000000 +color110:#000000 +color111:#000000 +color112:#000000 +color113:#000000 +color114:#000000 +color115:#000000 +color116:#000000 +color117:#000000 +color118:#000000 +color119:#000000 +color120:#000000 +color121:#000000 +color122:#000000 +color123:#000000 +color124:#000000 +color125:#000000 +color126:#000000 +color127:#000000 +color128:#000000 +color129:#000000 +color130:#000000 +color131:#000000 +color132:#000000 +color133:#000000 +color134:#000000 +color135:#000000 +color136:#000000 +color137:#000000 +color138:#000000 +color139:#000000 +color140:#000000 +color141:#000000 +color142:#000000 +color143:#000000 +color144:#000000 +color145:#000000 +color146:#000000 +color147:#000000 +color148:#000000 +color149:#000000 +color150:#000000 +color151:#000000 +color152:#000000 +color153:#000000 +color154:#000000 +color155:#000000 +color156:#000000 +color157:#000000 +color158:#000000 +color159:#000000 +color160:#000000 +color161:#000000 +color162:#000000 +color163:#000000 +color164:#000000 +color165:#000000 +color166:#000000 +color167:#000000 +color168:#000000 +color169:#000000 +color170:#000000 +color171:#000000 +color172:#000000 +color173:#000000 +color174:#000000 +color175:#000000 +color176:#000000 +color177:#000000 +color178:#000000 +color179:#000000 +color180:#000000 +color181:#000000 +color182:#000000 +color183:#000000 +color184:#000000 +color185:#000000 +color186:#000000 +color187:#000000 +color188:#000000 +color189:#000000 +color190:#000000 +color191:#000000 +color192:#000000 +color193:#000000 +color194:#000000 +color195:#000000 +color196:#000000 +color197:#000000 +color198:#000000 +color199:#000000 +color200:#000000 +color201:#000000 +color202:#000000 +color203:#000000 +color204:#000000 +color205:#000000 +color206:#000000 +color207:#000000 +color208:#000000 +color209:#000000 +color210:#000000 +color211:#000000 +color212:#000000 +color213:#000000 +color214:#000000 +color215:#000000 +color216:#000000 +color217:#000000 +color218:#000000 +color219:#000000 +color220:#000000 +color221:#000000 +color222:#000000 +color223:#000000 +color224:#000000 +color225:#000000 +color226:#000000 +color227:#000000 +color228:#000000 +color229:#000000 +color230:#000000 +color231:#000000 +color232:#000000 +color233:#000000 +color234:#000000 +color235:#000000 +color236:#000000 +color237:#000000 +color238:#000000 +color239:#000000 +color240:#000000 +color241:#000000 +color242:#000000 +color243:#000000 +color244:#000000 +color245:#000000 +color246:#000000 +color247:#000000 +color248:#000000 +color249:#000000 +color250:#000000 +color251:#000000 +color252:#000000 +color253:#000000 +color254:#000000 +color255:#000000 diff --git a/app/src/main/assets/termux-colors/catppuccin-frappe.properties b/app/src/main/assets/termux-colors/catppuccin-frappe.properties new file mode 100644 index 0000000..f134930 --- /dev/null +++ b/app/src/main/assets/termux-colors/catppuccin-frappe.properties @@ -0,0 +1,25 @@ +# https://github.com/catppuccin/catppuccin +foreground=#c6d0f5 +background=#303446 +cursor=#f2d5cf + +color0=#51576d +color1=#e78284 +color2=#a6d189 +color3=#e5c890 +color4=#8caaee +color5=#f4b8e4 +color6=#81c8be +color7=#b5bfe2 + +color8=#626880 +color9=#e78284 +color10=#a6d189 +color11=#e5c890 +color12=#8caaee +color13=#f4b8e4 +color14=#81c8be +color15=#a5adce + +color16=#ef9f76 +color17=#f2d5cf diff --git a/app/src/main/assets/termux-colors/catppuccin-latte.properties b/app/src/main/assets/termux-colors/catppuccin-latte.properties new file mode 100644 index 0000000..6296e30 --- /dev/null +++ b/app/src/main/assets/termux-colors/catppuccin-latte.properties @@ -0,0 +1,25 @@ +# https://github.com/catppuccin/catppuccin +foreground=#4c4f69 +background=#eff1f5 +cursor=#dc8a78 + +color0=#5c5f77 +color1=#d20f39 +color2=#40a02b +color3=#df8e1d +color4=#1e66f5 +color5=#ea76cb +color6=#179299 +color7=#acb0be + +color8=#acb0be +color9=#d20f39 +color10=#40a02b +color11=#df8e1d +color12=#1e66f5 +color13=#ea76cb +color14=#179299 +color15=#bcc0cc + +color16=#fe640b +color17=#dc8a78 diff --git a/app/src/main/assets/termux-colors/catppuccin-macchiato.properties b/app/src/main/assets/termux-colors/catppuccin-macchiato.properties new file mode 100644 index 0000000..dd6bfb0 --- /dev/null +++ b/app/src/main/assets/termux-colors/catppuccin-macchiato.properties @@ -0,0 +1,25 @@ +# https://github.com/catppuccin/catppuccin +foreground=#cad3f5 +background=#24273a +cursor=#f4dbd6 + +color0=#494d64 +color1=#ed8796 +color2=#a6da95 +color3=#eed49f +color4=#8aadf4 +color5=#f5bde6 +color6=#8bd5ca +color7=#b8c0e0 + +color8=#5b6078 +color9=#ed8796 +color10=#a6da95 +color11=#eed49f +color12=#8aadf4 +color13=#f5bde6 +color14=#8bd5ca +color15=#a5adcb + +color16=#f5a97f +color17=#f4dbd6 diff --git a/app/src/main/assets/termux-colors/catppuccin-mocha.properties b/app/src/main/assets/termux-colors/catppuccin-mocha.properties new file mode 100644 index 0000000..acc9f3d --- /dev/null +++ b/app/src/main/assets/termux-colors/catppuccin-mocha.properties @@ -0,0 +1,25 @@ +# https://github.com/catppuccin/catppuccin +foreground=#cdd6f4 +background=#1e1e2e +cursor=#f5e0dc + +color0=#45475a +color1=#f38ba8 +color2=#a6e3a1 +color3=#f9e2af +color4=#89b4fa +color5=#f5c2e7 +color6=#94e2d5 +color7=#bac2de + +color8=#585b70 +color9=#f38ba8 +color10=#a6e3a1 +color11=#f9e2af +color12=#89b4fa +color13=#f5c2e7 +color14=#94e2d5 +color15=#a6adc8 + +color16=#fab387 +color17=#f5e0dc diff --git a/app/src/main/assets/termux-colors/dracula.properties b/app/src/main/assets/termux-colors/dracula.properties new file mode 100644 index 0000000..f35e857 --- /dev/null +++ b/app/src/main/assets/termux-colors/dracula.properties @@ -0,0 +1,31 @@ +# https://draculatheme.com/ +# https://github.com/dracula/xresources/blob/master/Xresources +# special +foreground=#f8f8f2 +cursor=#f8f8f2 +background=#282a36 +# black +color0=#000000 +color8=#4d4d4d +# red +color1=#ff5555 +color9=#ff6e67 +# green +color2=#50fa7b +color10=#5af78e +# yellow +color3=#f1fa8c +color11=#f4f99d +# blue +color4=#bd93f9 +color12=#caa9fa +# magenta +color5=#ff79c6 +color13=#ff92d0 +# cyan +color6=#8be9fd +color14=#9aedfe +# white +color7=#bfbfbf +color15=#e6e6e6 + diff --git a/app/src/main/assets/termux-colors/e-ink-color.properties b/app/src/main/assets/termux-colors/e-ink-color.properties new file mode 100644 index 0000000..345d1b1 --- /dev/null +++ b/app/src/main/assets/termux-colors/e-ink-color.properties @@ -0,0 +1,21 @@ +background=#ffffff +foreground=#000000 +cursor=#c0c0c0 + +color0=#000000 +color1=#800000 +color2=#008000 +color3=#808000 +color4=#000080 +color5=#800080 +color6=#008080 +color7=#c0c0c0 + +color8=#808080 +color9=#ff0000 +color10=#00ff00 +color11=#ffff00 +color12=#0000ff +color13=#ff00ff +color14=#00ffff +color15=#ffffff diff --git a/app/src/main/assets/termux-colors/e-ink.properties b/app/src/main/assets/termux-colors/e-ink.properties new file mode 100644 index 0000000..d51c326 --- /dev/null +++ b/app/src/main/assets/termux-colors/e-ink.properties @@ -0,0 +1,20 @@ +background: #FFFFFF +foreground: #000000 +cursor=#c0c0c0 + +color0=#101010 +color1=#7c7c7c +color2=#8e8e8e +color3=#a0a0a0 +color4=#686868 +color5=#747474 +color6=#868686 +color7=#b9b9b9 +color8=#525252 +color9=#7c7c7c +color10=#8e8e8e +color11=#a0a0a0 +color12=#686868 +color13=#747474 +color14=#868686 +color15=#f7f7f7 diff --git a/app/src/main/assets/termux-colors/gnometerm-new.properties b/app/src/main/assets/termux-colors/gnometerm-new.properties new file mode 100644 index 0000000..350d0b3 --- /dev/null +++ b/app/src/main/assets/termux-colors/gnometerm-new.properties @@ -0,0 +1,19 @@ +# Gnome terminal 42 coloscheme +# https://github.com/termux/termux-styling/issues/164 + +color0: #171421 +color1: #c01c28 +color2: #26a269 +color3: #a2734c +color4: #12488b +color5: #a347ba +color6: #2aa1b3 +color7: #d0cfcc +color8: #5e5c64 +color9: #f66151 +color10: #33d17a +color11: #e9ad0c +color12: #2a7bde +color13: #c061cb +color14: #33c7de +color15: #ffffff diff --git a/app/src/main/assets/termux-colors/gnometerm.properties b/app/src/main/assets/termux-colors/gnometerm.properties new file mode 100644 index 0000000..21ad281 --- /dev/null +++ b/app/src/main/assets/termux-colors/gnometerm.properties @@ -0,0 +1,33 @@ +# http://www.xcolors.net/dl/gnometerm + +# Black +color0: #000000 +color8: #555753 + +# Red +color1: #cc0000 +color9: #ef2929 + +# Green +color2: #4e9a06 +color10: #8ae234 + +# Yellow +color3: #c4a000 +color11: #fce94f + +# Blue +color4: #3465a4 +color12: #729fcf + +# Magenta +color5: #75507b +color13: #ad7fa8 + +# Cyan +color6: #06989a +color14: #34e2e2 + +# White +color7: #d3d7cf +color15: #eeeeec diff --git a/app/src/main/assets/termux-colors/gotham.properties b/app/src/main/assets/termux-colors/gotham.properties new file mode 100644 index 0000000..9fd0b45 --- /dev/null +++ b/app/src/main/assets/termux-colors/gotham.properties @@ -0,0 +1,28 @@ +# https://github.com/whatyouhide/gotham-contrib/blob/master/xresources/gotham +foreground: #98d1ce +background: #0a0f14 +cursor: #98d1ce + +color0: #0a0f14 +color8: #10151b + +color1: #c33027 +color9: #d26939 + +color2: #26a98b +color10: #081f2d + +color3: #edb54b +color11: #245361 + +color4: #195465 +color12: #093748 + +color5: #4e5165 +color13: #888ba5 + +color6: #33859d +color14: #599caa + +color7: #98d1ce +color15: #d3ebe9 diff --git a/app/src/main/assets/termux-colors/gruvbox-dark.properties b/app/src/main/assets/termux-colors/gruvbox-dark.properties new file mode 100644 index 0000000..8b5bd43 --- /dev/null +++ b/app/src/main/assets/termux-colors/gruvbox-dark.properties @@ -0,0 +1,37 @@ +! ----------------------------------------------------------------------------- +! File: gruvbox-dark.xresources +! Description: Retro groove colorscheme generalized +! Author: morhetz +! Source: https://github.com/morhetz/gruvbox-generalized +! Last Modified: 6 Sep 2014 +! ----------------------------------------------------------------------------- + +! hard contrast: background: #1d2021 +background: #1d2021 +! background: #282828 +! soft contrast: background: #32302f +foreground: #ebdbb2 +! Black + DarkGrey +color0: #282828 +color8: #928374 +! DarkRed + Red +color1: #cc241d +color9: #fb4934 +! DarkGreen + Green +color2: #98971a +color10: #b8bb26 +! DarkYellow + Yellow +color3: #d79921 +color11: #fabd2f +! DarkBlue + Blue +color4: #458588 +color12: #83a598 +! DarkMagenta + Magenta +color5: #b16286 +color13: #d3869b +! DarkCyan + Cyan +color6: #689d6a +color14: #8ec07c +! LightGrey + White +color7: #a89984 +color15: #ebdbb2 \ No newline at end of file diff --git a/app/src/main/assets/termux-colors/gruvbox-light.properties b/app/src/main/assets/termux-colors/gruvbox-light.properties new file mode 100644 index 0000000..7283412 --- /dev/null +++ b/app/src/main/assets/termux-colors/gruvbox-light.properties @@ -0,0 +1,37 @@ +! ----------------------------------------------------------------------------- +! File: gruvbox-light.xresources +! Description: Retro groove colorscheme generalized +! Author: morhetz +! Source: https://github.com/morhetz/gruvbox-generalized +! Last Modified: 6 Sep 2014 +! ----------------------------------------------------------------------------- + +! hard contrast: background: #f9f5d7 +background: #f9f5d7 +! background: #fbf1c7 +! soft contrast: background: #f2e5bc +foreground: #3c3836 +! Black + DarkGrey +color0: #fdf4c1 +color8: #928374 +! DarkRed + Red +color1: #cc241d +color9: #9d0006 +! DarkGreen + Green +color2: #98971a +color10: #79740e +! DarkYellow + Yellow +color3: #d79921 +color11: #b57614 +! DarkBlue + Blue +color4: #458588 +color12: #076678 +! DarkMagenta + Magenta +color5: #b16286 +color13: #8f3f71 +! DarkCyan + Cyan +color6: #689d6a +color14: #427b58 +! LightGrey + White +color7: #7c6f64 +color15: #3c3836 \ No newline at end of file diff --git a/app/src/main/assets/termux-colors/gruvbox-material-dark-hard.properties b/app/src/main/assets/termux-colors/gruvbox-material-dark-hard.properties new file mode 100644 index 0000000..bef1b8a --- /dev/null +++ b/app/src/main/assets/termux-colors/gruvbox-material-dark-hard.properties @@ -0,0 +1,26 @@ +background: #1D2021 +foreground: #D4BE98 + +color0: #665C54 +color8: #928374 + +color1: #EA6962 +color9: #EA6962 + +color2: #A9B665 +color10: #A9B665 + +color3: #D8A657 +color11: #D8A657 + +color4: #7DAEA3 +color12: #7DAEA3 + +color5: #D3869B +color13: #D3869B + +color6: #89B482 +color14: #89B482 + +color7: #D4BE98 +color15: #D4BE98 diff --git a/app/src/main/assets/termux-colors/gruvbox-material-dark-medium.properties b/app/src/main/assets/termux-colors/gruvbox-material-dark-medium.properties new file mode 100644 index 0000000..77e4421 --- /dev/null +++ b/app/src/main/assets/termux-colors/gruvbox-material-dark-medium.properties @@ -0,0 +1,26 @@ +background: #282828 +foreground: #D4BE98 + +color0: #665C54 +color8: #928374 + +color1: #EA6962 +color9: #EA6962 + +color2: #A9B665 +color10: #A9B665 + +color3: #D8A657 +color11: #D8A657 + +color4: #7DAEA3 +color12: #7DAEA3 + +color5: #D3869B +color13: #D3869B + +color6: #89B482 +color14: #89B482 + +color7: #D4BE98 +color15: #D4BE98 diff --git a/app/src/main/assets/termux-colors/gruvbox-material-dark-soft.properties b/app/src/main/assets/termux-colors/gruvbox-material-dark-soft.properties new file mode 100644 index 0000000..bc46d66 --- /dev/null +++ b/app/src/main/assets/termux-colors/gruvbox-material-dark-soft.properties @@ -0,0 +1,26 @@ +background: #32302F +foreground: #D4BE98 + +color0: #665C54 +color8: #928374 + +color1: #EA6962 +color9: #EA6962 + +color2: #A9B665 +color10: #A9B665 + +color3: #D8A657 +color11: #D8A657 + +color4: #7DAEA3 +color12: #7DAEA3 + +color5: #D3869B +color13: #D3869B + +color6: #89B482 +color14: #89B482 + +color7: #D4BE98 +color15: #D4BE98 diff --git a/app/src/main/assets/termux-colors/gruvbox-material-light-hard.properties b/app/src/main/assets/termux-colors/gruvbox-material-light-hard.properties new file mode 100644 index 0000000..2163cf4 --- /dev/null +++ b/app/src/main/assets/termux-colors/gruvbox-material-light-hard.properties @@ -0,0 +1,26 @@ +background: #F9F5D7 +foreground: #654735 + +color0: #504945 +color8: #504945 + +color1: #C14A4A +color9: #C14A4A + +color2: #6C782E +color10: #6C782E + +color3: #B47109 +color11: #B47109 + +color4: #45707A +color12: #45707A + +color5: #945E80 +color13: #945E80 + +color6: #4C7A5D +color14: #4C7A5D + +color7: #D4BE98 +color15: #D4BE98 diff --git a/app/src/main/assets/termux-colors/gruvbox-material-light-medium.properties b/app/src/main/assets/termux-colors/gruvbox-material-light-medium.properties new file mode 100644 index 0000000..90566e3 --- /dev/null +++ b/app/src/main/assets/termux-colors/gruvbox-material-light-medium.properties @@ -0,0 +1,26 @@ +background: #FBF1C7 +foreground: #654735 + +color0: #504945 +color8: #504945 + +color1: #C14A4A +color9: #C14A4A + +color2: #6C782E +color10: #6C782E + +color3: #B47109 +color11: #B47109 + +color4: #45707A +color12: #45707A + +color5: #945E80 +color13: #945E80 + +color6: #4C7A5D +color14: #4C7A5D + +color7: #D4BE98 +color15: #D4BE98 diff --git a/app/src/main/assets/termux-colors/gruvbox-material-light-soft.properties b/app/src/main/assets/termux-colors/gruvbox-material-light-soft.properties new file mode 100644 index 0000000..1450ae6 --- /dev/null +++ b/app/src/main/assets/termux-colors/gruvbox-material-light-soft.properties @@ -0,0 +1,26 @@ +background: #F2E5BC +foreground: #654735 + +color0: #504945 +color8: #504945 + +color1: #C14A4A +color9: #C14A4A + +color2: #6C782E +color10: #6C782E + +color3: #B47109 +color11: #B47109 + +color4: #45707A +color12: #45707A + +color5: #945E80 +color13: #945E80 + +color6: #4C7A5D +color14: #4C7A5D + +color7: #D4BE98 +color15: #D4BE98 diff --git a/app/src/main/assets/termux-colors/iceberg.properties b/app/src/main/assets/termux-colors/iceberg.properties new file mode 100644 index 0000000..160ae9a --- /dev/null +++ b/app/src/main/assets/termux-colors/iceberg.properties @@ -0,0 +1,40 @@ +# Iceberg colorscheme (https://github.com/cocopon/iceberg.vim) +# Based on the Xresources file for the colorscheme at: +# https://gist.github.com/cocopon/1d481941907d12db7a0df2f8806cfd41 + +# special +foreground=#c6c8d1 +background=#161821 +cursor=#c6c8d1 + +# black +color0=#161821 +color8=#6b7089 + +# red +color1=#e27878 +color9=#e98989 + +# green +color2=#b4be82 +color10=#c0ca8e + +# yellow +color3=#e2a478 +color11=#e9b189 + +# blue +color4=#84a0c6 +color12=#91acd1 + +# magenta +color5=#a093c7 +color13=#ada0d3 + +# cyan +color6=#89b8c2 +color14=#95c4ce + +# white +color7=#c6c8d1 +color15=#d2d4de diff --git a/app/src/main/assets/termux-colors/material.properties b/app/src/main/assets/termux-colors/material.properties new file mode 100644 index 0000000..c34a442 --- /dev/null +++ b/app/src/main/assets/termux-colors/material.properties @@ -0,0 +1,19 @@ +background : #263238 +foreground : #eceff1 + +color0 : #263238 +color8 : #37474f +color1 : #ff9800 +color9 : #ffa74d +color2 : #8bc34a +color10 : #9ccc65 +color3 : #ffc107 +color11 : #ffa000 +color4 : #03a9f4 +color12 : #81d4fa +color5 : #e91e63 +color13 : #ad1457 +color6 : #009688 +color14 : #26a69a +color7 : #cfd8dc +color15 : #eceff1 diff --git a/app/src/main/assets/termux-colors/nancy.properties b/app/src/main/assets/termux-colors/nancy.properties new file mode 100644 index 0000000..3f53ede --- /dev/null +++ b/app/src/main/assets/termux-colors/nancy.properties @@ -0,0 +1,263 @@ +# http://www.xcolors.net/dl/nancy + +foreground: #fff +background: #010101 +cursor: #e5e5e5 + +color0: #1b1d1e +color1: #f92672 +color2: #82b414 +color3: #fd971f +color4: #4e82aa +color5: #8c54fe +color6: #465457 +color7: #ccccc6 +color8: #505354 +color9: #ff5995 +color10: #b6e354 +color11: #feed6c +color12: #0c73c2 +color13: #9e6ffe +color14: #899ca1 +color15: #f8f8f2 + +color16: #000000 +color17: #00005f +color18: #000087 +color19: #0000af +color20: #0000d7 +color21: #0000ff +color22: #005f00 +color23: #005f5f +color24: #005f87 +color25: #005faf +color26: #005fd7 +color27: #005fff +color28: #008700 +color29: #00875f +color30: #008787 +color31: #0087af +color32: #0087d7 +color33: #0087ff +color34: #00af00 +color35: #00af5f +color36: #00af87 +color37: #00afaf +color38: #00afd7 +color39: #00afff +color40: #00d700 +color41: #00d75f +color42: #00d787 +color43: #00d7af +color44: #00d7d7 +color45: #00d7ff +color46: #00ff00 +color47: #00ff5f +color48: #00ff87 +color49: #00ffaf +color50: #00ffd7 +color51: #00ffff +color52: #131324 +color53: #5f005f +color54: #5f0087 +color55: #5f00af +color56: #5f00d7 +color57: #5f00ff +color58: #5f5f00 +color59: #5f5f5f +color60: #5f5f87 +color61: #5f5faf +color62: #5f5fd7 +color63: #5f5fff +color64: #5f8700 +color65: #5f875f +color66: #5f8787 +color67: #5f87af +color68: #5f87d7 +color69: #5f87ff +color70: #5faf00 +color71: #5faf5f +color72: #5faf87 +color73: #5fafaf +color74: #5fafd7 +color75: #5fafff +color76: #5fd700 +color77: #5fd75f +color78: #5fd787 +color79: #5fd7af +color80: #5fd7d7 +color81: #5fd7ff +color82: #5fff00 +color83: #5fff5f +color84: #5fff87 +color85: #a03040 +color86: #565941 +color87: #594459 +color88: #009bff +color89: #87005f +color90: #870087 +color91: #8700af +color92: #8700d7 +color93: #8700ff +color94: #875f00 +color95: #875f5f +color96: #875f87 +color97: #875faf +color98: #875fd7 +color99: #875fff +color100: #878700 +color101: #87875f +color102: #878787 +color103: #8787af +color104: #8787d7 +color105: #8787ff +color106: #87af00 +color107: #87af5f +color108: #87af87 +color109: #87afaf +color110: #87afd7 +color111: #87afff +color112: #87d700 +color113: #87d75f +color114: #87d787 +color115: #87d7af +color116: #87d7d7 +color117: #87d7ff +color118: #87ff00 +color119: #87ff5f +color120: #87ff87 +color121: #87ffaf +color122: #87ffd7 +color123: #87ffff +color124: #af0000 +color125: #af005f +color126: #af0087 +color127: #af00af +color128: #af00d7 +color129: #af00ff +color130: #af5f00 +color131: #af5f5f +color132: #af5f87 +color133: #af5faf +color134: #af5fd7 +color135: #af5fff +color136: #af8700 +color137: #af875f +color138: #af8787 +color139: #af87af +color140: #af87d7 +color141: #af87ff +color142: #afaf00 +color143: #afaf5f +color144: #afaf87 +color145: #afafaf +color146: #afafd7 +color147: #afafff +color148: #afd700 +color149: #afd75f +color150: #afd787 +color151: #afd7af +color152: #afd7d7 +color153: #afd7ff +color154: #afff00 +color155: #afff5f +color156: #afff87 +color157: #afffaf +color158: #afffd7 +color159: #afffff +color160: #d70000 +color161: #d7005f +color162: #d70087 +color163: #d700af +color164: #d700d7 +color165: #d700ff +color166: #d75f00 +color167: #d75f5f +color168: #d75f87 +color169: #d75faf +color170: #d75fd7 +color171: #d75fff +color172: #d78700 +color173: #d7875f +color174: #d78787 +color175: #d787af +color176: #d787d7 +color177: #d787ff +color178: #d7af00 +color179: #d7af5f +color180: #d7af87 +color181: #d7afaf +color182: #d7afd7 +color183: #d7afff +color184: #d7d700 +color185: #ffff00 +color186: #d7d787 +color187: #d7d7af +color188: #d7d7d7 +color189: #d7d7ff +color190: #d7ff00 +color191: #d7ff5f +color192: #d7ff87 +color193: #d7ffaf +color194: #d7ffd7 +color195: #d7ffff +color196: #ff0000 +color197: #ff005f +color198: #ff0087 +color199: #ff00af +color200: #ff00d7 +color201: #ff00ff +color202: #ff5f00 +color203: #ff5f5f +color204: #ff5f87 +color205: #ff5faf +color206: #ff5fd7 +color207: #ff5fff +color208: #ff8700 +color209: #ff875f +color210: #ff8787 +color211: #ff87af +color212: #ff87d7 +color213: #ff87ff +color214: #ffaf00 +color215: #ffaf5f +color216: #ffaf87 +color217: #ffafaf +color218: #ffafd7 +color219: #ffafff +color220: #ffd700 +color221: #ffd75f +color222: #ffd787 +color223: #ffd7af +color224: #ffd7d7 +color225: #ffd7ff +color226: #ffff00 +color227: #ffff5f +color228: #ffff87 +color229: #ffffaf +color230: #ffffd7 +color231: #060000 +color232: #080808 +color233: #121212 +color234: #1c1c1c +color235: #262626 +color236: #303030 +color237: #3a3a3a +color238: #444444 +color239: #4e4e4e +color240: #585858 +color241: #626262 +color242: #6c6c6c +color243: #767676 +color244: #808080 +color245: #8a8a8a +color246: #949494 +color247: #9e9e9e +color248: #a8a8a8 +color249: #b2b2b2 +color250: #bcbcbc +color251: #c6c6c6 +color252: #d0d0d0 +color253: #dadada +color254: #e4e4e4 +color255: #eeeeee diff --git a/app/src/main/assets/termux-colors/neon.properties b/app/src/main/assets/termux-colors/neon.properties new file mode 100644 index 0000000..3e1633f --- /dev/null +++ b/app/src/main/assets/termux-colors/neon.properties @@ -0,0 +1,27 @@ +# http=//xcolors.net/dl/neon +background=#171717 +foreground=#F8F8F8 +# black +color0=#171717 +color8=#38252C +# red +color1=#D81765 +color9=#FF0000 +# green +color2=#97D01A +color10=#76B639 +# yellow +color3=#FFA800 +color11=#E1A126 +# blue +color4=#16B1FB +color12=#289CD5 +# magenta +color5=#FF2491 +color13=#FF2491 +# cyan +color6=#0FDCB6 +color14=#0A9B81 +# white +color7=#EBEBEB +color15=#F8F8F8 diff --git a/app/src/main/assets/termux-colors/nord.properties b/app/src/main/assets/termux-colors/nord.properties new file mode 100644 index 0000000..7a71bf2 --- /dev/null +++ b/app/src/main/assets/termux-colors/nord.properties @@ -0,0 +1,22 @@ +# https://git.io/nord +foreground=#d8dee9 +background=#2e3440 +cursor=#d8dee9 + +color0=#3b4252 +color1=#bf616a +color2=#a3be8c +color3=#ebcb8b +color4=#81a1c1 +color5=#b48ead +color6=#88c0d0 +color7=#e5e8f0 + +color8=#4c566a +color9=#bf616a +color10=#a3be8c +color11=#ebcb8b +color12=#81a1c1 +color13=#b48ead +color14=#8fbcbb +color15=#eceff4 diff --git a/app/src/main/assets/termux-colors/rosé-pine-dawn.properties b/app/src/main/assets/termux-colors/rosé-pine-dawn.properties new file mode 100644 index 0000000..f657bd5 --- /dev/null +++ b/app/src/main/assets/termux-colors/rosé-pine-dawn.properties @@ -0,0 +1,31 @@ +# Scheme: Rosé Pine Dawn (http://rosepinetheme.com) +# Author: ThatOneCalculator (https://github.com/thatonecalculator) + +background=#faf4ed +foreground=#575279 +cursor=#797593 + +# black +color0=#f2e9e1 +color8=#797593 +# red +color1=#b4637a +color9=#b4637a +# green +color2=#286983 +color10=#286983 +# yellow +color3=#ea9d34 +color11=#ea9d34 +# blue +color4=#56949f +color12=#56949f +# magenta +color5=#907aa9 +color13=#907aa9 +# cyan +color6=#d7827e +color14=#d7827e +# white +color7=#575279 +color15=#575279 diff --git a/app/src/main/assets/termux-colors/rosé-pine-moon.properties b/app/src/main/assets/termux-colors/rosé-pine-moon.properties new file mode 100644 index 0000000..5f49036 --- /dev/null +++ b/app/src/main/assets/termux-colors/rosé-pine-moon.properties @@ -0,0 +1,31 @@ +# Scheme: Rosé Pine Moon (http://rosepinetheme.com) +# Author: ThatOneCalculator (https://github.com/thatonecalculator) + +background=#232136 +foreground=#e0def4 +cursor=#6e6a86 + +# black +color0=#393552 +color8=#6e6a86 +# red +color1=#eb6f92 +color9=#eb6f92 +# green +color2=#3e8fb0 +color10=#3e8fb0 +# yellow +color3=#f6c177 +color11=#f6c177 +# blue +color4=#9ccfd8 +color12=#9ccfd8 +# magenta +color5=#c4a7e7 +color13=#c4a7e7 +# cyan +color6=#ea9a97 +color14=#ea9a97 +# white +color7=#e0def4 +color15=#e0def4 diff --git a/app/src/main/assets/termux-colors/rosé-pine.properties b/app/src/main/assets/termux-colors/rosé-pine.properties new file mode 100644 index 0000000..15e74c5 --- /dev/null +++ b/app/src/main/assets/termux-colors/rosé-pine.properties @@ -0,0 +1,31 @@ +# Scheme: Rosé Pine (http://rosepinetheme.com) +# Author: ThatOneCalculator (https://github.com/thatonecalculator) + +background=#191724 +foreground=#e0def4 +cursor=#6e6a86 + +# black +color0=#26233a +color8=#6e6a86 +# red +color1=#eb6f92 +color9=#eb6f92 +# green +color2=#31748f +color10=#31748f +# yellow +color3=#f6c177 +color11=#f6c177 +# blue +color4=#9ccfd8 +color12=#9ccfd8 +# magenta +color5=#c4a7e7 +color13=#c4a7e7 +# cyan +color6=#ebbcba +color14=#ebbcba +# white +color7=#e0def4 +color15=#e0def4 diff --git a/app/src/main/assets/termux-colors/rydgel.properties b/app/src/main/assets/termux-colors/rydgel.properties new file mode 100644 index 0000000..9f7d40e --- /dev/null +++ b/app/src/main/assets/termux-colors/rydgel.properties @@ -0,0 +1,33 @@ +# Black +color0: #303430 +color8: #cdb5cd + +# Red +color1: #bf7979 +color9: #f4a45f + +# Green +color2: #97b26b +color10: #c5f779 + +# Yellow +color3: #cdcdc1 +color11: #ffffed + +# Blue +color4: #86a2be +color12: #98afd9 + +# Magenta +color5: #d9b798 +color13: #d7d998 + +# Cyan +color6: #a1b5cd +color14: #a1b5cd + +# White +color7: #ffffff +color15: #dedede + +# vim: et sw=2 syn=xdefaults diff --git a/app/src/main/assets/termux-colors/smyck.properties b/app/src/main/assets/termux-colors/smyck.properties new file mode 100644 index 0000000..4bd7031 --- /dev/null +++ b/app/src/main/assets/termux-colors/smyck.properties @@ -0,0 +1,21 @@ +#https://github.com/hukl/Smyck-Color-Scheme/blob/master/colors +background=#212121 +foreground=#f7f7f7 +cursor=#218693 + +color0=#000000 +color1=#c75646 +color2=#8eb33b +color3=#d0b03c +color4=#4e90a7 +color5=#c8a0d1 +color6=#218693 +color7=#b0b0b0 +color9=#e09690 +color8=#5d5d5d +color10=#cdee69 +color11=#ffe377 +color12=#9cd9f0 +color13=#fbb1f9 +color14=#77dfd8 +color15=#f7f7f7 diff --git a/app/src/main/assets/termux-colors/solarized-dark.properties b/app/src/main/assets/termux-colors/solarized-dark.properties new file mode 100644 index 0000000..2173fc6 --- /dev/null +++ b/app/src/main/assets/termux-colors/solarized-dark.properties @@ -0,0 +1,22 @@ +# https://github.com/altercation/solarized/blob/master/xresources/solarized +background=#002b36 +foreground=#839496 +cursor=#93a1a1 + +color0=#073642 +color1=#dc322f +color2=#859900 +color3=#b58900 +color4=#268bd2 +color5=#d33682 +color6=#2aa198 +color7=#eee8d5 +color9=#cb4b16 +color8=#002b36 +color10=#586e75 +color11=#657b83 +color12=#839496 +color13=#6c71c4 +color14=#93a1a1 +color15=#fdf6e3 + diff --git a/app/src/main/assets/termux-colors/solarized-light.properties b/app/src/main/assets/termux-colors/solarized-light.properties new file mode 100644 index 0000000..e966e68 --- /dev/null +++ b/app/src/main/assets/termux-colors/solarized-light.properties @@ -0,0 +1,21 @@ +# https://github.com/altercation/solarized/blob/master/xresources/solarized +background=#fdf6e3 +foreground=#657b83 +cursor=#586e75 + +color0=#073642 +color1=#dc322f +color2=#859900 +color3=#b58900 +color4=#268bd2 +color5=#d33682 +color6=#2aa198 +color7=#eee8d5 +color8=#002b36 +color9=#cb4b16 +color10=#586e75 +color11=#657b83 +color12=#839496 +color13=#6c71c4 +color14=#93a1a1 +color15=#fdf6e3 diff --git a/app/src/main/assets/termux-colors/spacemacs.properties b/app/src/main/assets/termux-colors/spacemacs.properties new file mode 100644 index 0000000..6b29351 --- /dev/null +++ b/app/src/main/assets/termux-colors/spacemacs.properties @@ -0,0 +1,25 @@ +foreground=#adb0a2 +background=#292b2e +cursor=#eead0e +color0=#1C2023 +color1=#C7AE95 +color2=#cc5279 +color3=#6690da +color4=#000000 +color5=#C795AE +color6=#4f97d7 +color7=#C7CCD1 +color8=#747C84 +color9=#C7AE95 +color10=#95C7AE +color11=#AEC795 +color12=#bb6dc4 +color13=#C795AE +color14=#95AEC7 +color15=#F3F4F5 +color16=#C7C795 +color17=#C79595 +color18=#393F45 +color19=#565E65 +color20=#ADB3BA +color21=#DFE2E5 diff --git a/app/src/main/assets/termux-colors/tokyonight-dark.properties b/app/src/main/assets/termux-colors/tokyonight-dark.properties new file mode 100644 index 0000000..ceaf782 --- /dev/null +++ b/app/src/main/assets/termux-colors/tokyonight-dark.properties @@ -0,0 +1,33 @@ +## Name: Tokyo Night Dark + +# Special +foreground = #c0caf5 +cursor = #c0caf5 +background = #1a1b26 +# Black +color0 = #15161E +color8 = #414868 +# Red +color1 = #f7768e +color9 = #f7768e +color17 = #db4b4b +# Green +color2 = #9ece6a +color10 = #9ece6a +# Yellow +color3 = #e0af68 +color11 = #e0af68 +# Blue +color4 = #7aa2f7 +color12 = #7aa2f7 +# Purple +color5 = #bb9af7 +color13 = #bb9af7 +# Cyan +color14 = #7dcfff +color6 = #7dcfff +# White +color7 = #a9b1d6 +color15 = #c0caf5 +# Orange +color16 = #ff9e64 diff --git a/app/src/main/assets/termux-colors/tokyonight-day.properties b/app/src/main/assets/termux-colors/tokyonight-day.properties new file mode 100644 index 0000000..2b8dbaa --- /dev/null +++ b/app/src/main/assets/termux-colors/tokyonight-day.properties @@ -0,0 +1,32 @@ +## name: Tokyo Night Day + +# Special + background = #e1e2e7 + foreground = #3760bf + cursor = #3760bf +# White +color0 = #e9e9ed +color8 = #a1a6c5 +# Red +color1 = #f52a65 +color9 = #f52a65 +color17 = #c64343 +# Green +color2 = #587539 +color10 = #587539 +# Yellow +color3 = #8c6c3e +color11 = #8c6c3e +# Blue +color4 = #2e7de9 +color12 = #2e7de9 +color7 = #6172b0 +color15 = #3760bf +# Purple +color5 = #9854f1 +color13 = #9854f1 +# Cyan +color6 = #007197 +color14 = #007197 +# Orange +color16 = #b15c00 diff --git a/app/src/main/assets/termux-colors/tomorrow-night.properties b/app/src/main/assets/termux-colors/tomorrow-night.properties new file mode 100644 index 0000000..da7c447 --- /dev/null +++ b/app/src/main/assets/termux-colors/tomorrow-night.properties @@ -0,0 +1,21 @@ +# http://chriskempson.github.io/base16/#tomorrow +background=#1d1f21 +foreground=#c5c8c6 +cursor=#c5c8c6 + +color0=#1d1f21 +color1=#cc6666 +color2=#b5bd68 +color3=#f0c674 +color4=#81a2be +color5=#b294bb +color6=#8abeb7 +color7=#c5c8c6 +color9=#969896 +color8=#cc6666 +color10=#b5bd68 +color11=#f0c674 +color12=#81a2be +color13=#b294bb +color14=#8abeb7 +color15=#ffffff diff --git a/app/src/main/assets/termux-colors/ubuntu.properties b/app/src/main/assets/termux-colors/ubuntu.properties new file mode 100644 index 0000000..ba3cceb --- /dev/null +++ b/app/src/main/assets/termux-colors/ubuntu.properties @@ -0,0 +1,22 @@ +# https://github.com/Mayccoll/Gogh/blob/master/themes/clone-of-ubuntu.sh +background=#300a24 +foreground=#ffffff +cursor=#ffffff + +color0=#2E3436 +color1=#CC0000 +color2=#4E9A06 +color3=#C4A000 +color4=#3465A4 +color5=#75507B +color6=#06989A +color7=#D3D7CF + +color8=#555753 +color9=#EF2929 +color10=#8AE234 +color11=#FCE94F +color12=#729FCF +color13=#AD7FA8 +color14=#34E2E2 +color15=#EEEEEC diff --git a/app/src/main/assets/termux-colors/white-on-black.properties b/app/src/main/assets/termux-colors/white-on-black.properties new file mode 100644 index 0000000..a4a9aca --- /dev/null +++ b/app/src/main/assets/termux-colors/white-on-black.properties @@ -0,0 +1,258 @@ +background: #000000 +foreground: #FFFFFF + +color1:#FFFFFF +color2:#FFFFFF +color3:#FFFFFF +color4:#FFFFFF +color5:#FFFFFF +color6:#FFFFFF +color7:#FFFFFF +color8:#FFFFFF +color9:#FFFFFF +color10:#FFFFFF +color11:#FFFFFF +color12:#FFFFFF +color13:#FFFFFF +color14:#FFFFFF +color15:#FFFFFF +color16:#FFFFFF +color17:#FFFFFF +color18:#FFFFFF +color19:#FFFFFF +color20:#FFFFFF +color21:#FFFFFF +color22:#FFFFFF +color23:#FFFFFF +color24:#FFFFFF +color25:#FFFFFF +color26:#FFFFFF +color27:#FFFFFF +color28:#FFFFFF +color29:#FFFFFF +color30:#FFFFFF +color31:#FFFFFF +color32:#FFFFFF +color33:#FFFFFF +color34:#FFFFFF +color35:#FFFFFF +color36:#FFFFFF +color37:#FFFFFF +color38:#FFFFFF +color39:#FFFFFF +color40:#FFFFFF +color41:#FFFFFF +color42:#FFFFFF +color43:#FFFFFF +color44:#FFFFFF +color45:#FFFFFF +color46:#FFFFFF +color47:#FFFFFF +color48:#FFFFFF +color49:#FFFFFF +color50:#FFFFFF +color51:#FFFFFF +color52:#FFFFFF +color53:#FFFFFF +color54:#FFFFFF +color55:#FFFFFF +color56:#FFFFFF +color57:#FFFFFF +color58:#FFFFFF +color59:#FFFFFF +color60:#FFFFFF +color61:#FFFFFF +color62:#FFFFFF +color63:#FFFFFF +color64:#FFFFFF +color65:#FFFFFF +color66:#FFFFFF +color67:#FFFFFF +color68:#FFFFFF +color69:#FFFFFF +color70:#FFFFFF +color71:#FFFFFF +color72:#FFFFFF +color73:#FFFFFF +color74:#FFFFFF +color75:#FFFFFF +color76:#FFFFFF +color77:#FFFFFF +color78:#FFFFFF +color79:#FFFFFF +color80:#FFFFFF +color81:#FFFFFF +color82:#FFFFFF +color83:#FFFFFF +color84:#FFFFFF +color85:#FFFFFF +color86:#FFFFFF +color87:#FFFFFF +color88:#FFFFFF +color89:#FFFFFF +color90:#FFFFFF +color91:#FFFFFF +color92:#FFFFFF +color93:#FFFFFF +color94:#FFFFFF +color95:#FFFFFF +color96:#FFFFFF +color97:#FFFFFF +color98:#FFFFFF +color99:#FFFFFF +color100:#FFFFFF +color101:#FFFFFF +color102:#FFFFFF +color103:#FFFFFF +color104:#FFFFFF +color105:#FFFFFF +color106:#FFFFFF +color107:#FFFFFF +color108:#FFFFFF +color109:#FFFFFF +color110:#FFFFFF +color111:#FFFFFF +color112:#FFFFFF +color113:#FFFFFF +color114:#FFFFFF +color115:#FFFFFF +color116:#FFFFFF +color117:#FFFFFF +color118:#FFFFFF +color119:#FFFFFF +color120:#FFFFFF +color121:#FFFFFF +color122:#FFFFFF +color123:#FFFFFF +color124:#FFFFFF +color125:#FFFFFF +color126:#FFFFFF +color127:#FFFFFF +color128:#FFFFFF +color129:#FFFFFF +color130:#FFFFFF +color131:#FFFFFF +color132:#FFFFFF +color133:#FFFFFF +color134:#FFFFFF +color135:#FFFFFF +color136:#FFFFFF +color137:#FFFFFF +color138:#FFFFFF +color139:#FFFFFF +color140:#FFFFFF +color141:#FFFFFF +color142:#FFFFFF +color143:#FFFFFF +color144:#FFFFFF +color145:#FFFFFF +color146:#FFFFFF +color147:#FFFFFF +color148:#FFFFFF +color149:#FFFFFF +color150:#FFFFFF +color151:#FFFFFF +color152:#FFFFFF +color153:#FFFFFF +color154:#FFFFFF +color155:#FFFFFF +color156:#FFFFFF +color157:#FFFFFF +color158:#FFFFFF +color159:#FFFFFF +color160:#FFFFFF +color161:#FFFFFF +color162:#FFFFFF +color163:#FFFFFF +color164:#FFFFFF +color165:#FFFFFF +color166:#FFFFFF +color167:#FFFFFF +color168:#FFFFFF +color169:#FFFFFF +color170:#FFFFFF +color171:#FFFFFF +color172:#FFFFFF +color173:#FFFFFF +color174:#FFFFFF +color175:#FFFFFF +color176:#FFFFFF +color177:#FFFFFF +color178:#FFFFFF +color179:#FFFFFF +color180:#FFFFFF +color181:#FFFFFF +color182:#FFFFFF +color183:#FFFFFF +color184:#FFFFFF +color185:#FFFFFF +color186:#FFFFFF +color187:#FFFFFF +color188:#FFFFFF +color189:#FFFFFF +color190:#FFFFFF +color191:#FFFFFF +color192:#FFFFFF +color193:#FFFFFF +color194:#FFFFFF +color195:#FFFFFF +color196:#FFFFFF +color197:#FFFFFF +color198:#FFFFFF +color199:#FFFFFF +color200:#FFFFFF +color201:#FFFFFF +color202:#FFFFFF +color203:#FFFFFF +color204:#FFFFFF +color205:#FFFFFF +color206:#FFFFFF +color207:#FFFFFF +color208:#FFFFFF +color209:#FFFFFF +color210:#FFFFFF +color211:#FFFFFF +color212:#FFFFFF +color213:#FFFFFF +color214:#FFFFFF +color215:#FFFFFF +color216:#FFFFFF +color217:#FFFFFF +color218:#FFFFFF +color219:#FFFFFF +color220:#FFFFFF +color221:#FFFFFF +color222:#FFFFFF +color223:#FFFFFF +color224:#FFFFFF +color225:#FFFFFF +color226:#FFFFFF +color227:#FFFFFF +color228:#FFFFFF +color229:#FFFFFF +color230:#FFFFFF +color231:#FFFFFF +color232:#FFFFFF +color233:#FFFFFF +color234:#FFFFFF +color235:#FFFFFF +color236:#FFFFFF +color237:#FFFFFF +color238:#FFFFFF +color239:#FFFFFF +color240:#FFFFFF +color241:#FFFFFF +color242:#FFFFFF +color243:#FFFFFF +color244:#FFFFFF +color245:#FFFFFF +color246:#FFFFFF +color247:#FFFFFF +color248:#FFFFFF +color249:#FFFFFF +color250:#FFFFFF +color251:#FFFFFF +color252:#FFFFFF +color253:#FFFFFF +color254:#FFFFFF +color255:#FFFFFF diff --git a/app/src/main/assets/termux-colors/wild-cherry.properties b/app/src/main/assets/termux-colors/wild-cherry.properties new file mode 100644 index 0000000..b9b5a10 --- /dev/null +++ b/app/src/main/assets/termux-colors/wild-cherry.properties @@ -0,0 +1,21 @@ +# https://github.com/mashaal/wild-cherry +color0: #099BD7 +color10: #2AB250 +color11: #FFD16F +color12: #883CDB +color13: #099BD7 +color14: #4F5D95 +color15: #FFF8DE +color1: #D94085 +color2: #2AB250 +color3: #FFD16F +color4: #883CDC +color5: #0F9DBA +color6: #4F5D95 +color7: #FFF8DD +color8: #099BD7 +color9: #D94084 +background: #1F1626 +cursor: #0F9DBA +foreground: #FFFFFF + diff --git a/app/src/main/assets/termux-colors/zenburn.properties b/app/src/main/assets/termux-colors/zenburn.properties new file mode 100644 index 0000000..0761a1a --- /dev/null +++ b/app/src/main/assets/termux-colors/zenburn.properties @@ -0,0 +1,21 @@ +# http://dotfiles.org/~jbromley/.Xresources +background=#000010 +foreground=#ffffff +cursor=#FF00FF + +color0=#000000 +color1=#9e1828 +color2=#aece92 +color3=#968a38 +color4=#414171 +color5=#963c59 +color6=#418179 +color7=#bebebe +color8=#666666 +color9=#cf6171 +color10=#c5f779 +color11=#fff796 +color12=#4186be +color13=#cf9ebe +color14=#71bebe +color15=#ffffff diff --git a/app/src/main/java/io/nekohasekai/sfa/bg/BoxService.kt b/app/src/main/java/io/nekohasekai/sfa/bg/BoxService.kt index da211c8..49fc8ce 100644 --- a/app/src/main/java/io/nekohasekai/sfa/bg/BoxService.kt +++ b/app/src/main/java/io/nekohasekai/sfa/bg/BoxService.kt @@ -427,4 +427,6 @@ class BoxService(private val service: Service, private val platformInterface: Pl override fun writeDebugMessage(message: String?) { Log.d("sing-box", message!!) } + + override fun connectSSHAgent(): Int = -1 } diff --git a/app/src/main/java/io/nekohasekai/sfa/compose/MainActivity.kt b/app/src/main/java/io/nekohasekai/sfa/compose/MainActivity.kt index 80f12bf..f64dcf2 100644 --- a/app/src/main/java/io/nekohasekai/sfa/compose/MainActivity.kt +++ b/app/src/main/java/io/nekohasekai/sfa/compose/MainActivity.kt @@ -114,6 +114,7 @@ import io.nekohasekai.sfa.compose.screen.dashboard.DashboardViewModel import io.nekohasekai.sfa.compose.screen.dashboard.GroupsCard import io.nekohasekai.sfa.compose.screen.dashboard.groups.GroupsViewModel import io.nekohasekai.sfa.compose.screen.log.LogViewModel +import io.nekohasekai.sfa.compose.screen.tools.TailscaleSSHSharedViewModel import io.nekohasekai.sfa.compose.screen.tools.TailscaleStatusViewModel import io.nekohasekai.sfa.compose.theme.SFATheme import io.nekohasekai.sfa.compose.topbar.LocalTopBarController @@ -748,6 +749,8 @@ class MainActivity : null } + val tailscaleSSHSharedViewModel: TailscaleSSHSharedViewModel = viewModel() + val isToolsRoute = currentRootRoute == Screen.Tools.route val tailscaleStatusViewModel: TailscaleStatusViewModel? = if (isToolsRoute) { @@ -871,6 +874,7 @@ class MainActivity : groupsViewModel = groupsViewModel, connectionsViewModel = connectionsViewModel, tailscaleStatusViewModel = tailscaleStatusViewModel, + tailscaleSSHSharedViewModel = tailscaleSSHSharedViewModel, modifier = Modifier.fillMaxSize(), ) if (!useNavigationRail) { diff --git a/app/src/main/java/io/nekohasekai/sfa/compose/navigation/SFANavigation.kt b/app/src/main/java/io/nekohasekai/sfa/compose/navigation/SFANavigation.kt index c40f03a..0a9d0e0 100644 --- a/app/src/main/java/io/nekohasekai/sfa/compose/navigation/SFANavigation.kt +++ b/app/src/main/java/io/nekohasekai/sfa/compose/navigation/SFANavigation.kt @@ -34,6 +34,9 @@ import io.nekohasekai.sfa.compose.screen.settings.PrivilegeSettingsScreen import io.nekohasekai.sfa.compose.screen.settings.ProfileOverrideScreen import io.nekohasekai.sfa.compose.screen.settings.ServiceSettingsScreen import io.nekohasekai.sfa.compose.screen.settings.SettingsScreen +import io.nekohasekai.sfa.compose.screen.settings.TailscaleFontPickerScreen +import io.nekohasekai.sfa.compose.screen.settings.TailscaleTerminalConfigScreen +import io.nekohasekai.sfa.compose.screen.settings.TailscaleThemePickerScreen import io.nekohasekai.sfa.compose.screen.tools.CrashReportDetailScreen import io.nekohasekai.sfa.compose.screen.tools.CrashReportFileContentScreen import io.nekohasekai.sfa.compose.screen.tools.CrashReportListScreen @@ -48,6 +51,9 @@ import io.nekohasekai.sfa.compose.screen.tools.STUNTestScreen import io.nekohasekai.sfa.compose.screen.tools.TailscaleEndpointScreen import io.nekohasekai.sfa.compose.screen.tools.TailscaleExitNodePickerScreen import io.nekohasekai.sfa.compose.screen.tools.TailscalePeerScreen +import io.nekohasekai.sfa.compose.screen.tools.TailscaleSSHPromptScreen +import io.nekohasekai.sfa.compose.screen.tools.TailscaleSSHSharedViewModel +import io.nekohasekai.sfa.compose.screen.tools.TailscaleSSHTerminalScreen import io.nekohasekai.sfa.compose.screen.tools.TailscaleStatusViewModel import io.nekohasekai.sfa.compose.screen.tools.ToolsScreen import io.nekohasekai.sfa.constant.Status @@ -82,6 +88,7 @@ fun SFANavHost( groupsViewModel: GroupsViewModel? = null, connectionsViewModel: ConnectionsViewModel? = null, tailscaleStatusViewModel: TailscaleStatusViewModel? = null, + tailscaleSSHSharedViewModel: TailscaleSSHSharedViewModel? = null, modifier: Modifier = Modifier, ) { NavHost( @@ -230,7 +237,8 @@ fun SFANavHost( composable(Screen.Tools.route) { val tailscaleViewModel: TailscaleStatusViewModel = tailscaleStatusViewModel ?: viewModel() - ToolsScreen(navController = navController, serviceStatus = serviceStatus, tailscaleViewModel = tailscaleViewModel) + val sshSharedViewModel: TailscaleSSHSharedViewModel = tailscaleSSHSharedViewModel ?: viewModel() + ToolsScreen(navController = navController, serviceStatus = serviceStatus, tailscaleViewModel = tailscaleViewModel, sshSharedViewModel = sshSharedViewModel) } // Tools subscreens with slide animations @@ -276,7 +284,8 @@ fun SFANavHost( ) { backStackEntry -> val endpointTag = Uri.decode(backStackEntry.arguments?.getString("endpointTag") ?: return@composable) val tailscaleViewModel: TailscaleStatusViewModel = tailscaleStatusViewModel ?: viewModel() - TailscaleEndpointScreen(navController = navController, viewModel = tailscaleViewModel, endpointTag = endpointTag) + val sshSharedViewModel: TailscaleSSHSharedViewModel = tailscaleSSHSharedViewModel ?: viewModel() + TailscaleEndpointScreen(navController = navController, viewModel = tailscaleViewModel, sshSharedViewModel = sshSharedViewModel, endpointTag = endpointTag) } composable( @@ -309,6 +318,50 @@ fun SFANavHost( TailscalePeerScreen(navController = navController, viewModel = tailscaleViewModel, endpointTag = endpointTag, peerId = peerId) } + composable( + route = "tools/tailscale/{endpointTag}/peer/{peerId}/ssh", + arguments = listOf( + navArgument("endpointTag") { type = NavType.StringType }, + navArgument("peerId") { type = NavType.StringType }, + ), + enterTransition = slideInFromRight, + exitTransition = slideOutToLeft, + popEnterTransition = slideInFromLeft, + popExitTransition = slideOutToRight, + ) { backStackEntry -> + val endpointTag = Uri.decode(backStackEntry.arguments?.getString("endpointTag") ?: return@composable) + val peerId = Uri.decode(backStackEntry.arguments?.getString("peerId") ?: return@composable) + val tailscaleViewModel: TailscaleStatusViewModel = tailscaleStatusViewModel ?: viewModel() + val sshSharedViewModel: TailscaleSSHSharedViewModel = tailscaleSSHSharedViewModel ?: viewModel() + TailscaleSSHPromptScreen( + navController = navController, + sharedViewModel = sshSharedViewModel, + viewModel = tailscaleViewModel, + endpointTag = endpointTag, + peerId = peerId, + ) + } + + composable( + route = "tools/tailscale/{endpointTag}/peer/{peerId}/terminal", + arguments = listOf( + navArgument("endpointTag") { type = NavType.StringType }, + navArgument("peerId") { type = NavType.StringType }, + ), + enterTransition = slideInFromRight, + exitTransition = slideOutToLeft, + popEnterTransition = slideInFromLeft, + popExitTransition = slideOutToRight, + ) { backStackEntry -> + val sshSharedViewModel: TailscaleSSHSharedViewModel = tailscaleSSHSharedViewModel ?: viewModel() + val tailscaleViewModel: TailscaleStatusViewModel = tailscaleStatusViewModel ?: viewModel() + TailscaleSSHTerminalScreen( + navController = navController, + sharedViewModel = sshSharedViewModel, + tailscaleViewModel = tailscaleViewModel, + ) + } + composable( route = "tools/crash_report", enterTransition = slideInFromRight, @@ -494,6 +547,38 @@ fun SFANavHost( PrivilegeSettingsManageScreen(onBack = { navController.navigateUp() }, serviceStatus = serviceStatus) } + composable( + route = "settings/tailscale/terminal_config", + enterTransition = slideInFromRight, + exitTransition = slideOutToLeft, + popEnterTransition = slideInFromLeft, + popExitTransition = slideOutToRight, + ) { + TailscaleTerminalConfigScreen(navController = navController) + } + + composable( + route = "settings/tailscale/theme_picker/{isDark}", + arguments = listOf(navArgument("isDark") { type = NavType.StringType }), + enterTransition = slideInFromRight, + exitTransition = slideOutToLeft, + popEnterTransition = slideInFromLeft, + popExitTransition = slideOutToRight, + ) { backStackEntry -> + val isDarkStr = backStackEntry.arguments?.getString("isDark") ?: "false" + TailscaleThemePickerScreen(navController = navController, isDark = isDarkStr == "true") + } + + composable( + route = "settings/tailscale/font_picker", + enterTransition = slideInFromRight, + exitTransition = slideOutToLeft, + popEnterTransition = slideInFromLeft, + popExitTransition = slideOutToRight, + ) { + TailscaleFontPickerScreen(navController = navController) + } + composable( route = "settings/privilege/logs", enterTransition = slideInFromRight, diff --git a/app/src/main/java/io/nekohasekai/sfa/compose/screen/settings/AppSettingsScreen.kt b/app/src/main/java/io/nekohasekai/sfa/compose/screen/settings/AppSettingsScreen.kt index 52b8d4c..d98651e 100644 --- a/app/src/main/java/io/nekohasekai/sfa/compose/screen/settings/AppSettingsScreen.kt +++ b/app/src/main/java/io/nekohasekai/sfa/compose/screen/settings/AppSettingsScreen.kt @@ -31,6 +31,7 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.ContentCopy +import androidx.compose.material.icons.filled.Terminal import androidx.compose.material.icons.outlined.AdminPanelSettings import androidx.compose.material.icons.outlined.Autorenew import androidx.compose.material.icons.outlined.DeleteForever @@ -619,6 +620,50 @@ fun AppSettingsScreen( Spacer(modifier = Modifier.height(8.dp)) + Text( + text = stringResource(R.string.tailscale), + style = MaterialTheme.typography.labelLarge, + color = MaterialTheme.colorScheme.primary, + modifier = Modifier.padding(horizontal = 32.dp, vertical = 8.dp), + ) + + Card( + modifier = + Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + colors = + CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surfaceContainer, + ), + ) { + ListItem( + headlineContent = { + Text( + stringResource(R.string.tailscale_terminal_config), + style = MaterialTheme.typography.bodyLarge, + ) + }, + leadingContent = { + Icon( + imageVector = Icons.Default.Terminal, + contentDescription = null, + tint = MaterialTheme.colorScheme.primary, + ) + }, + modifier = + Modifier + .clip(RoundedCornerShape(12.dp)) + .clickable { navController.navigate("settings/tailscale/terminal_config") }, + colors = + ListItemDefaults.colors( + containerColor = Color.Transparent, + ), + ) + } + + Spacer(modifier = Modifier.height(8.dp)) + Text( text = stringResource(R.string.notification_settings), style = MaterialTheme.typography.labelLarge, diff --git a/app/src/main/java/io/nekohasekai/sfa/compose/screen/settings/TailscaleFontPickerScreen.kt b/app/src/main/java/io/nekohasekai/sfa/compose/screen/settings/TailscaleFontPickerScreen.kt new file mode 100644 index 0000000..f5cfb0e --- /dev/null +++ b/app/src/main/java/io/nekohasekai/sfa/compose/screen/settings/TailscaleFontPickerScreen.kt @@ -0,0 +1,212 @@ +package io.nekohasekai.sfa.compose.screen.settings + +import android.graphics.Typeface +import android.os.Build +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.outlined.Delete +import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.ListItem +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.RadioButton +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController +import io.nekohasekai.sfa.R +import io.nekohasekai.sfa.compose.topbar.OverrideTopBar +import io.nekohasekai.sfa.database.Settings +import io.nekohasekai.sfa.terminal.ImportedFont +import io.nekohasekai.sfa.terminal.ImportedFontStore + +private val knownMonospaceFamilies = listOf( + "monospace", + "Droid Sans Mono", + "Courier New", + "Courier", +) + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun TailscaleFontPickerScreen(navController: NavController) { + val context = LocalContext.current + var selectedFamily by remember { mutableStateOf(Settings.tailscaleSSHFontFamily) } + var selectedCustomPath by remember { mutableStateOf(Settings.tailscaleSSHCustomFontPath) } + var importedFonts by remember { mutableStateOf(ImportedFontStore.listImportedFonts(context)) } + + val systemFonts = remember { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + enumerateSystemMonospaceFonts() + } else { + knownMonospaceFamilies + } + } + + val importLauncher = rememberLauncherForActivityResult( + contract = ActivityResultContracts.GetContent(), + ) { uri -> + if (uri != null) { + val imported = ImportedFontStore.importFont(context, uri) + if (imported != null) { + importedFonts = ImportedFontStore.listImportedFonts(context) + selectedFamily = "" + selectedCustomPath = imported.path + Settings.tailscaleSSHFontFamily = "" + Settings.tailscaleSSHCustomFontPath = imported.path + } + } + } + + OverrideTopBar { + TopAppBar( + title = { Text(stringResource(R.string.tailscale_terminal_font)) }, + navigationIcon = { + IconButton(onClick = { navController.navigateUp() }) { + Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.content_description_back)) + } + }, + ) + } + + Column( + modifier = Modifier + .fillMaxSize() + .background(MaterialTheme.colorScheme.surface), + ) { + LazyColumn(modifier = Modifier.weight(1f)) { + item { + Text( + text = stringResource(R.string.tailscale_terminal_font_system), + style = MaterialTheme.typography.titleSmall, + color = MaterialTheme.colorScheme.primary, + modifier = Modifier.padding(horizontal = 32.dp, vertical = 8.dp), + ) + } + item { + val isDefault = selectedFamily.isBlank() && selectedCustomPath.isBlank() + ListItem( + headlineContent = { Text(stringResource(R.string.tailscale_terminal_font_default)) }, + leadingContent = { + RadioButton(selected = isDefault, onClick = null) + }, + modifier = Modifier.clickable { + selectedFamily = "" + selectedCustomPath = "" + Settings.tailscaleSSHFontFamily = "" + Settings.tailscaleSSHCustomFontPath = "" + }, + ) + } + items(systemFonts) { family -> + val isSelected = selectedFamily == family && selectedCustomPath.isBlank() + ListItem( + headlineContent = { Text(family) }, + leadingContent = { + RadioButton(selected = isSelected, onClick = null) + }, + modifier = Modifier.clickable { + selectedFamily = family + selectedCustomPath = "" + Settings.tailscaleSSHFontFamily = family + Settings.tailscaleSSHCustomFontPath = "" + }, + ) + } + + if (importedFonts.isNotEmpty()) { + item { + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = stringResource(R.string.tailscale_terminal_font_imported), + style = MaterialTheme.typography.titleSmall, + color = MaterialTheme.colorScheme.primary, + modifier = Modifier.padding(horizontal = 32.dp, vertical = 8.dp), + ) + } + items(importedFonts) { font -> + val isSelected = selectedCustomPath == font.path + ListItem( + headlineContent = { Text(font.name) }, + leadingContent = { + RadioButton(selected = isSelected, onClick = null) + }, + trailingContent = { + IconButton(onClick = { + ImportedFontStore.deleteFont(context, font.name) + importedFonts = ImportedFontStore.listImportedFonts(context) + if (isSelected) { + selectedFamily = "" + selectedCustomPath = "" + Settings.tailscaleSSHFontFamily = "" + Settings.tailscaleSSHCustomFontPath = "" + } + }) { + Icon( + Icons.Outlined.Delete, + contentDescription = null, + tint = MaterialTheme.colorScheme.error, + ) + } + }, + modifier = Modifier.clickable { + selectedFamily = "" + selectedCustomPath = font.path + Settings.tailscaleSSHFontFamily = "" + Settings.tailscaleSSHCustomFontPath = font.path + }, + ) + } + } + } + + Button( + onClick = { importLauncher.launch("font/*") }, + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + ) { + Text(stringResource(R.string.tailscale_terminal_import_font)) + } + } +} + +private fun enumerateSystemMonospaceFonts(): List { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) return knownMonospaceFamilies + + val families = mutableSetOf() + try { + val monoReference = Typeface.MONOSPACE + for (family in knownMonospaceFamilies) { + val typeface = Typeface.create(family, Typeface.NORMAL) + if (typeface != Typeface.DEFAULT) { + families.add(family) + } + } + } catch (_: Exception) { + } + return families.sorted() +} diff --git a/app/src/main/java/io/nekohasekai/sfa/compose/screen/settings/TailscaleTerminalConfigScreen.kt b/app/src/main/java/io/nekohasekai/sfa/compose/screen/settings/TailscaleTerminalConfigScreen.kt new file mode 100644 index 0000000..a5f6224 --- /dev/null +++ b/app/src/main/java/io/nekohasekai/sfa/compose/screen/settings/TailscaleTerminalConfigScreen.kt @@ -0,0 +1,224 @@ +package io.nekohasekai.sfa.compose.screen.settings + +import android.net.Uri +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Remove +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.ListItem +import androidx.compose.material3.ListItemDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController +import io.nekohasekai.sfa.R +import io.nekohasekai.sfa.compose.topbar.OverrideTopBar +import io.nekohasekai.sfa.database.Settings + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun TailscaleTerminalConfigScreen(navController: NavController) { + OverrideTopBar { + TopAppBar( + title = { Text(stringResource(R.string.tailscale_terminal_config)) }, + navigationIcon = { + IconButton(onClick = { navController.navigateUp() }) { + Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.content_description_back)) + } + }, + ) + } + + var lightTheme by remember { mutableStateOf(Settings.tailscaleSSHLightTheme) } + var darkTheme by remember { mutableStateOf(Settings.tailscaleSSHDarkTheme) } + var fontFamily by remember { mutableStateOf(Settings.tailscaleSSHFontFamily) } + var customFontPath by remember { mutableStateOf(Settings.tailscaleSSHCustomFontPath) } + var fontSize by remember { mutableIntStateOf(Settings.tailscaleSSHFontSize) } + + Column( + modifier = Modifier + .fillMaxSize() + .background(MaterialTheme.colorScheme.surface) + .verticalScroll(rememberScrollState()) + .padding(vertical = 8.dp), + ) { + Text( + text = stringResource(R.string.tailscale_terminal_color_theme), + style = MaterialTheme.typography.titleSmall, + color = MaterialTheme.colorScheme.primary, + modifier = Modifier.padding(horizontal = 32.dp, vertical = 8.dp), + ) + Card( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surfaceContainer, + ), + ) { + Column { + ListItem( + headlineContent = { Text(stringResource(R.string.tailscale_terminal_light_theme)) }, + trailingContent = { + Text( + lightTheme.ifBlank { "Default" }, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + }, + modifier = Modifier + .clip(RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp)) + .clickable { + navController.navigate("settings/tailscale/theme_picker/${Uri.encode("false")}") + }, + colors = ListItemDefaults.colors(containerColor = Color.Transparent), + ) + ListItem( + headlineContent = { Text(stringResource(R.string.tailscale_terminal_dark_theme)) }, + trailingContent = { + Text( + darkTheme.ifBlank { "Default" }, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + }, + modifier = Modifier + .clip(RoundedCornerShape(bottomStart = 12.dp, bottomEnd = 12.dp)) + .clickable { + navController.navigate("settings/tailscale/theme_picker/${Uri.encode("true")}") + }, + colors = ListItemDefaults.colors(containerColor = Color.Transparent), + ) + } + } + + Spacer(modifier = Modifier.height(16.dp)) + + Text( + text = stringResource(R.string.tailscale_terminal_font_config), + style = MaterialTheme.typography.titleSmall, + color = MaterialTheme.colorScheme.primary, + modifier = Modifier.padding(horizontal = 32.dp, vertical = 8.dp), + ) + Card( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surfaceContainer, + ), + ) { + Column { + ListItem( + headlineContent = { Text(stringResource(R.string.tailscale_terminal_font)) }, + trailingContent = { + val fontDisplayName = when { + customFontPath.isNotBlank() -> java.io.File(customFontPath).nameWithoutExtension + fontFamily.isNotBlank() -> fontFamily + else -> stringResource(R.string.tailscale_terminal_font_default) + } + Text( + fontDisplayName, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + }, + modifier = Modifier + .clip(RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp)) + .clickable { + navController.navigate("settings/tailscale/font_picker") + }, + colors = ListItemDefaults.colors(containerColor = Color.Transparent), + ) + ListItem( + headlineContent = { Text(stringResource(R.string.tailscale_terminal_font_size)) }, + trailingContent = { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + Surface( + onClick = { + if (fontSize > 8) { + fontSize-- + Settings.tailscaleSSHFontSize = fontSize + } + }, + shape = RoundedCornerShape(6.dp), + color = MaterialTheme.colorScheme.surfaceContainerHighest, + modifier = Modifier.size(32.dp), + ) { + IconButton(onClick = { + if (fontSize > 8) { + fontSize-- + Settings.tailscaleSSHFontSize = fontSize + } + }) { + Icon(Icons.Default.Remove, contentDescription = null, modifier = Modifier.size(16.dp)) + } + } + Text( + "$fontSize", + style = MaterialTheme.typography.bodyMedium, + ) + Surface( + onClick = { + if (fontSize < 32) { + fontSize++ + Settings.tailscaleSSHFontSize = fontSize + } + }, + shape = RoundedCornerShape(6.dp), + color = MaterialTheme.colorScheme.surfaceContainerHighest, + modifier = Modifier.size(32.dp), + ) { + IconButton(onClick = { + if (fontSize < 32) { + fontSize++ + Settings.tailscaleSSHFontSize = fontSize + } + }) { + Icon(Icons.Default.Add, contentDescription = null, modifier = Modifier.size(16.dp)) + } + } + } + }, + modifier = Modifier + .clip(RoundedCornerShape(bottomStart = 12.dp, bottomEnd = 12.dp)), + colors = ListItemDefaults.colors(containerColor = Color.Transparent), + ) + } + } + + Spacer(modifier = Modifier.height(16.dp)) + } +} diff --git a/app/src/main/java/io/nekohasekai/sfa/compose/screen/settings/TailscaleThemePickerScreen.kt b/app/src/main/java/io/nekohasekai/sfa/compose/screen/settings/TailscaleThemePickerScreen.kt new file mode 100644 index 0000000..c7866ac --- /dev/null +++ b/app/src/main/java/io/nekohasekai/sfa/compose/screen/settings/TailscaleThemePickerScreen.kt @@ -0,0 +1,126 @@ +package io.nekohasekai.sfa.compose.screen.settings + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.ListItem +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.RadioButton +import androidx.compose.material3.SearchBar +import androidx.compose.material3.SearchBarDefaults +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController +import io.nekohasekai.sfa.R +import io.nekohasekai.sfa.compose.topbar.OverrideTopBar +import io.nekohasekai.sfa.database.Settings +import io.nekohasekai.sfa.terminal.TerminalColorSchemeLoader + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun TailscaleThemePickerScreen( + navController: NavController, + isDark: Boolean, +) { + val context = LocalContext.current + var searchQuery by remember { mutableStateOf("") } + var selectedTheme by remember { + mutableStateOf(if (isDark) Settings.tailscaleSSHDarkTheme else Settings.tailscaleSSHLightTheme) + } + var schemes by remember { mutableStateOf>(emptyList()) } + + LaunchedEffect(Unit) { + schemes = TerminalColorSchemeLoader.listSchemes(context, isDark) + } + + val filteredSchemes = if (searchQuery.isBlank()) { + schemes + } else { + schemes.filter { it.contains(searchQuery, ignoreCase = true) } + } + + OverrideTopBar { + TopAppBar( + title = { + Text( + if (isDark) { + stringResource(R.string.tailscale_terminal_dark_theme) + } else { + stringResource(R.string.tailscale_terminal_light_theme) + }, + ) + }, + navigationIcon = { + IconButton(onClick = { navController.navigateUp() }) { + Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.content_description_back)) + } + }, + ) + } + + Column( + modifier = Modifier + .fillMaxSize() + .background(MaterialTheme.colorScheme.surface), + ) { + SearchBar( + inputField = { + SearchBarDefaults.InputField( + query = searchQuery, + onQueryChange = { searchQuery = it }, + onSearch = {}, + expanded = false, + onExpandedChange = {}, + placeholder = { Text(stringResource(R.string.tailscale_terminal_search_themes)) }, + ) + }, + expanded = false, + onExpandedChange = {}, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) {} + + LazyColumn(modifier = Modifier.fillMaxSize()) { + items(filteredSchemes) { scheme -> + ListItem( + headlineContent = { Text(scheme) }, + leadingContent = { + RadioButton( + selected = scheme == selectedTheme, + onClick = null, + ) + }, + modifier = Modifier.clickable { + selectedTheme = scheme + if (isDark) { + Settings.tailscaleSSHDarkTheme = scheme + } else { + Settings.tailscaleSSHLightTheme = scheme + } + }, + ) + } + } + } +} diff --git a/app/src/main/java/io/nekohasekai/sfa/compose/screen/tools/TailscaleEndpointScreen.kt b/app/src/main/java/io/nekohasekai/sfa/compose/screen/tools/TailscaleEndpointScreen.kt index c19934f..b977fb0 100644 --- a/app/src/main/java/io/nekohasekai/sfa/compose/screen/tools/TailscaleEndpointScreen.kt +++ b/app/src/main/java/io/nekohasekai/sfa/compose/screen/tools/TailscaleEndpointScreen.kt @@ -3,8 +3,10 @@ package io.nekohasekai.sfa.compose.screen.tools import android.content.Intent import android.net.Uri import android.text.format.DateUtils +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -23,8 +25,11 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.outlined.OpenInNew import androidx.compose.material.icons.filled.QrCode2 +import androidx.compose.material.icons.filled.Terminal import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon @@ -59,6 +64,7 @@ import io.nekohasekai.sfa.compose.util.QRCodeGenerator fun TailscaleEndpointScreen( navController: NavController, viewModel: TailscaleStatusViewModel, + sshSharedViewModel: TailscaleSSHSharedViewModel, endpointTag: String, ) { OverrideTopBar { @@ -311,20 +317,50 @@ fun TailscaleEndpointScreen( color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.5f), ) } - PeerItem( - peer = peer, - onClick = { - navController.navigate( - "tools/tailscale/${Uri.encode(endpointTag)}/peer/${Uri.encode(peer.id)}", + val canSSH = peer.online && peer.sshHostKeys.isNotEmpty() && + peer.tailscaleIPs.isNotEmpty() && peer.id != endpoint.selfPeer?.id + var showSSHMenu by remember { mutableStateOf(false) } + Box { + PeerItem( + peer = peer, + onClick = { + navController.navigate( + "tools/tailscale/${Uri.encode(endpointTag)}/peer/${Uri.encode(peer.id)}", + ) + }, + onLongClick = if (canSSH) { + { showSSHMenu = true } + } else { + null + }, + modifier = when { + group.peers.size == 1 -> Modifier.clip(RoundedCornerShape(12.dp)) + index == 0 -> Modifier.clip(RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp)) + index == group.peers.lastIndex -> Modifier.clip(RoundedCornerShape(bottomStart = 12.dp, bottomEnd = 12.dp)) + else -> Modifier + }, + ) + DropdownMenu( + expanded = showSSHMenu, + onDismissRequest = { showSSHMenu = false }, + ) { + DropdownMenuItem( + text = { Text(stringResource(R.string.tailscale_ssh_connect)) }, + leadingIcon = { + Icon(Icons.Default.Terminal, contentDescription = null) + }, + onClick = { + showSSHMenu = false + handleSSHNavigation( + navController, + sshSharedViewModel, + peer, + endpointTag, + ) + }, ) - }, - modifier = when { - group.peers.size == 1 -> Modifier.clip(RoundedCornerShape(12.dp)) - index == 0 -> Modifier.clip(RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp)) - index == group.peers.lastIndex -> Modifier.clip(RoundedCornerShape(bottomStart = 12.dp, bottomEnd = 12.dp)) - else -> Modifier - }, - ) + } + } } } } @@ -352,10 +388,12 @@ private fun SectionHeader(title: String) { ) } +@OptIn(ExperimentalFoundationApi::class) @Composable private fun PeerItem( peer: TailscalePeerData, onClick: () -> Unit, + onLongClick: (() -> Unit)? = null, modifier: Modifier = Modifier, ) { val badges = peerBadges(peer) @@ -363,7 +401,10 @@ private fun PeerItem( Row( modifier = modifier .fillMaxWidth() - .clickable(onClick = onClick) + .combinedClickable( + onClick = onClick, + onLongClick = onLongClick, + ) .padding(horizontal = 16.dp, vertical = 12.dp), horizontalArrangement = Arrangement.spacedBy(12.dp), verticalAlignment = Alignment.CenterVertically, diff --git a/app/src/main/java/io/nekohasekai/sfa/compose/screen/tools/TailscalePeerScreen.kt b/app/src/main/java/io/nekohasekai/sfa/compose/screen/tools/TailscalePeerScreen.kt index 549ec22..839d714 100644 --- a/app/src/main/java/io/nekohasekai/sfa/compose/screen/tools/TailscalePeerScreen.kt +++ b/app/src/main/java/io/nekohasekai/sfa/compose/screen/tools/TailscalePeerScreen.kt @@ -2,6 +2,7 @@ package io.nekohasekai.sfa.compose.screen.tools import android.text.format.DateUtils import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -20,15 +21,19 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.automirrored.filled.ArrowForward import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material.icons.filled.Stop +import androidx.compose.material.icons.filled.Terminal import androidx.compose.material.icons.outlined.ContentCopy import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.ListItem +import androidx.compose.material3.ListItemDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text @@ -406,14 +411,60 @@ fun TailscalePeerScreen( ) } if (peer.sshHostKeys.isNotEmpty()) { - DetailRow( - label = stringResource(R.string.tailscale_ssh), - value = stringResource(R.string.tailscale_available), - ) + if (!peer.online || isSelf || peer.tailscaleIPs.isEmpty()) { + DetailRow( + label = stringResource(R.string.tailscale_ssh), + value = stringResource(R.string.tailscale_available), + ) + } } } } + if (peer.sshHostKeys.isNotEmpty() && peer.online && !isSelf && peer.tailscaleIPs.isNotEmpty()) { + Spacer(modifier = Modifier.height(16.dp)) + Card( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f), + ), + ) { + ListItem( + headlineContent = { + Text( + stringResource(R.string.tailscale_ssh_connect), + style = MaterialTheme.typography.bodyMedium, + ) + }, + leadingContent = { + Icon( + imageVector = Icons.Default.Terminal, + contentDescription = null, + tint = MaterialTheme.colorScheme.primary, + ) + }, + trailingContent = { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowForward, + contentDescription = null, + tint = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.size(16.dp), + ) + }, + modifier = Modifier + .clip(RoundedCornerShape(12.dp)) + .clickable { + navController.navigate( + "tools/tailscale/${android.net.Uri.encode(endpointTag)}/peer/${android.net.Uri.encode(peerId)}/ssh", + ) + }, + colors = ListItemDefaults.colors(containerColor = Color.Transparent), + ) + } + } + Spacer(modifier = Modifier.height(24.dp)) } diff --git a/app/src/main/java/io/nekohasekai/sfa/compose/screen/tools/TailscaleSSHPromptScreen.kt b/app/src/main/java/io/nekohasekai/sfa/compose/screen/tools/TailscaleSSHPromptScreen.kt new file mode 100644 index 0000000..61e9207 --- /dev/null +++ b/app/src/main/java/io/nekohasekai/sfa/compose/screen/tools/TailscaleSSHPromptScreen.kt @@ -0,0 +1,218 @@ +package io.nekohasekai.sfa.compose.screen.tools + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material3.Button +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.ListItem +import androidx.compose.material3.ListItemDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Switch +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.KeyboardCapitalization +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController +import io.nekohasekai.sfa.R +import io.nekohasekai.sfa.compose.topbar.OverrideTopBar +import io.nekohasekai.sfa.database.Settings +import io.nekohasekai.sfa.terminal.TailscaleSSHPresentedSession + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun TailscaleSSHPromptScreen( + navController: NavController, + sharedViewModel: TailscaleSSHSharedViewModel, + viewModel: TailscaleStatusViewModel, + endpointTag: String, + peerId: String, +) { + val peer = viewModel.peer(endpointTag, peerId) + + if (peer == null) { + LaunchedEffect(Unit) { navController.navigateUp() } + return + } + + val rememberedUsernames = Settings.tailscaleSSHRememberedUsernames + val quickConnectPeers = Settings.tailscaleSSHQuickConnectPeers + + var username by remember { + mutableStateOf(rememberedUsernames[peer.stableID]?.takeIf { it.isNotBlank() } ?: "root") + } + var rememberOptions by remember { + mutableStateOf(quickConnectPeers.contains(peer.stableID)) + } + + OverrideTopBar { + TopAppBar( + title = { Text(peer.hostName) }, + navigationIcon = { + IconButton(onClick = { navController.navigateUp() }) { + Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.content_description_back)) + } + }, + ) + } + + Column( + modifier = Modifier + .fillMaxSize() + .background(MaterialTheme.colorScheme.surface) + .verticalScroll(rememberScrollState()) + .padding(vertical = 8.dp), + ) { + Text( + text = stringResource(R.string.tailscale_ssh_options), + style = MaterialTheme.typography.titleSmall, + color = MaterialTheme.colorScheme.primary, + modifier = Modifier.padding(horizontal = 32.dp, vertical = 8.dp), + ) + Card( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surfaceContainer, + ), + ) { + Column(modifier = Modifier.padding(16.dp)) { + OutlinedTextField( + value = username, + onValueChange = { username = it }, + label = { Text(stringResource(R.string.tailscale_ssh_username)) }, + singleLine = true, + keyboardOptions = KeyboardOptions( + capitalization = KeyboardCapitalization.None, + autoCorrectEnabled = false, + imeAction = ImeAction.Done, + ), + modifier = Modifier.fillMaxWidth(), + ) + } + } + + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = stringResource(R.string.tailscale_ssh_terminal_hint), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.padding(horizontal = 32.dp), + ) + + Spacer(modifier = Modifier.height(16.dp)) + + Text( + text = stringResource(R.string.tailscale_ssh_quick_connect), + style = MaterialTheme.typography.titleSmall, + color = MaterialTheme.colorScheme.tertiary, + modifier = Modifier.padding(horizontal = 32.dp, vertical = 8.dp), + ) + Card( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surfaceContainer, + ), + ) { + ListItem( + headlineContent = { Text(stringResource(R.string.tailscale_ssh_remember_options)) }, + trailingContent = { + Switch( + checked = rememberOptions, + onCheckedChange = { checked -> + rememberOptions = checked + val peers = Settings.tailscaleSSHQuickConnectPeers.toMutableSet() + if (checked) { + peers.add(peer.stableID) + } else { + peers.remove(peer.stableID) + } + Settings.tailscaleSSHQuickConnectPeers = peers + }, + ) + }, + modifier = Modifier.clip(RoundedCornerShape(12.dp)), + colors = ListItemDefaults.colors(containerColor = Color.Transparent), + ) + } + + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = stringResource(R.string.tailscale_ssh_quick_connect_description), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.padding(horizontal = 32.dp), + ) + + Spacer(modifier = Modifier.height(24.dp)) + + Button( + onClick = { + val trimmedUsername = username.trim() + val usernames = Settings.tailscaleSSHRememberedUsernames.toMutableMap() + if (trimmedUsername == "root") { + usernames.remove(peer.stableID) + } else { + usernames[peer.stableID] = trimmedUsername + } + Settings.tailscaleSSHRememberedUsernames = usernames + + sharedViewModel.setPendingSession( + TailscaleSSHPresentedSession( + endpointTag = endpointTag, + peerHostName = peer.hostName, + peerAddress = peer.tailscaleIPs.first(), + username = trimmedUsername, + hostKeys = peer.sshHostKeys, + ), + ) + navController.navigate( + "tools/tailscale/${android.net.Uri.encode(endpointTag)}/peer/${android.net.Uri.encode(peerId)}/terminal", + ) { + popUpTo( + "tools/tailscale/${android.net.Uri.encode(endpointTag)}/peer/${android.net.Uri.encode(peerId)}/ssh", + ) { + inclusive = true + } + } + }, + enabled = username.trim().isNotEmpty(), + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + ) { + Text(stringResource(R.string.tailscale_ssh_connect_button)) + } + + Spacer(modifier = Modifier.height(16.dp)) + } +} diff --git a/app/src/main/java/io/nekohasekai/sfa/compose/screen/tools/TailscaleSSHSharedViewModel.kt b/app/src/main/java/io/nekohasekai/sfa/compose/screen/tools/TailscaleSSHSharedViewModel.kt new file mode 100644 index 0000000..6f80533 --- /dev/null +++ b/app/src/main/java/io/nekohasekai/sfa/compose/screen/tools/TailscaleSSHSharedViewModel.kt @@ -0,0 +1,22 @@ +package io.nekohasekai.sfa.compose.screen.tools + +import io.nekohasekai.sfa.compose.base.BaseViewModel +import io.nekohasekai.sfa.terminal.TailscaleSSHPresentedSession + +data class TailscaleSSHSharedState( + val pendingSession: TailscaleSSHPresentedSession? = null, +) + +class TailscaleSSHSharedViewModel : BaseViewModel() { + override fun createInitialState() = TailscaleSSHSharedState() + + fun setPendingSession(session: TailscaleSSHPresentedSession) { + updateState { copy(pendingSession = session) } + } + + fun consumePendingSession(): TailscaleSSHPresentedSession? { + val session = currentState.pendingSession + updateState { copy(pendingSession = null) } + return session + } +} diff --git a/app/src/main/java/io/nekohasekai/sfa/compose/screen/tools/TailscaleSSHTerminalScreen.kt b/app/src/main/java/io/nekohasekai/sfa/compose/screen/tools/TailscaleSSHTerminalScreen.kt new file mode 100644 index 0000000..22c3ab2 --- /dev/null +++ b/app/src/main/java/io/nekohasekai/sfa/compose/screen/tools/TailscaleSSHTerminalScreen.kt @@ -0,0 +1,431 @@ +package io.nekohasekai.sfa.compose.screen.tools + +import android.content.Context +import android.graphics.Typeface +import android.view.KeyEvent +import android.view.MotionEvent +import android.view.inputmethod.InputMethodManager +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.ExpandLess +import androidx.compose.material.icons.filled.ExpandMore +import androidx.compose.material.icons.filled.MoreVert +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.RadioButton +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.viewinterop.AndroidView +import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.NavController +import com.termux.terminal.TerminalSession +import com.termux.terminal.TerminalSessionClient +import com.termux.view.TerminalView +import com.termux.view.TerminalViewClient +import io.nekohasekai.sfa.R +import io.nekohasekai.sfa.compose.topbar.OverrideTopBar +import io.nekohasekai.sfa.database.Settings +import io.nekohasekai.sfa.terminal.ImportedFontStore +import io.nekohasekai.sfa.terminal.TailscaleSSHPresentedSession +import io.nekohasekai.sfa.terminal.TailscaleSSHTerminalSession +import io.nekohasekai.sfa.terminal.TerminalColorSchemeLoader +import io.nekohasekai.sfa.terminal.TerminalExtraKeysState + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun TailscaleSSHTerminalScreen( + navController: NavController, + sharedViewModel: TailscaleSSHSharedViewModel, + tailscaleViewModel: TailscaleStatusViewModel, +) { + val terminalViewModel: TailscaleSSHTerminalViewModel = viewModel() + val state by terminalViewModel.uiState.collectAsState() + val context = LocalContext.current + val isDark = isSystemInDarkTheme() + val keyboardController = LocalSoftwareKeyboardController.current + + val terminalViewRef = remember { TerminalViewRef() } + val extraKeysState = remember { TerminalExtraKeysState() } + val sessionClient = remember { createSessionClient(terminalViewModel, terminalViewRef) } + + LaunchedEffect(Unit) { + terminalViewModel.sessionClient = sessionClient + val pending = sharedViewModel.consumePendingSession() + if (pending != null && state.sessions.isEmpty()) { + terminalViewModel.addSession(pending) + } + } + + val activeSession = state.activeSession + val displayTitle = when { + activeSession == null -> "" + activeSession.terminalSession.phase == TailscaleSSHTerminalSession.Phase.CONNECTING -> + activeSession.presentedSession.peerHostName + else -> { + val termTitle = activeSession.terminalSession.title + if (termTitle.isNullOrBlank()) activeSession.presentedSession.peerHostName else termTitle + } + } + + var showSessionMenu by remember { mutableStateOf(false) } + var expandedNewSession by remember { mutableStateOf(false) } + + val tailscaleState by tailscaleViewModel.uiState.collectAsState() + val quickConnectPeerIDs = Settings.tailscaleSSHQuickConnectPeers + val otherQCPeers = remember(tailscaleState, quickConnectPeerIDs, activeSession) { + val currentAddress = activeSession?.presentedSession?.peerAddress + val currentEndpointTag = activeSession?.presentedSession?.endpointTag + tailscaleState.endpoints.flatMap { endpoint -> + endpoint.userGroups.flatMap { group -> + group.peers.filter { peer -> + peer.online && peer.sshHostKeys.isNotEmpty() && + peer.tailscaleIPs.isNotEmpty() && + peer.id != endpoint.selfPeer?.id && + quickConnectPeerIDs.contains(peer.stableID) && + !(endpoint.endpointTag == currentEndpointTag && peer.tailscaleIPs.firstOrNull() == currentAddress) + }.map { peer -> peer to endpoint.endpointTag } + } + } + } + + OverrideTopBar { + TopAppBar( + title = { Text(displayTitle, style = MaterialTheme.typography.titleMedium) }, + navigationIcon = { + IconButton(onClick = { + keyboardController?.hide() + navController.navigateUp() + }) { + Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) + } + }, + actions = { + Box { + IconButton(onClick = { showSessionMenu = true }) { + Icon(Icons.Default.MoreVert, contentDescription = null) + } + DropdownMenu( + expanded = showSessionMenu, + onDismissRequest = { + showSessionMenu = false + expandedNewSession = false + }, + ) { + if (otherQCPeers.isEmpty()) { + DropdownMenuItem( + text = { Text(stringResource(R.string.tailscale_ssh_new_session)) }, + leadingIcon = { Icon(Icons.Default.Add, contentDescription = null) }, + onClick = { + showSessionMenu = false + terminalViewModel.duplicateCurrentSession() + }, + ) + } else { + DropdownMenuItem( + text = { Text(stringResource(R.string.tailscale_ssh_new_session)) }, + leadingIcon = { Icon(Icons.Default.Add, contentDescription = null) }, + trailingIcon = { + Icon( + if (expandedNewSession) Icons.Default.ExpandLess else Icons.Default.ExpandMore, + contentDescription = null, + ) + }, + onClick = { expandedNewSession = !expandedNewSession }, + ) + if (expandedNewSession) { + if (activeSession != null) { + DropdownMenuItem( + text = { + Text( + activeSession.presentedSession.peerHostName, + modifier = Modifier.padding(start = 24.dp), + ) + }, + onClick = { + showSessionMenu = false + expandedNewSession = false + terminalViewModel.duplicateCurrentSession() + }, + ) + } + otherQCPeers.forEach { (peer, endpointTag) -> + val usernames = Settings.tailscaleSSHRememberedUsernames + DropdownMenuItem( + text = { + Text( + peer.hostName, + modifier = Modifier.padding(start = 24.dp), + ) + }, + onClick = { + showSessionMenu = false + expandedNewSession = false + terminalViewModel.addSession( + TailscaleSSHPresentedSession( + endpointTag = endpointTag, + peerHostName = peer.hostName, + peerAddress = peer.tailscaleIPs.first(), + username = usernames[peer.stableID]?.takeIf { it.isNotBlank() } ?: "root", + hostKeys = peer.sshHostKeys, + ), + ) + }, + ) + } + } + } + if (state.sessions.size > 1) { + HorizontalDivider() + state.sessions.forEach { session -> + val isActive = session.id == state.activeSessionId + val sessionTitle = when { + session.terminalSession.phase == TailscaleSSHTerminalSession.Phase.CONNECTING -> + session.presentedSession.peerHostName + else -> { + val termTitle = session.terminalSession.title + if (termTitle.isNullOrBlank()) session.presentedSession.peerHostName else termTitle + } + } + DropdownMenuItem( + text = { Text(sessionTitle) }, + onClick = { + showSessionMenu = false + terminalViewModel.switchSession(session.id) + }, + leadingIcon = { + RadioButton( + selected = isActive, + onClick = null, + ) + }, + ) + } + } + } + } + }, + ) + } + + Column(modifier = Modifier.fillMaxSize().imePadding()) { + if (activeSession != null) { + Box(modifier = Modifier.weight(1f)) { + val themeName = if (isDark) Settings.tailscaleSSHDarkTheme else Settings.tailscaleSSHLightTheme + val fontSize = Settings.tailscaleSSHFontSize + val fontFamily = Settings.tailscaleSSHFontFamily + val customFontPath = Settings.tailscaleSSHCustomFontPath + + AndroidView( + factory = { ctx -> + TerminalColorSchemeLoader.applyScheme(ctx, themeName) + TerminalView(ctx, null).apply { + isFocusable = true + isFocusableInTouchMode = true + terminalViewRef.view = this + setTerminalViewClient( + createViewClient(ctx, this, extraKeysState) { + val active = terminalViewModel.uiState.value.activeSession + if (active != null) { + terminalViewModel.removeSession(active.id) + if (terminalViewModel.uiState.value.sessions.isEmpty()) { + keyboardController?.hide() + navController.navigateUp() + } + } + }, + ) + attachSession(activeSession.terminalSession) + setTextSize(fontSize) + val typeface = resolveTypeface(fontFamily, customFontPath) + if (typeface != null) { + setTypeface(typeface) + } + post { + requestFocus() + val imm = ctx.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + imm.showSoftInput(this, InputMethodManager.SHOW_IMPLICIT) + } + } + }, + update = { view -> + if (view.currentSession !== activeSession.terminalSession) { + TerminalColorSchemeLoader.applyScheme(context, themeName) + view.attachSession(activeSession.terminalSession) + view.onScreenUpdated() + } + }, + modifier = Modifier.fillMaxSize(), + ) + + if (activeSession.terminalSession.phase == TailscaleSSHTerminalSession.Phase.CONNECTING) { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center, + ) { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + CircularProgressIndicator() + Text( + stringResource(R.string.tailscale_ssh_connecting), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurface, + modifier = Modifier.padding(top = 16.dp), + ) + } + } + } + } + TerminalExtraKeysBar( + terminalView = terminalViewRef.view, + extraKeysState = extraKeysState, + modifier = Modifier.fillMaxWidth(), + ) + } else if (state.sessions.isEmpty()) { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center, + ) { + Text( + stringResource(R.string.tailscale_ssh_no_sessions), + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } + } + + DisposableEffect(Unit) { + onDispose { + keyboardController?.hide() + } + } +} + +private class TerminalViewRef { + var view: TerminalView? = null +} + +private fun resolveTypeface(fontFamily: String, customFontPath: String): Typeface? { + if (customFontPath.isNotBlank()) { + val typeface = ImportedFontStore.loadTypeface(customFontPath) + if (typeface != null) return typeface + } + if (fontFamily.isNotBlank()) { + return Typeface.create(fontFamily, Typeface.NORMAL) + } + return null +} + +private fun createSessionClient(viewModel: TailscaleSSHTerminalViewModel, viewRef: TerminalViewRef): TerminalSessionClient = object : TerminalSessionClient { + override fun onTextChanged(changedSession: TerminalSession) { + viewRef.view?.onScreenUpdated() + } + override fun onTitleChanged(changedSession: TerminalSession) { + viewModel.onTitleChanged() + } + override fun onSessionFinished(finishedSession: TerminalSession) { + val state = viewModel.uiState.value + if (state.sessions.size > 1) { + val managed = state.sessions.firstOrNull { it.terminalSession === finishedSession } + if (managed != null) { + viewModel.removeSession(managed.id) + } + } + } + override fun onCopyTextToClipboard(session: TerminalSession, text: String) {} + override fun onPasteTextFromClipboard(session: TerminalSession?) {} + override fun onBell(session: TerminalSession) {} + override fun onColorsChanged(session: TerminalSession) {} + override fun onTerminalCursorStateChange(state: Boolean) {} + override fun setTerminalShellPid(session: TerminalSession, pid: Int) {} + override fun getTerminalCursorStyle(): Int = 0 + override fun logError(tag: String, message: String) {} + override fun logWarn(tag: String, message: String) {} + override fun logInfo(tag: String, message: String) {} + override fun logDebug(tag: String, message: String) {} + override fun logVerbose(tag: String, message: String) {} + override fun logStackTraceWithMessage(tag: String, message: String, e: Exception) {} + override fun logStackTrace(tag: String, e: Exception) {} +} + +private fun createViewClient(context: Context, terminalView: TerminalView, extraKeysState: TerminalExtraKeysState, onDismissFinishedSession: () -> Unit): TerminalViewClient = object : TerminalViewClient { + override fun onScale(scale: Float): Float { + if (scale < 0.9f || scale > 1.1f) { + val increase = scale > 1f + var currentSize = Settings.tailscaleSSHFontSize + currentSize = if (increase) { + (currentSize + 1).coerceAtMost(48) + } else { + (currentSize - 1).coerceAtLeast(8) + } + Settings.tailscaleSSHFontSize = currentSize + terminalView.setTextSize(currentSize) + return 1.0f + } + return scale + } + override fun onSingleTapUp(e: MotionEvent) { + terminalView.requestFocus() + val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + imm.showSoftInput(terminalView, 0) + } + override fun shouldBackButtonBeMappedToEscape(): Boolean = false + override fun shouldEnforceCharBasedInput(): Boolean = true + override fun shouldUseCtrlSpaceWorkaround(): Boolean = false + override fun isTerminalViewSelected(): Boolean = true + override fun copyModeChanged(copyMode: Boolean) {} + override fun onKeyDown(keyCode: Int, e: KeyEvent, session: TerminalSession): Boolean { + if (!session.isRunning) { + onDismissFinishedSession() + return true + } + return false + } + override fun onKeyUp(keyCode: Int, e: KeyEvent): Boolean = false + override fun onLongPress(event: MotionEvent): Boolean = false + override fun readControlKey(): Boolean = extraKeysState.isCtrlActive + override fun readAltKey(): Boolean = extraKeysState.isAltActive + override fun readShiftKey(): Boolean = false + override fun readFnKey(): Boolean = false + override fun onCodePoint(codePoint: Int, ctrlDown: Boolean, session: TerminalSession): Boolean { + if (!session.isRunning) { + onDismissFinishedSession() + return true + } + return false + } + override fun onEmulatorSet() {} + override fun logError(tag: String, message: String) {} + override fun logWarn(tag: String, message: String) {} + override fun logInfo(tag: String, message: String) {} + override fun logDebug(tag: String, message: String) {} + override fun logVerbose(tag: String, message: String) {} + override fun logStackTraceWithMessage(tag: String, message: String, e: Exception) {} + override fun logStackTrace(tag: String, e: Exception) {} +} diff --git a/app/src/main/java/io/nekohasekai/sfa/compose/screen/tools/TailscaleSSHTerminalViewModel.kt b/app/src/main/java/io/nekohasekai/sfa/compose/screen/tools/TailscaleSSHTerminalViewModel.kt new file mode 100644 index 0000000..b5cedec --- /dev/null +++ b/app/src/main/java/io/nekohasekai/sfa/compose/screen/tools/TailscaleSSHTerminalViewModel.kt @@ -0,0 +1,143 @@ +package io.nekohasekai.sfa.compose.screen.tools + +import android.util.Log +import androidx.lifecycle.viewModelScope +import com.termux.terminal.TerminalSession +import com.termux.terminal.TerminalSessionClient +import io.nekohasekai.libbox.Libbox +import io.nekohasekai.libbox.StringIterator +import io.nekohasekai.libbox.TailscaleSSHHandler +import io.nekohasekai.libbox.TailscaleSSHOptions +import io.nekohasekai.sfa.compose.base.BaseViewModel +import io.nekohasekai.sfa.terminal.ManagedSession +import io.nekohasekai.sfa.terminal.TailscaleSSHPresentedSession +import io.nekohasekai.sfa.terminal.TailscaleSSHTerminalSession +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +data class TailscaleSSHTerminalState( + val sessions: List = emptyList(), + val activeSessionId: String? = null, + val version: Int = 0, +) { + val activeSession: ManagedSession? + get() = sessions.firstOrNull { it.id == activeSessionId } +} + +class TailscaleSSHTerminalViewModel : BaseViewModel() { + override fun createInitialState() = TailscaleSSHTerminalState() + + var sessionClient: TerminalSessionClient? = null + + fun addSession(presented: TailscaleSSHPresentedSession) { + val client = sessionClient ?: return + val terminalSession = TailscaleSSHTerminalSession(client) + val managed = ManagedSession( + terminalSession = terminalSession, + presentedSession = presented, + ) + + terminalSession.setPhaseCallback(object : TailscaleSSHTerminalSession.PhaseCallback { + override fun onPhaseChanged(phase: TailscaleSSHTerminalSession.Phase) { + updateState { copy(sessions = sessions.toList(), version = version + 1) } + } + + override fun onAuthBanner(message: String) { + } + }) + + updateState { + copy( + sessions = sessions + managed, + activeSessionId = managed.id, + ) + } + + startSSHConnection(managed) + } + + fun removeSession(id: String) { + val session = currentState.sessions.firstOrNull { it.id == id } ?: return + session.terminalSession.close() + val remaining = currentState.sessions.filter { it.id != id } + val newActiveId = if (currentState.activeSessionId == id) { + remaining.lastOrNull()?.id + } else { + currentState.activeSessionId + } + updateState { copy(sessions = remaining, activeSessionId = newActiveId) } + } + + fun switchSession(id: String) { + updateState { copy(activeSessionId = id) } + } + + fun duplicateCurrentSession() { + val current = currentState.activeSession ?: return + addSession(current.presentedSession) + } + + fun onTitleChanged() { + updateState { copy(version = version + 1) } + } + + private fun startSSHConnection(managed: ManagedSession) { + viewModelScope.launch(Dispatchers.IO) { + try { + val options = TailscaleSSHOptions().apply { + endpointTag = managed.presentedSession.endpointTag + peerAddress = managed.presentedSession.peerAddress + username = managed.presentedSession.username + terminalType = "xterm-256color" + columns = 80 + rows = 24 + widthPixels = 0 + heightPixels = 0 + hostKeys = object : StringIterator { + private var index = 0 + override fun hasNext(): Boolean = index < managed.presentedSession.hostKeys.size + override fun next(): String = managed.presentedSession.hostKeys[index++] + override fun len(): Int = managed.presentedSession.hostKeys.size + } + forwardAgent = false + } + + val commandClient = Libbox.newStandaloneCommandClient() + val sshSession = commandClient.startTailscaleSSHSession( + options, + object : TailscaleSSHHandler { + override fun onReady() { + managed.terminalSession.onReady() + } + + override fun onOutput(data: ByteArray) { + managed.terminalSession.feedOutput(data) + } + + override fun onAuthBanner(message: String) { + managed.terminalSession.onAuthBanner(message) + } + + override fun onExit(exitCode: Int, signal: String, errorMessage: String) { + managed.terminalSession.onExit(exitCode, signal, errorMessage) + } + + override fun onError(message: String) { + managed.terminalSession.onError(message) + } + }, + ) + managed.terminalSession.setSshSession(sshSession) + } catch (e: Exception) { + managed.terminalSession.onError(e.message ?: "SSH connection failed") + } + } + } + + override fun onCleared() { + for (session in currentState.sessions) { + session.terminalSession.close() + } + super.onCleared() + } +} diff --git a/app/src/main/java/io/nekohasekai/sfa/compose/screen/tools/TerminalExtraKeysBar.kt b/app/src/main/java/io/nekohasekai/sfa/compose/screen/tools/TerminalExtraKeysBar.kt new file mode 100644 index 0000000..920693a --- /dev/null +++ b/app/src/main/java/io/nekohasekai/sfa/compose/screen/tools/TerminalExtraKeysBar.kt @@ -0,0 +1,278 @@ +package io.nekohasekai.sfa.compose.screen.tools + +import android.content.ClipboardManager +import android.content.Context +import android.view.KeyEvent +import androidx.compose.foundation.background +import androidx.compose.foundation.horizontalScroll +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.interaction.collectIsPressedAsState +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.defaultMinSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ContentPaste +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.termux.view.TerminalView +import io.nekohasekai.sfa.terminal.TerminalExtraKeysState +import io.nekohasekai.sfa.terminal.TerminalExtraKeysState.StickyModifierState +import kotlinx.coroutines.delay + +@Composable +fun TerminalExtraKeysBar( + terminalView: TerminalView?, + extraKeysState: TerminalExtraKeysState, + modifier: Modifier = Modifier, +) { + val context = LocalContext.current + val ctrlState by extraKeysState.ctrlState.collectAsState() + val altState by extraKeysState.altState.collectAsState() + + Surface( + modifier = modifier.fillMaxWidth(), + color = MaterialTheme.colorScheme.surfaceContainerHigh, + tonalElevation = 2.dp, + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .horizontalScroll(rememberScrollState()) + .padding(horizontal = 6.dp, vertical = 4.dp), + horizontalArrangement = Arrangement.spacedBy(4.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + ExtraKeyButton("ESC") { + terminalView?.handleKeyCode(KeyEvent.KEYCODE_ESCAPE, extraKeysState.currentKeyMod()) + extraKeysState.consumeModifiers() + terminalView?.requestFocus() + } + ExtraKeyButton("TAB") { + terminalView?.handleKeyCode(KeyEvent.KEYCODE_TAB, extraKeysState.currentKeyMod()) + extraKeysState.consumeModifiers() + terminalView?.requestFocus() + } + + StickyModifierButton("CTRL", ctrlState) { + extraKeysState.toggleCtrl(System.currentTimeMillis()) + terminalView?.requestFocus() + } + StickyModifierButton("ALT", altState) { + extraKeysState.toggleAlt(System.currentTimeMillis()) + terminalView?.requestFocus() + } + + Divider() + + RepeatableKeyButton("◀") { + terminalView?.handleKeyCode( + KeyEvent.KEYCODE_DPAD_LEFT, + extraKeysState.currentKeyMod(), + ) + extraKeysState.consumeModifiers() + terminalView?.requestFocus() + } + RepeatableKeyButton("▲") { + terminalView?.handleKeyCode( + KeyEvent.KEYCODE_DPAD_UP, + extraKeysState.currentKeyMod(), + ) + extraKeysState.consumeModifiers() + terminalView?.requestFocus() + } + RepeatableKeyButton("▼") { + terminalView?.handleKeyCode( + KeyEvent.KEYCODE_DPAD_DOWN, + extraKeysState.currentKeyMod(), + ) + extraKeysState.consumeModifiers() + terminalView?.requestFocus() + } + RepeatableKeyButton("▶") { + terminalView?.handleKeyCode( + KeyEvent.KEYCODE_DPAD_RIGHT, + extraKeysState.currentKeyMod(), + ) + extraKeysState.consumeModifiers() + terminalView?.requestFocus() + } + + Divider() + + for (ch in symbolKeys) { + ExtraKeyButton(ch.toString()) { + terminalView?.inputCodePoint( + TerminalView.KEY_EVENT_SOURCE_VIRTUAL_KEYBOARD, + ch.code, + false, + false, + ) + extraKeysState.consumeModifiers() + terminalView?.requestFocus() + } + } + + IconKeyButton(Icons.Default.ContentPaste) { + val clipboard = + context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val clip = clipboard.primaryClip + if (clip != null && clip.itemCount > 0) { + val text = clip.getItemAt(0).coerceToText(context).toString() + if (text.isNotEmpty()) { + terminalView?.mEmulator?.paste(text) + } + } + extraKeysState.consumeModifiers() + terminalView?.requestFocus() + } + } + } +} + +private val symbolKeys = charArrayOf('|', '/', '~', '-', '_', '`', '\'', '"') + +@Composable +private fun ExtraKeyButton(label: String, onClick: () -> Unit) { + Surface( + onClick = onClick, + modifier = Modifier.defaultMinSize(minWidth = 42.dp).height(36.dp), + shape = RoundedCornerShape(8.dp), + color = MaterialTheme.colorScheme.surfaceContainerHighest, + ) { + Box(contentAlignment = Alignment.Center, modifier = Modifier.padding(horizontal = 4.dp)) { + Text( + text = label, + fontSize = if (label.length > 1) 10.sp else 14.sp, + fontWeight = FontWeight.SemiBold, + fontFamily = FontFamily.Monospace, + color = MaterialTheme.colorScheme.onSurface, + maxLines = 1, + ) + } + } +} + +@Composable +private fun StickyModifierButton( + label: String, + state: StickyModifierState, + onClick: () -> Unit, +) { + val backgroundColor = when (state) { + StickyModifierState.INACTIVE -> MaterialTheme.colorScheme.surfaceContainerHighest + StickyModifierState.ARMED -> MaterialTheme.colorScheme.primaryContainer + StickyModifierState.LOCKED -> MaterialTheme.colorScheme.primary + } + val textColor = when (state) { + StickyModifierState.INACTIVE -> MaterialTheme.colorScheme.onSurface + StickyModifierState.ARMED -> MaterialTheme.colorScheme.onPrimaryContainer + StickyModifierState.LOCKED -> MaterialTheme.colorScheme.onPrimary + } + + Surface( + onClick = onClick, + modifier = Modifier.defaultMinSize(minWidth = 42.dp).height(36.dp), + shape = RoundedCornerShape(8.dp), + color = backgroundColor, + ) { + Box(contentAlignment = Alignment.Center, modifier = Modifier.padding(horizontal = 4.dp)) { + Text( + text = label, + fontSize = 10.sp, + fontWeight = FontWeight.SemiBold, + fontFamily = FontFamily.Monospace, + color = textColor, + maxLines = 1, + ) + } + } +} + +@Composable +private fun RepeatableKeyButton(label: String, onAction: () -> Unit) { + val interactionSource = remember { MutableInteractionSource() } + val isPressed by interactionSource.collectIsPressedAsState() + + LaunchedEffect(isPressed) { + if (isPressed) { + delay(REPEAT_INITIAL_DELAY_MS) + while (true) { + onAction() + delay(REPEAT_INTERVAL_MS) + } + } + } + + Surface( + onClick = onAction, + interactionSource = interactionSource, + modifier = Modifier.size(36.dp), + shape = RoundedCornerShape(8.dp), + color = MaterialTheme.colorScheme.surfaceContainerHighest, + ) { + Box(contentAlignment = Alignment.Center) { + Text( + text = label, + fontSize = 12.sp, + color = MaterialTheme.colorScheme.onSurface, + maxLines = 1, + ) + } + } +} + +@Composable +private fun IconKeyButton(icon: androidx.compose.ui.graphics.vector.ImageVector, onClick: () -> Unit) { + Surface( + onClick = onClick, + modifier = Modifier.size(36.dp), + shape = RoundedCornerShape(8.dp), + color = MaterialTheme.colorScheme.surfaceContainerHighest, + ) { + Box(contentAlignment = Alignment.Center) { + Icon( + imageVector = icon, + contentDescription = null, + modifier = Modifier.size(18.dp), + tint = MaterialTheme.colorScheme.onSurface, + ) + } + } +} + +@Composable +private fun Divider() { + Box( + modifier = Modifier + .padding(horizontal = 2.dp) + .size(5.dp) + .clip(CircleShape) + .background(MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.3f)), + ) +} + +private const val REPEAT_INITIAL_DELAY_MS = 400L +private const val REPEAT_INTERVAL_MS = 80L diff --git a/app/src/main/java/io/nekohasekai/sfa/compose/screen/tools/ToolsScreen.kt b/app/src/main/java/io/nekohasekai/sfa/compose/screen/tools/ToolsScreen.kt index b0e0207..11ed9ce 100644 --- a/app/src/main/java/io/nekohasekai/sfa/compose/screen/tools/ToolsScreen.kt +++ b/app/src/main/java/io/nekohasekai/sfa/compose/screen/tools/ToolsScreen.kt @@ -1,8 +1,11 @@ package io.nekohasekai.sfa.compose.screen.tools import android.net.Uri +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -11,6 +14,7 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Terminal import androidx.compose.material.icons.outlined.BugReport import androidx.compose.material.icons.outlined.Hub import androidx.compose.material.icons.outlined.Memory @@ -18,6 +22,8 @@ import androidx.compose.material.icons.outlined.NetworkCheck import androidx.compose.material3.Badge import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.ListItem @@ -29,6 +35,10 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color @@ -40,13 +50,16 @@ import io.nekohasekai.sfa.bg.CrashReportManager import io.nekohasekai.sfa.bg.OOMReportManager import io.nekohasekai.sfa.compose.topbar.OverrideTopBar import io.nekohasekai.sfa.constant.Status +import io.nekohasekai.sfa.database.Settings +import io.nekohasekai.sfa.terminal.TailscaleSSHPresentedSession -@OptIn(ExperimentalMaterial3Api::class) +@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) @Composable fun ToolsScreen( navController: NavController, serviceStatus: Status = Status.Stopped, tailscaleViewModel: TailscaleStatusViewModel, + sshSharedViewModel: TailscaleSSHSharedViewModel, ) { OverrideTopBar { TopAppBar( @@ -97,31 +110,92 @@ fun ToolsScreen( index == endpoints.size - 1 -> RoundedCornerShape(bottomStart = 12.dp, bottomEnd = 12.dp) else -> RoundedCornerShape(0.dp) } - ListItem( - headlineContent = { - Text( - if (endpoints.size == 1) { - stringResource(R.string.tailscale) - } else { - stringResource(R.string.tailscale_with_tag, endpoint.endpointTag) - }, - style = MaterialTheme.typography.bodyLarge, - ) - }, - leadingContent = { - Icon( - imageVector = Icons.Outlined.Hub, - contentDescription = null, - tint = MaterialTheme.colorScheme.primary, - ) - }, - modifier = Modifier - .clip(shape) - .clickable { - navController.navigate("tools/tailscale/${Uri.encode(endpoint.endpointTag)}") + var showSSHMenu by remember { mutableStateOf(false) } + val sshPeers = remember(endpoint) { + endpoint.userGroups.flatMap { it.peers }.filter { peer -> + peer.online && peer.sshHostKeys.isNotEmpty() && + peer.tailscaleIPs.isNotEmpty() && peer.id != endpoint.selfPeer?.id + } + } + Box { + ListItem( + headlineContent = { + Text( + if (endpoints.size == 1) { + stringResource(R.string.tailscale) + } else { + stringResource(R.string.tailscale_with_tag, endpoint.endpointTag) + }, + style = MaterialTheme.typography.bodyLarge, + ) }, - colors = ListItemDefaults.colors(containerColor = Color.Transparent), - ) + leadingContent = { + Icon( + imageVector = Icons.Outlined.Hub, + contentDescription = null, + tint = MaterialTheme.colorScheme.primary, + ) + }, + modifier = Modifier + .clip(shape) + .combinedClickable( + onClick = { + navController.navigate("tools/tailscale/${Uri.encode(endpoint.endpointTag)}") + }, + onLongClick = { + if (sshPeers.isNotEmpty()) { + showSSHMenu = true + } + }, + ), + colors = ListItemDefaults.colors(containerColor = Color.Transparent), + ) + DropdownMenu( + expanded = showSSHMenu, + onDismissRequest = { showSSHMenu = false }, + ) { + if (sshPeers.size == 1) { + DropdownMenuItem( + text = { Text(stringResource(R.string.tailscale_ssh_connect)) }, + leadingIcon = { + Icon(Icons.Default.Terminal, contentDescription = null) + }, + onClick = { + showSSHMenu = false + handleSSHNavigation( + navController, + sshSharedViewModel, + sshPeers[0], + endpoint.endpointTag, + ) + }, + ) + } else { + DropdownMenuItem( + text = { Text(stringResource(R.string.tailscale_ssh_connect)) }, + leadingIcon = { + Icon(Icons.Default.Terminal, contentDescription = null) + }, + enabled = false, + onClick = {}, + ) + sshPeers.forEach { peer -> + DropdownMenuItem( + text = { Text(peer.hostName) }, + onClick = { + showSSHMenu = false + handleSSHNavigation( + navController, + sshSharedViewModel, + peer, + endpoint.endpointTag, + ) + }, + ) + } + } + } + } } } } @@ -251,3 +325,31 @@ fun ToolsScreen( } } } + +internal fun handleSSHNavigation( + navController: NavController, + sshSharedViewModel: TailscaleSSHSharedViewModel, + peer: TailscalePeerData, + endpointTag: String, +) { + val quickConnectPeers = Settings.tailscaleSSHQuickConnectPeers + if (quickConnectPeers.contains(peer.stableID)) { + val usernames = Settings.tailscaleSSHRememberedUsernames + sshSharedViewModel.setPendingSession( + TailscaleSSHPresentedSession( + endpointTag = endpointTag, + peerHostName = peer.hostName, + peerAddress = peer.tailscaleIPs.first(), + username = usernames[peer.stableID]?.takeIf { it.isNotBlank() } ?: "root", + hostKeys = peer.sshHostKeys, + ), + ) + navController.navigate( + "tools/tailscale/${Uri.encode(endpointTag)}/peer/${Uri.encode(peer.id)}/terminal", + ) + } else { + navController.navigate( + "tools/tailscale/${Uri.encode(endpointTag)}/peer/${Uri.encode(peer.id)}/ssh", + ) + } +} diff --git a/app/src/main/java/io/nekohasekai/sfa/constant/SettingsKey.kt b/app/src/main/java/io/nekohasekai/sfa/constant/SettingsKey.kt index 8454278..d78d648 100644 --- a/app/src/main/java/io/nekohasekai/sfa/constant/SettingsKey.kt +++ b/app/src/main/java/io/nekohasekai/sfa/constant/SettingsKey.kt @@ -40,6 +40,15 @@ object SettingsKey { const val DASHBOARD_ITEM_ORDER = "dashboard_item_order" const val DASHBOARD_DISABLED_ITEMS = "dashboard_disabled_items" + // Tailscale SSH + const val TAILSCALE_SSH_REMEMBERED_USERNAMES = "tailscale_ssh_remembered_usernames" + const val TAILSCALE_SSH_QUICK_CONNECT_PEERS = "tailscale_ssh_quick_connect_peers" + const val TAILSCALE_SSH_LIGHT_THEME = "tailscale_ssh_light_theme" + const val TAILSCALE_SSH_DARK_THEME = "tailscale_ssh_dark_theme" + const val TAILSCALE_SSH_FONT_FAMILY = "tailscale_ssh_font_family" + const val TAILSCALE_SSH_FONT_SIZE = "tailscale_ssh_font_size" + const val TAILSCALE_SSH_CUSTOM_FONT_PATH = "tailscale_ssh_custom_font_path" + // cache const val STARTED_BY_USER = "started_by_user" const val CACHED_UPDATE_INFO = "cached_update_info" diff --git a/app/src/main/java/io/nekohasekai/sfa/database/Settings.kt b/app/src/main/java/io/nekohasekai/sfa/database/Settings.kt index 721ac01..e9b22fb 100644 --- a/app/src/main/java/io/nekohasekai/sfa/database/Settings.kt +++ b/app/src/main/java/io/nekohasekai/sfa/database/Settings.kt @@ -14,6 +14,7 @@ import io.nekohasekai.sfa.database.preference.RoomPreferenceDataStore import io.nekohasekai.sfa.ktx.boolean import io.nekohasekai.sfa.ktx.int import io.nekohasekai.sfa.ktx.long +import io.nekohasekai.sfa.ktx.map import io.nekohasekai.sfa.ktx.string import io.nekohasekai.sfa.ktx.stringSet import kotlinx.coroutines.DelicateCoroutinesApi @@ -113,6 +114,15 @@ object Settings { var dashboardItemOrder by dataStore.string(SettingsKey.DASHBOARD_ITEM_ORDER) { "" } var dashboardDisabledItems by dataStore.stringSet(SettingsKey.DASHBOARD_DISABLED_ITEMS) { emptySet() } + // Tailscale SSH + var tailscaleSSHRememberedUsernames by dataStore.map(SettingsKey.TAILSCALE_SSH_REMEMBERED_USERNAMES) + var tailscaleSSHQuickConnectPeers by dataStore.stringSet(SettingsKey.TAILSCALE_SSH_QUICK_CONNECT_PEERS) + var tailscaleSSHLightTheme by dataStore.string(SettingsKey.TAILSCALE_SSH_LIGHT_THEME) { "base16-3024-light" } + var tailscaleSSHDarkTheme by dataStore.string(SettingsKey.TAILSCALE_SSH_DARK_THEME) { "argonaut" } + var tailscaleSSHFontFamily by dataStore.string(SettingsKey.TAILSCALE_SSH_FONT_FAMILY) + var tailscaleSSHFontSize by dataStore.int(SettingsKey.TAILSCALE_SSH_FONT_SIZE) { 14 } + var tailscaleSSHCustomFontPath by dataStore.string(SettingsKey.TAILSCALE_SSH_CUSTOM_FONT_PATH) + var cachedUpdateInfo by dataStore.string(SettingsKey.CACHED_UPDATE_INFO) { "" } var cachedApkPath by dataStore.string(SettingsKey.CACHED_APK_PATH) { "" } var lastShownUpdateVersion by dataStore.int(SettingsKey.LAST_SHOWN_UPDATE_VERSION) { 0 } diff --git a/app/src/main/java/io/nekohasekai/sfa/ktx/Preferences.kt b/app/src/main/java/io/nekohasekai/sfa/ktx/Preferences.kt index bd0fcbd..b51de90 100644 --- a/app/src/main/java/io/nekohasekai/sfa/ktx/Preferences.kt +++ b/app/src/main/java/io/nekohasekai/sfa/ktx/Preferences.kt @@ -1,6 +1,7 @@ package io.nekohasekai.sfa.ktx import androidx.preference.PreferenceDataStore +import kotlinx.serialization.json.Json import kotlin.reflect.KProperty fun PreferenceDataStore.string(name: String, defaultValue: () -> String = { "" }) = PreferenceProxy(name, defaultValue, ::getString, ::putString) @@ -31,6 +32,24 @@ fun PreferenceDataStore.stringToLong(name: String, defaultValue: () -> Long = { fun PreferenceDataStore.stringSet(name: String, defaultValue: () -> Set = { emptySet() }) = PreferenceProxy(name, defaultValue, ::getStringSet, ::putStringSet) +fun PreferenceDataStore.map(name: String, defaultValue: () -> Map = { emptyMap() }) = PreferenceProxy( + name, + defaultValue, + { key, default -> + val json = getString(key, null) + if (json.isNullOrBlank()) { + default + } else { + try { + Json.decodeFromString>(json) + } catch (_: Exception) { + default + } + } + }, + { key, value -> putString(key, Json.encodeToString(value)) }, +) + class PreferenceProxy( val name: String, val defaultValue: () -> T, diff --git a/app/src/main/java/io/nekohasekai/sfa/terminal/ImportedFontStore.kt b/app/src/main/java/io/nekohasekai/sfa/terminal/ImportedFontStore.kt new file mode 100644 index 0000000..d1e0e18 --- /dev/null +++ b/app/src/main/java/io/nekohasekai/sfa/terminal/ImportedFontStore.kt @@ -0,0 +1,65 @@ +package io.nekohasekai.sfa.terminal + +import android.content.Context +import android.graphics.Typeface +import android.net.Uri +import java.io.File + +data class ImportedFont( + val name: String, + val path: String, +) + +object ImportedFontStore { + + private fun fontsDir(context: Context): File = File(context.filesDir, "fonts").also { it.mkdirs() } + + fun listImportedFonts(context: Context): List { + val dir = fontsDir(context) + if (!dir.exists()) return emptyList() + return dir.listFiles() + ?.filter { it.extension in listOf("ttf", "otf", "ttc", "otc") } + ?.mapNotNull { file -> + try { + Typeface.createFromFile(file) + ImportedFont(name = file.nameWithoutExtension, path = file.absolutePath) + } catch (_: Exception) { + null + } + } + ?.sortedBy { it.name } + ?: emptyList() + } + + fun importFont(context: Context, uri: Uri): ImportedFont? { + val resolver = context.contentResolver + val inputStream = resolver.openInputStream(uri) ?: return null + val fileName = uri.lastPathSegment?.substringAfterLast('/') ?: "font.ttf" + val destFile = File(fontsDir(context), fileName) + inputStream.use { input -> + destFile.outputStream().use { output -> + input.copyTo(output) + } + } + return try { + Typeface.createFromFile(destFile) + ImportedFont(name = destFile.nameWithoutExtension, path = destFile.absolutePath) + } catch (_: Exception) { + destFile.delete() + null + } + } + + fun deleteFont(context: Context, name: String) { + val dir = fontsDir(context) + dir.listFiles() + ?.filter { it.nameWithoutExtension == name } + ?.forEach { it.delete() } + } + + fun loadTypeface(path: String): Typeface? = try { + Typeface.createFromFile(path) + } catch (_: Exception) { + null + } +} diff --git a/app/src/main/java/io/nekohasekai/sfa/terminal/TailscaleSSHPresentedSession.kt b/app/src/main/java/io/nekohasekai/sfa/terminal/TailscaleSSHPresentedSession.kt new file mode 100644 index 0000000..3dc260a --- /dev/null +++ b/app/src/main/java/io/nekohasekai/sfa/terminal/TailscaleSSHPresentedSession.kt @@ -0,0 +1,9 @@ +package io.nekohasekai.sfa.terminal + +data class TailscaleSSHPresentedSession( + val endpointTag: String, + val peerHostName: String, + val peerAddress: String, + val username: String, + val hostKeys: List, +) diff --git a/app/src/main/java/io/nekohasekai/sfa/terminal/TailscaleSSHTerminalSession.java b/app/src/main/java/io/nekohasekai/sfa/terminal/TailscaleSSHTerminalSession.java new file mode 100644 index 0000000..ea10522 --- /dev/null +++ b/app/src/main/java/io/nekohasekai/sfa/terminal/TailscaleSSHTerminalSession.java @@ -0,0 +1,217 @@ +package io.nekohasekai.sfa.terminal; + +import android.os.Handler; +import android.os.Looper; +import com.termux.terminal.TerminalEmulator; +import com.termux.terminal.TerminalSession; +import com.termux.terminal.TerminalSessionClient; +import io.nekohasekai.libbox.TailscaleSSHSession; +import java.util.Arrays; + +public class TailscaleSSHTerminalSession extends TerminalSession { + + public enum Phase { + CONNECTING, + RUNNING, + FINISHED + } + + public interface PhaseCallback { + void onPhaseChanged(Phase phase); + + void onAuthBanner(String message); + } + + private final Handler mainHandler = new Handler(Looper.getMainLooper()); + private TailscaleSSHSession sshSession; + private PhaseCallback phaseCallback; + private volatile Phase phase = Phase.CONNECTING; + private int exitCode; + private String exitSignal; + private String exitErrorMessage; + private int lastColumns; + private int lastRows; + private int lastCellWidthPixels; + private int lastCellHeightPixels; + + public TailscaleSSHTerminalSession(TerminalSessionClient client) { + super("", "", new String[0], new String[0], null, client); + } + + public void setPhaseCallback(PhaseCallback callback) { + this.phaseCallback = callback; + } + + public void setSshSession(TailscaleSSHSession session) { + this.sshSession = session; + if (mEmulator != null && lastColumns > 0 && lastRows > 0) { + try { + session.sendResize(lastColumns, lastRows, lastCellWidthPixels, lastCellHeightPixels); + } catch (Exception ignored) { + } + } + } + + public Phase getPhase() { + return phase; + } + + public int getSSHExitCode() { + return exitCode; + } + + public String getSSHExitSignal() { + return exitSignal; + } + + public String getSSHExitErrorMessage() { + return exitErrorMessage; + } + + @Override + public void initializeEmulator(int columns, int rows, int cellWidthPixels, int cellHeightPixels) { + mEmulator = + new TerminalEmulator(this, columns, rows, cellWidthPixels, cellHeightPixels, null, mClient); + } + + @Override + public void updateSize(int columns, int rows, int cellWidthPixels, int cellHeightPixels) { + lastColumns = columns; + lastRows = rows; + lastCellWidthPixels = cellWidthPixels; + lastCellHeightPixels = cellHeightPixels; + if (mEmulator == null) { + initializeEmulator(columns, rows, cellWidthPixels, cellHeightPixels); + } else { + mEmulator.resize(columns, rows, cellWidthPixels, cellHeightPixels); + } + if (sshSession != null) { + try { + sshSession.sendResize(columns, rows, cellWidthPixels, cellHeightPixels); + } catch (Exception ignored) { + } + } + } + + @Override + public void write(byte[] data, int offset, int count) { + if (sshSession == null) return; + byte[] sanitized = sanitizeInput(data, offset, count); + try { + sshSession.sendInput(sanitized); + } catch (Exception ignored) { + } + } + + @Override + public synchronized boolean isRunning() { + return phase == Phase.RUNNING || phase == Phase.CONNECTING; + } + + @Override + public void finishIfRunning() { + close(); + } + + public void feedOutput(byte[] data) { + mainHandler.post( + () -> { + if (mEmulator != null) { + mEmulator.append(data, data.length); + notifyScreenUpdate(); + } + }); + } + + public void onReady() { + phase = Phase.RUNNING; + mainHandler.post( + () -> { + if (phaseCallback != null) { + phaseCallback.onPhaseChanged(Phase.RUNNING); + } + }); + } + + public void onAuthBanner(String message) { + mainHandler.post( + () -> { + if (phaseCallback != null) { + phaseCallback.onAuthBanner(message); + } + }); + } + + public void onExit(int exitCode, String signal, String errorMessage) { + this.exitCode = exitCode; + this.exitSignal = signal; + this.exitErrorMessage = errorMessage; + phase = Phase.FINISHED; + mainHandler.post( + () -> { + if (mEmulator != null) { + StringBuilder exitText = new StringBuilder("\r\n[Session ended"); + if (errorMessage != null && !errorMessage.isEmpty()) { + exitText.append(": ").append(errorMessage); + } else if (exitCode != 0) { + exitText.append(" (exit ").append(exitCode).append(")"); + } + if (signal != null && !signal.isEmpty()) { + exitText.append(" (signal ").append(signal).append(")"); + } + exitText.append(" - press any key]"); + byte[] bytes = exitText.toString().getBytes(); + mEmulator.append(bytes, bytes.length); + notifyScreenUpdate(); + } + if (phaseCallback != null) { + phaseCallback.onPhaseChanged(Phase.FINISHED); + } + mClient.onSessionFinished(TailscaleSSHTerminalSession.this); + }); + } + + public void onError(String message) { + onExit(-1, null, message); + } + + public void close() { + if (sshSession != null) { + try { + sshSession.close(); + } catch (Exception ignored) { + } + sshSession = null; + } + } + + private static byte[] sanitizeInput(byte[] data, int offset, int count) { + byte[] result = new byte[count]; + int writePos = 0; + for (int i = offset; i < offset + count; i++) { + byte b = data[i]; + if (b == 0x1b && i + 4 < offset + count) { + // Strip bracketed paste markers: ESC[200~ and ESC[201~ + if (data[i + 1] == '[' + && data[i + 2] == '2' + && data[i + 3] == '0' + && (data[i + 4] == '0' || data[i + 4] == '1') + && i + 5 < offset + count + && data[i + 5] == '~') { + i += 5; + continue; + } + } + // Convert LF to CR + if (b == 0x0A) { + result[writePos++] = 0x0D; + } else { + result[writePos++] = b; + } + } + if (writePos == count) { + return result; + } + return Arrays.copyOf(result, writePos); + } +} diff --git a/app/src/main/java/io/nekohasekai/sfa/terminal/TerminalColorSchemeLoader.kt b/app/src/main/java/io/nekohasekai/sfa/terminal/TerminalColorSchemeLoader.kt new file mode 100644 index 0000000..e8ede53 --- /dev/null +++ b/app/src/main/java/io/nekohasekai/sfa/terminal/TerminalColorSchemeLoader.kt @@ -0,0 +1,55 @@ +package io.nekohasekai.sfa.terminal + +import android.content.Context +import android.graphics.Color +import com.termux.terminal.TerminalColorScheme +import com.termux.terminal.TerminalColors +import java.util.Properties + +object TerminalColorSchemeLoader { + + fun listSchemes(context: Context): List { + val files = context.assets.list("termux-colors") ?: return emptyList() + return files + .filter { it.endsWith(".properties") } + .map { it.removeSuffix(".properties") } + .sorted() + } + + fun listSchemes(context: Context, isDark: Boolean): List { + return listSchemes(context).filter { name -> + val props = loadScheme(context, name) ?: return@filter false + val background = props.getProperty("background") ?: return@filter isDark + val luminance = backgroundLuminance(background) + if (isDark) luminance <= 128 else luminance > 128 + } + } + + fun loadScheme(context: Context, name: String): Properties? = try { + val props = Properties() + context.assets.open("termux-colors/$name.properties").use { props.load(it) } + props + } catch (_: Exception) { + null + } + + fun applyScheme(context: Context, name: String) { + val props = loadScheme(context, name) ?: return + TerminalColors.COLOR_SCHEME.updateWith(props) + } + + fun applySchemeToEmulator(emulator: com.termux.terminal.TerminalEmulator, context: Context, name: String) { + applyScheme(context, name) + emulator.mColors.reset() + } + + private fun backgroundLuminance(hex: String): Int = try { + val color = Color.parseColor(hex) + val r = Color.red(color) + val g = Color.green(color) + val b = Color.blue(color) + (0.299 * r + 0.587 * g + 0.114 * b).toInt() + } catch (_: Exception) { + 0 + } +} diff --git a/app/src/main/java/io/nekohasekai/sfa/terminal/TerminalExtraKeysState.kt b/app/src/main/java/io/nekohasekai/sfa/terminal/TerminalExtraKeysState.kt new file mode 100644 index 0000000..daa5271 --- /dev/null +++ b/app/src/main/java/io/nekohasekai/sfa/terminal/TerminalExtraKeysState.kt @@ -0,0 +1,64 @@ +package io.nekohasekai.sfa.terminal + +import com.termux.terminal.KeyHandler +import kotlinx.coroutines.flow.MutableStateFlow + +class TerminalExtraKeysState { + + enum class StickyModifierState { INACTIVE, ARMED, LOCKED } + + val ctrlState = MutableStateFlow(StickyModifierState.INACTIVE) + val altState = MutableStateFlow(StickyModifierState.INACTIVE) + + val isCtrlActive: Boolean get() = ctrlState.value != StickyModifierState.INACTIVE + val isAltActive: Boolean get() = altState.value != StickyModifierState.INACTIVE + + private var lastCtrlTapTime = 0L + private var lastAltTapTime = 0L + + fun toggleCtrl(currentTimeMs: Long) { + ctrlState.value = toggleModifier(ctrlState.value, currentTimeMs, lastCtrlTapTime) + lastCtrlTapTime = currentTimeMs + } + + fun toggleAlt(currentTimeMs: Long) { + altState.value = toggleModifier(altState.value, currentTimeMs, lastAltTapTime) + lastAltTapTime = currentTimeMs + } + + fun consumeModifiers() { + if (ctrlState.value == StickyModifierState.ARMED) { + ctrlState.value = StickyModifierState.INACTIVE + } + if (altState.value == StickyModifierState.ARMED) { + altState.value = StickyModifierState.INACTIVE + } + } + + fun currentKeyMod(): Int { + var mod = 0 + if (isCtrlActive) mod = mod or KeyHandler.KEYMOD_CTRL + if (isAltActive) mod = mod or KeyHandler.KEYMOD_ALT + return mod + } + + private fun toggleModifier( + current: StickyModifierState, + currentTimeMs: Long, + lastTapTime: Long, + ): StickyModifierState = when (current) { + StickyModifierState.INACTIVE -> StickyModifierState.ARMED + StickyModifierState.ARMED -> { + if (currentTimeMs - lastTapTime < DOUBLE_TAP_THRESHOLD_MS) { + StickyModifierState.LOCKED + } else { + StickyModifierState.INACTIVE + } + } + StickyModifierState.LOCKED -> StickyModifierState.INACTIVE + } + + companion object { + private const val DOUBLE_TAP_THRESHOLD_MS = 300L + } +} diff --git a/app/src/main/java/io/nekohasekai/sfa/terminal/TerminalSessionManager.kt b/app/src/main/java/io/nekohasekai/sfa/terminal/TerminalSessionManager.kt new file mode 100644 index 0000000..47ab77e --- /dev/null +++ b/app/src/main/java/io/nekohasekai/sfa/terminal/TerminalSessionManager.kt @@ -0,0 +1,9 @@ +package io.nekohasekai.sfa.terminal + +import java.util.UUID + +data class ManagedSession( + val id: String = UUID.randomUUID().toString(), + val terminalSession: TailscaleSSHTerminalSession, + val presentedSession: TailscaleSSHPresentedSession, +) diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 15ae744..2049e4a 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -479,6 +479,33 @@ در دسترس بله + + اتصال از طریق SSH + گزینه‌های SSH + نام کاربری + به خاطر سپردن گزینه‌های SSH + اتصال سریع + در صورت فعال بودن، می‌توانید از طریق منوی زمینه (فشار طولانی) روی ورودی Tailscale در ابزارها و ورودی‌های همتا در لیست همتاها به سرعت به این همتا متصل شوید.\n\nاین همتا همچنین در منوی جلسه جدید هنگام اتصال به همتاهای دیگر از طریق SSH ظاهر خواهد شد. + می‌توانید ظاهر ترمینال را در تنظیمات ← پیکربندی Termux سفارشی کنید. + اتصال + در حال اتصال… + جلسه فعالی وجود ندارد + جلسه جدید + + + پیکربندی Termux + تم رنگ + تم روشن + تم تیره + فونت + خانواده فونت + اندازه فونت + پیش‌فرض + فونت‌های سیستم + فونت‌های وارد شده + وارد کردن از فایل + جستجوی تم‌ها + تست STUN سرور شروع تست diff --git a/app/src/main/res/values-ru-rRU/strings.xml b/app/src/main/res/values-ru-rRU/strings.xml index 21cdc47..8b45871 100644 --- a/app/src/main/res/values-ru-rRU/strings.xml +++ b/app/src/main/res/values-ru-rRU/strings.xml @@ -485,6 +485,33 @@ Доступно Да + + Подключение по SSH + Параметры SSH + Имя пользователя + Запомнить параметры SSH + Быстрое подключение + Если включено, вы можете быстро подключиться к этому узлу через контекстное меню (долгое нажатие) записи Tailscale в инструментах и записей узлов в списке узлов.\n\nЭтот узел также появится в меню «Новая сессия» при подключении к другим узлам по SSH. + Вы можете настроить внешний вид терминала в разделе Параметры → Конфигурация Termux. + Подключение + Подключение… + Нет активных сессий + Новая сессия + + + Конфигурация Termux + Цветовая тема + Светлая тема + Тёмная тема + Шрифт + Семейство шрифтов + Размер шрифта + По умолчанию + Системные шрифты + Импортированные шрифты + Импорт из файла + Поиск тем + STUN-тест Сервер Начать тест diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 3332a1b..99da2f9 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -476,6 +476,33 @@ 可用 + + 通过 SSH 连接 + SSH 选项 + 用户名 + 记住 SSH 选项 + 快速连接 + 启用后,你可以通过工具中 Tailscale 条目和节点列表中节点条目的上下文菜单(长按)快速连接到此节点。\n\n当通过 SSH 连接到其他节点时,此节点也会出现在新建会话菜单中。 + 你可以在设置 → Termux 配置中自定义终端外观。 + 连接 + 连接中… + 无活动会话 + 新建会话 + + + Termux 配置 + 颜色主题 + 浅色主题 + 深色主题 + 字体 + 字体 + 字号 + 默认 + 系统字体 + 已导入的字体 + 从文件导入 + 搜索主题 + STUN 测试 服务器 开始测试 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index f7666e9..65fe1f8 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -479,6 +479,33 @@ 可用 + + 透過 SSH 連接 + SSH 選項 + 使用者名稱 + 記住 SSH 選項 + 快速連接 + 啟用後,你可以透過工具中 Tailscale 條目和節點列表中節點條目的上下文選單(長按)快速連接到此節點。\n\n當透過 SSH 連接到其他節點時,此節點也會出現在新建工作階段選單中。 + 你可以在設定 → Termux 配置中自定義終端外觀。 + 連接 + 連接中… + 無活動工作階段 + 新建工作階段 + + + Termux 配置 + 顏色主題 + 淺色主題 + 深色主題 + 字體 + 字型 + 字體大小 + 預設 + 系統字型 + 已匯入的字型 + 從檔案匯入 + 搜索主題 + STUN 測試 伺服器 開始測試 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1ab3008..d96338b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -490,6 +490,33 @@ Available Yes + + Connect via SSH + SSH Options + Username + Remember SSH Options + Quick Connect + If enabled, you can quickly connect to this peer via the context menu (long press) on the Tailscale entry in Tools and on peer entries in the peer list.\n\nThis peer will also appear in the New Session menu when connected to other peers via SSH. + You can customize the terminal appearance in Settings → Termux Configuration. + Connect + Connecting… + No active sessions + New session + + + Termux Configuration + Color Theme + Light Theme + Dark Theme + Font + Font Family + Font Size + Default + System Fonts + Imported Fonts + Import from File + Search themes + STUN Test Server diff --git a/gradle.properties b/gradle.properties index b36c765..4fe1f22 100644 --- a/gradle.properties +++ b/gradle.properties @@ -24,3 +24,9 @@ android.nonTransitiveRClass=true # Keep explicit Kotlin Gradle plugins enabled for current build scripts/plugins. android.newDsl=false android.builtInKotlin=false + +# Properties used by third_party/termux-app modules +compileSdkVersion=36 +minSdkVersion=21 +targetSdkVersion=35 +ndkVersion=28.0.13004108 diff --git a/settings.gradle.kts b/settings.gradle.kts index 4044430..5841932 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -18,3 +18,7 @@ rootProject.name = "sing-box" include(":app") include(":libxposed-api") project(":libxposed-api").projectDir = file("third_party/libxposed-api") +include(":terminal-emulator") +project(":terminal-emulator").projectDir = file("third_party/termux-app/terminal-emulator") +include(":terminal-view") +project(":terminal-view").projectDir = file("third_party/termux-app/terminal-view") diff --git a/third_party/termux-app b/third_party/termux-app new file mode 160000 index 0000000..a2a0ff6 --- /dev/null +++ b/third_party/termux-app @@ -0,0 +1 @@ +Subproject commit a2a0ff696dd04eef41c479d544df7dee31a53511