Compare commits

...

26 Commits

Author SHA1 Message Date
dependabot[bot]
a5c524f761 chore(deps): bump com.github.mwiede:jsch from 2.27.3 to 2.27.7
Bumps [com.github.mwiede:jsch](https://github.com/mwiede/jsch) from 2.27.3 to 2.27.7.
- [Release notes](https://github.com/mwiede/jsch/releases)
- [Changelog](https://github.com/mwiede/jsch/blob/master/ChangeLog)
- [Commits](https://github.com/mwiede/jsch/compare/jsch-2.27.3...jsch-2.27.7)

---
updated-dependencies:
- dependency-name: com.github.mwiede:jsch
  dependency-version: 2.27.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-28 02:17:27 +00:00
dependabot[bot]
5b165ed587 chore(deps): bump com.maxmind.geoip2:geoip2 from 4.4.0 to 5.0.0
Bumps [com.maxmind.geoip2:geoip2](https://github.com/maxmind/GeoIP2-java) from 4.4.0 to 5.0.0.
- [Release notes](https://github.com/maxmind/GeoIP2-java/releases)
- [Changelog](https://github.com/maxmind/GeoIP2-java/blob/main/CHANGELOG.md)
- [Commits](https://github.com/maxmind/GeoIP2-java/compare/v4.4.0...v5.0.0)

---
updated-dependencies:
- dependency-name: com.maxmind.geoip2:geoip2
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-24 18:16:48 +08:00
dependabot[bot]
d73b3b706e chore(deps): bump com.qcloud:cos_api from 5.6.257 to 5.6.259
Bumps [com.qcloud:cos_api](https://github.com/tencentyun/cos-java-sdk-v5) from 5.6.257 to 5.6.259.
- [Release notes](https://github.com/tencentyun/cos-java-sdk-v5/releases)
- [Changelog](https://github.com/tencentyun/cos-java-sdk-v5/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tencentyun/cos-java-sdk-v5/commits)

---
updated-dependencies:
- dependency-name: com.qcloud:cos_api
  dependency-version: 5.6.259
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-22 07:04:52 +08:00
dependabot[bot]
2928b35585 chore(deps): bump org.apache.commons:commons-lang3 from 3.19.0 to 3.20.0
Bumps org.apache.commons:commons-lang3 from 3.19.0 to 3.20.0.

---
updated-dependencies:
- dependency-name: org.apache.commons:commons-lang3
  dependency-version: 3.20.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-19 09:04:14 +08:00
hstyi
04bece21ff feat: insert new terminal tab at the correct index in terminal tab manager 2025-11-17 08:12:55 +08:00
hstyi
9e2e104baa chore: enhance terminal tab closing behavior to support reconnect option 2025-11-13 09:32:36 +08:00
hstyi
0615378a17 fix: selected terminal tab when transferring to a host 2025-11-11 10:52:28 +08:00
dependabot[bot]
013b03f9ef chore(deps): bump commons-codec:commons-codec from 1.19.0 to 1.20.0
Bumps [commons-codec:commons-codec](https://github.com/apache/commons-codec) from 1.19.0 to 1.20.0.
- [Changelog](https://github.com/apache/commons-codec/blob/master/RELEASE-NOTES.txt)
- [Commits](https://github.com/apache/commons-codec/compare/rel/commons-codec-1.19.0...rel/commons-codec-1.20.0)

---
updated-dependencies:
- dependency-name: commons-codec:commons-codec
  dependency-version: 1.20.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-10 10:17:06 +08:00
hstyi
026b13ba05 feat: add support for UI scaling via TERMORA_SCALE environment variable 2025-11-07 09:59:37 +08:00
dependabot[bot]
6ec526eeeb chore(deps): bump com.fazecast:jSerialComm from 2.11.2 to 2.11.4
Bumps [com.fazecast:jSerialComm](https://github.com/Fazecast/jSerialComm) from 2.11.2 to 2.11.4.
- [Release notes](https://github.com/Fazecast/jSerialComm/releases)
- [Commits](https://github.com/Fazecast/jSerialComm/commits)

---
updated-dependencies:
- dependency-name: com.fazecast:jSerialComm
  dependency-version: 2.11.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-04 11:00:57 +08:00
dependabot[bot]
e064bb9bb5 chore(deps): bump com.github.hstyi:geolite2
Bumps [com.github.hstyi:geolite2](https://github.com/hstyi/GeoLite2) from v1.0-202510200054 to v1.0-202510270056.
- [Release notes](https://github.com/hstyi/GeoLite2/releases)
- [Commits](https://github.com/hstyi/GeoLite2/commits)

---
updated-dependencies:
- dependency-name: com.github.hstyi:geolite2
  dependency-version: v1.0-202510270056
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-03 15:58:19 +08:00
dependabot[bot]
1f3fb5e2c0 chore(deps): bump okhttp from 5.2.1 to 5.3.0
Bumps `okhttp` from 5.2.1 to 5.3.0.

Updates `com.squareup.okhttp3:okhttp` from 5.2.1 to 5.3.0
- [Changelog](https://github.com/square/okhttp/blob/master/CHANGELOG.md)
- [Commits](https://github.com/square/okhttp/compare/parent-5.2.1...parent-5.3.0)

Updates `com.squareup.okhttp3:logging-interceptor` from 5.2.1 to 5.3.0
- [Changelog](https://github.com/square/okhttp/blob/master/CHANGELOG.md)
- [Commits](https://github.com/square/okhttp/compare/parent-5.2.1...parent-5.3.0)

---
updated-dependencies:
- dependency-name: com.squareup.okhttp3:okhttp
  dependency-version: 5.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: com.squareup.okhttp3:logging-interceptor
  dependency-version: 5.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-03 10:37:23 +08:00
hstyi
5984f3e856 chore: update JBR version to 21.0.8-b1163.69 2025-11-03 10:24:58 +08:00
hstyi
572c381e90 release: 2.0.0-beta.15 2025-11-03 09:23:44 +08:00
hstyi
7a8ecb06bf feat: add help message for removing shortcuts in keymap settings 2025-10-31 09:37:25 +08:00
hstyi
4c928ac826 feat: add domain field to SMB host options 2025-10-30 15:15:41 +08:00
dependabot[bot]
d07f9ede8c chore(deps): bump org.testcontainers:testcontainers-bom
Bumps [org.testcontainers:testcontainers-bom](https://github.com/testcontainers/testcontainers-java) from 2.0.0 to 2.0.1.
- [Release notes](https://github.com/testcontainers/testcontainers-java/releases)
- [Changelog](https://github.com/testcontainers/testcontainers-java/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testcontainers/testcontainers-java/compare/2.0.0...2.0.1)

---
updated-dependencies:
- dependency-name: org.testcontainers:testcontainers-bom
  dependency-version: 2.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-30 09:35:24 +08:00
dependabot[bot]
21a015bf8c chore(deps): bump org.javadelight:delight-rhino-sandbox
Bumps [org.javadelight:delight-rhino-sandbox](https://github.com/javadelight/delight-rhino-sandbox) from 0.0.17 to 0.2.1.
- [Release notes](https://github.com/javadelight/delight-rhino-sandbox/releases)
- [Commits](https://github.com/javadelight/delight-rhino-sandbox/compare/v0.0.17...v0.2.1)

---
updated-dependencies:
- dependency-name: org.javadelight:delight-rhino-sandbox
  dependency-version: 0.2.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-30 09:33:18 +08:00
hstyi
71a1f5db4b fix: replace WindowScope with Window in context menu extensions 2025-10-29 11:25:34 +08:00
hstyi
96fd07a6ff chore: update Alpine repository mirror and adjust SSH configuration 2025-10-28 10:18:23 +08:00
hstyi
733e062a7b chore(deps): correct module reference for testcontainers-junit-jupiter 2025-10-27 17:41:29 +08:00
hstyi
e87a779adc chore: refactor Mixpanel integration and add event tracking 2025-10-27 17:41:29 +08:00
dependabot[bot]
9c6aa4dcb6 chore(deps): bump com.mixpanel:mixpanel-java from 1.5.3 to 1.5.4
Bumps [com.mixpanel:mixpanel-java](https://github.com/mixpanel/mixpanel-java) from 1.5.3 to 1.5.4.
- [Release notes](https://github.com/mixpanel/mixpanel-java/releases)
- [Commits](https://github.com/mixpanel/mixpanel-java/compare/mixpanel-java-1.5.3...v1.5.4)

---
updated-dependencies:
- dependency-name: com.mixpanel:mixpanel-java
  dependency-version: 1.5.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-27 16:06:57 +08:00
dependabot[bot]
566b087eb1 chore(deps): bump com.github.oshi:oshi-core from 6.9.0 to 6.9.1
Bumps [com.github.oshi:oshi-core](https://github.com/oshi/oshi) from 6.9.0 to 6.9.1.
- [Release notes](https://github.com/oshi/oshi/releases)
- [Changelog](https://github.com/oshi/oshi/blob/master/CHANGELOG.md)
- [Commits](https://github.com/oshi/oshi/compare/oshi-parent-6.9.0...oshi-parent-6.9.1)

---
updated-dependencies:
- dependency-name: com.github.oshi:oshi-core
  dependency-version: 6.9.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-26 21:08:45 +08:00
dependabot[bot]
2235e4c2a4 chore(deps): bump org.commonmark:commonmark from 0.26.0 to 0.27.0
Bumps [org.commonmark:commonmark](https://github.com/commonmark/commonmark-java) from 0.26.0 to 0.27.0.
- [Release notes](https://github.com/commonmark/commonmark-java/releases)
- [Changelog](https://github.com/commonmark/commonmark-java/blob/main/CHANGELOG.md)
- [Commits](https://github.com/commonmark/commonmark-java/compare/commonmark-parent-0.26.0...commonmark-parent-0.27.0)

---
updated-dependencies:
- dependency-name: org.commonmark:commonmark
  dependency-version: 0.27.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-24 17:27:28 +08:00
dependabot[bot]
3b9d1f277b chore(deps): bump kotlin from 2.2.20 to 2.2.21
Bumps `kotlin` from 2.2.20 to 2.2.21.

Updates `org.jetbrains.kotlin.jvm` from 2.2.20 to 2.2.21
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/v2.2.21/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v2.2.20...v2.2.21)

Updates `org.jetbrains.kotlin.plugin.serialization` from 2.2.20 to 2.2.21
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/v2.2.21/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v2.2.20...v2.2.21)

---
updated-dependencies:
- dependency-name: org.jetbrains.kotlin.jvm
  dependency-version: 2.2.21
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.jetbrains.kotlin.plugin.serialization
  dependency-version: 2.2.21
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-24 11:03:42 +08:00
36 changed files with 208 additions and 118 deletions

View File

@@ -4,7 +4,7 @@ on: [ push, pull_request ]
env:
JBR_MAJOR: 21.0.8
JBR_PATCH: b1138.52
JBR_PATCH: b1163.69
jobs:
build:

View File

@@ -9,14 +9,14 @@ env:
TERMORA_MAC_NOTARY: "${{ startsWith(github.event.head_commit.message, 'release: ') && github.repository == 'TermoraDev/termora' }}"
TERMORA_MAC_NOTARY_KEYCHAIN_PROFILE: ${{ secrets.TERMORA_MAC_NOTARY_KEYCHAIN_PROFILE }}
JBR_MAJOR: 21.0.8
JBR_PATCH: b1138.52
JBR_PATCH: b1163.69
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ macos-15, macos-13 ]
os: [ macos-15-intel, macos-latest ]
steps:
- uses: actions/checkout@v4
with:

View File

@@ -4,7 +4,7 @@ on: [ push, pull_request ]
env:
JBR_MAJOR: 21.0.8
JBR_PATCH: b1138.52
JBR_PATCH: b1163.69
jobs:
build:

View File

@@ -1 +1 @@
2.0.0-beta.14
2.0.0-beta.15

View File

@@ -1,13 +1,13 @@
[versions]
kotlin = "2.2.20"
kotlin = "2.2.21"
slf4j = "2.0.17"
pty4j = "0.13.10"
tinylog = "2.7.0"
kotlinx-coroutines = "1.10.2"
flatlaf = "3.6.2"
kotlinx-serialization-json = "1.9.0"
commons-codec = "1.19.0"
commons-lang3 = "3.19.0"
commons-codec = "1.20.0"
commons-lang3 = "3.20.0"
commons-csv = "1.14.1"
commons-net = "3.12.0"
commons-text = "1.14.0"
@@ -16,28 +16,28 @@ commons-vfs2 = "2.10.0"
swingx = "1.6.5-1"
jgoodies-forms = "1.9.0"
jfa = "1.2.0"
oshi = "6.9.0"
oshi = "6.9.1"
versioncompare = "1.4.1"
jna = "5.18.1"
jSystemThemeDetector = "3.9.1"
commons-io = "2.20.0"
jbr-api = "17.1.10.1"
hutool = "5.8.40"
jsch = "2.27.3"
okhttp = "5.2.1"
jsch = "2.27.7"
okhttp = "5.3.0"
sshj = "0.39.0"
sshd-core = "2.15.0"
jgit = "7.4.0.202509020913-r"
commonmark = "0.26.0"
commonmark = "0.27.0"
jnafilechooser = "1.1.2"
xodus = "2.0.1"
bip39 = "1.0.9"
colorpicker = "2.0.1"
rhino = "1.8.0"
delight-rhino-sandbox = "0.0.17"
testcontainers = "2.0.0"
mixpanel = "1.5.3"
jSerialComm = "2.11.2"
delight-rhino-sandbox = "0.2.1"
testcontainers = "2.0.1"
mixpanel = "1.5.4"
jSerialComm = "2.11.4"
ini4j = "0.5.5-2"
restart4j = "0.0.1"
eddsa = "0.3.0"
@@ -70,7 +70,7 @@ flatlafextras = { group = "com.formdev", name = "flatlaf-extras", version.ref =
flatlafswingx = { module = "com.formdev:flatlaf-swingx", version.ref = "flatlaf" }
testcontainers-bom = { module = "org.testcontainers:testcontainers-bom", version.ref = "testcontainers" }
testcontainers = { module = "org.testcontainers:testcontainers" }
testcontainers-junit-jupiter = { module = "org.testcontainers:junit-jupiter" }
testcontainers-junit-jupiter = { module = "org.testcontainers:testcontainers-junit-jupiter" }
swingx = { module = "org.swinglabs.swingx:swingx-all", version.ref = "swingx" }
jgoodies-forms = { module = "com.jgoodies:jgoodies-forms", version.ref = "jgoodies-forms" }
jna = { module = "net.java.dev.jna:jna", version.ref = "jna" }

View File

@@ -8,7 +8,7 @@ project.version = "0.0.4"
dependencies {
testImplementation(kotlin("test"))
implementation("com.qcloud:cos_api:5.6.257")
implementation("com.qcloud:cos_api:5.6.259")
compileOnly(project(":"))
}

View File

@@ -7,9 +7,9 @@ project.version = "0.0.8"
dependencies {
testImplementation(kotlin("test"))
compileOnly(project(":"))
implementation("com.maxmind.geoip2:geoip2:4.4.0")
implementation("com.maxmind.geoip2:geoip2:5.0.0")
// https://github.com/hstyi/geolite2
implementation("com.github.hstyi:geolite2:v1.0-202510200054")
implementation("com.github.hstyi:geolite2:v1.0-202510270056")
}
apply(from = "$rootDir/plugins/common.gradle.kts")

View File

@@ -10,7 +10,7 @@ project.version = "0.0.5"
dependencies {
testImplementation(kotlin("test"))
compileOnly(project(":"))
implementation("com.fazecast:jSerialComm:2.11.2")
implementation("com.fazecast:jSerialComm:2.11.4")
}
apply(from = "$rootDir/plugins/common.gradle.kts")

View File

@@ -2,7 +2,7 @@ plugins {
alias(libs.plugins.kotlin.jvm)
}
project.version = "0.0.3"
project.version = "0.0.4"
dependencies {
testImplementation(kotlin("test"))

View File

@@ -42,6 +42,7 @@ class SMBHostOptionsPane : OptionsPane() {
sftpDefaultDirectory = sftpOption.defaultDirectoryField.text,
extras = mutableMapOf(
"smb.share" to generalOption.shareTextField.text,
"smb.domain" to generalOption.domainTextField.text,
)
)
@@ -66,6 +67,7 @@ class SMBHostOptionsPane : OptionsPane() {
generalOption.remarkTextArea.text = host.remark
generalOption.passwordTextField.text = host.authentication.password
generalOption.shareTextField.text = host.options.extras["smb.share"] ?: StringUtils.EMPTY
generalOption.domainTextField.text = host.options.extras["smb.domain"] ?: StringUtils.EMPTY
sftpOption.defaultDirectoryField.text = host.options.sftpDefaultDirectory
}
@@ -114,6 +116,7 @@ class SMBHostOptionsPane : OptionsPane() {
val nameTextField = OutlineTextField(128)
val shareTextField = OutlineTextField(256)
val usernameTextField = OutlineComboBox<String>()
val domainTextField = OutlineTextField(128)
val hostTextField = OutlineTextField(255)
val passwordTextField = OutlinePasswordField(255)
val remarkTextArea = FixedLengthTextArea(512)
@@ -188,7 +191,9 @@ class SMBHostOptionsPane : OptionsPane() {
.add(portTextField).xy(7, rows).apply { rows += step }
.add("${I18n.getString("termora.new-host.general.username")}:").xy(1, rows)
.add(usernameTextField).xyw(3, rows, 5).apply { rows += step }
.add(usernameTextField).xy(3, rows)
.add("${SMBI18n.getString("termora.plugins.smb.domain")}:").xy(5, rows)
.add(domainTextField).xy(7, rows).apply { rows += step }
.add("${I18n.getString("termora.new-host.general.password")}:").xy(1, rows)
.add(passwordTextField).xyw(3, rows, 5).apply { rows += step }

View File

@@ -30,6 +30,7 @@ class SMBProtocolProvider private constructor() : TransferProtocolProvider {
val client = SMBClient()
val host = requester.host
val connection = client.connect(host.host, host.port)
val domain = host.options.extras["smb.domain"] ?: StringUtils.EMPTY
val session = when (host.username) {
"Guest" -> connection.authenticate(AuthenticationContext.guest())
"Anonymous" -> connection.authenticate(AuthenticationContext.anonymous())
@@ -37,7 +38,7 @@ class SMBProtocolProvider private constructor() : TransferProtocolProvider {
AuthenticationContext(
host.username,
host.authentication.password.toCharArray(),
null
domain.ifBlank { null }
)
)
}

View File

@@ -1 +1,2 @@
termora.plugins.smb.share=Share name
termora.plugins.smb.domain=Domain

View File

@@ -1 +1,3 @@
termora.plugins.smb.share=共享名称
termora.plugins.smb.domain=域名

View File

@@ -1 +1,3 @@
termora.plugins.smb.share=共享名稱
termora.plugins.smb.domain=網域

View File

@@ -50,6 +50,17 @@ class ApplicationInitializr {
}
}
// https://github.com/TermoraDev/termora/issues/1254
if (System.getProperty(FlatSystemProperties.UI_SCALE).isNullOrBlank()) {
val scale = System.getenv("TERMORA_SCALE")
if (scale.isNullOrBlank().not()) {
if (NumberUtils.toDouble(scale, -1.0) > 0) {
System.setProperty(FlatSystemProperties.UI_SCALE_ENABLED, "true")
System.setProperty(FlatSystemProperties.UI_SCALE, scale)
}
}
}
// 启动
val runtime = measureTimeMillis { ApplicationRunner().run() }
val log = LoggerFactory.getLogger(javaClass)

View File

@@ -10,15 +10,11 @@ import com.formdev.flatlaf.extras.FlatInspector
import com.formdev.flatlaf.ui.FlatTableCellBorder
import com.formdev.flatlaf.util.SystemInfo
import com.jthemedetecor.OsThemeDetector
import com.mixpanel.mixpanelapi.ClientDelivery
import com.mixpanel.mixpanelapi.MessageBuilder
import com.mixpanel.mixpanelapi.MixpanelAPI
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.apache.commons.io.FileUtils
import org.apache.commons.lang3.LocaleUtils
import org.apache.commons.lang3.SystemUtils
import org.json.JSONObject
import org.slf4j.LoggerFactory
import java.awt.*
import java.awt.desktop.AppReopenedEvent
@@ -369,61 +365,8 @@ class ApplicationRunner {
if (Application.isUnknownVersion()) {
return
}
swingCoroutineScope.launch(Dispatchers.IO) {
try {
val properties = JSONObject()
properties.put("os", SystemUtils.OS_NAME)
if (SystemInfo.isLinux) {
properties.put("platform", "Linux")
} else if (SystemInfo.isWindows) {
properties.put("platform", "Windows")
} else if (SystemInfo.isMacOS) {
properties.put("platform", "macOS")
}
properties.put("version", Application.getVersion())
properties.put("language", Locale.getDefault().toString())
val message = MessageBuilder("0871335f59ee6d0eb246b008a20f9d1c")
.event(getAnalyticsUserID(), "launch", properties)
val delivery = ClientDelivery()
delivery.addMessage(message)
val endpoints = listOf(
"https://api-eu.mixpanel.com",
"https://api-in.mixpanel.com",
"https://api.mixpanel.com",
"http://api.mixpanel.com",
)
for (endpoint in endpoints) {
try {
MixpanelAPI(
"$endpoint/track",
"$endpoint/engage",
"$endpoint/groups"
).deliver(delivery, true)
break
} catch (e: Exception) {
if (log.isErrorEnabled) {
log.error(e.message, e)
}
continue
}
}
} catch (e: Exception) {
if (log.isErrorEnabled) {
log.error(e.message, e)
}
}
}
MixpanelService.getInstance().push("launch")
}
private fun getAnalyticsUserID(): String {
val properties = DatabaseManager.getInstance().properties
var id = properties.getString("AnalyticsUserID")
if (id.isNullOrBlank()) {
id = randomUUID()
properties.putString("AnalyticsUserID", id)
}
return id
}
}

View File

@@ -0,0 +1,85 @@
package app.termora
import app.termora.database.DatabaseManager
import com.formdev.flatlaf.util.SystemInfo
import com.mixpanel.mixpanelapi.ClientDelivery
import com.mixpanel.mixpanelapi.MessageBuilder
import com.mixpanel.mixpanelapi.MixpanelAPI
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.apache.commons.lang3.SystemUtils
import org.json.JSONObject
import org.slf4j.LoggerFactory
import java.util.*
internal class MixpanelService private constructor() {
companion object {
private val log = LoggerFactory.getLogger(MixpanelService::class.java)
fun getInstance(): MixpanelService {
return ApplicationScope.forApplicationScope().getOrCreate(MixpanelService::class) { MixpanelService() }
}
}
fun push(event: String, extras: Map<String, String> = emptyMap()) {
swingCoroutineScope.launch(Dispatchers.IO) {
try {
val properties = JSONObject()
for (entry in extras) {
properties.put(entry.key, entry.value)
}
properties.put("os", SystemUtils.OS_NAME)
if (SystemInfo.isLinux) {
properties.put("platform", "Linux")
} else if (SystemInfo.isWindows) {
properties.put("platform", "Windows")
} else if (SystemInfo.isMacOS) {
properties.put("platform", "macOS")
}
properties.put("version", Application.getVersion())
properties.put("language", Locale.getDefault().toString())
val message = MessageBuilder("0871335f59ee6d0eb246b008a20f9d1c")
.event(getAnalyticsUserID(), event, properties)
val delivery = ClientDelivery()
delivery.addMessage(message)
val endpoints = listOf(
"https://api-eu.mixpanel.com",
"https://api-in.mixpanel.com",
"https://api.mixpanel.com",
"http://api.mixpanel.com",
)
for (endpoint in endpoints) {
try {
MixpanelAPI(
"$endpoint/track",
"$endpoint/engage",
"$endpoint/groups"
).deliver(delivery, true)
break
} catch (e: Exception) {
if (log.isErrorEnabled) {
log.error(e.message, e)
}
continue
}
}
} catch (e: Exception) {
if (log.isErrorEnabled) {
log.error(e.message, e)
}
}
}
}
private fun getAnalyticsUserID(): String {
val properties = DatabaseManager.getInstance().properties
var id = properties.getString("AnalyticsUserID")
if (id.isNullOrBlank()) {
id = randomUUID()
properties.putString("AnalyticsUserID", id)
}
return id
}
}

View File

@@ -3,5 +3,5 @@ package app.termora
import app.termora.actions.AnActionEvent
import java.util.*
class OpenHostActionEvent(source: Any, val host: Host, event: EventObject) :
class OpenHostActionEvent(source: Any, val host: Host, event: EventObject, val tabIndex: Int = -1) :
AnActionEvent(source, String(), event)

View File

@@ -179,7 +179,7 @@ abstract class PtyHostTerminalTab(
val tab = createReconnectTerminalTab()
manager.addTerminalTab(index, tab, true)
manager.closeTerminalTab(this, true)
manager.closeTerminalTab(this, disposable = true, reconnect = true)
if (tab is HostTerminalTab) {
tab.start()

View File

@@ -141,25 +141,28 @@ class TerminalTabbed(
}
private fun removeTabAt(index: Int, disposable: Boolean = true) {
private fun removeTabAt(index: Int, disposable: Boolean = true, reconnect: Boolean = false) {
if (tabbedPane.isTabClosable(index)) {
val tab = tabs[index]
// 询问是否可以关闭
if (disposable) {
// 如果开启了关闭确认,那么直接询问用户
if (appearance.confirmTabClose) {
if (OptionPane.showConfirmDialog(
windowScope.window,
I18n.getString("termora.tabbed.tab.close-prompt"),
messageType = JOptionPane.QUESTION_MESSAGE,
optionType = JOptionPane.OK_CANCEL_OPTION
) != JOptionPane.OK_OPTION
) {
// 如果是重连接,那么直接关闭不进行任何形式的询问
if (reconnect.not()) {
// 如果开启了关闭确认,那么直接询问用户
if (appearance.confirmTabClose) {
if (OptionPane.showConfirmDialog(
windowScope.window,
I18n.getString("termora.tabbed.tab.close-prompt"),
messageType = JOptionPane.QUESTION_MESSAGE,
optionType = JOptionPane.OK_CANCEL_OPTION
) != JOptionPane.OK_OPTION
) {
return
}
} else if (!tab.willBeClose()) { // 如果没有开启则询问用户
return
}
} else if (!tab.willBeClose()) { // 如果没有开启则询问用户
return
}
}
@@ -233,7 +236,7 @@ class TerminalTabbed(
if (tab is HostTerminalTab) {
actionManager
.getAction(OpenHostAction.OPEN_HOST)
.actionPerformed(OpenHostActionEvent(this, tab.host, evt))
.actionPerformed(OpenHostActionEvent(this, tab.host, evt, tabIndex + 1))
}
}
@@ -361,7 +364,7 @@ class TerminalTabbed(
}
}
override fun indexOfTerminalTab(tab: TerminalTab):Int {
override fun indexOfTerminalTab(tab: TerminalTab): Int {
return tabbedPane.indexOfComponent(tab.getJComponent())
}
@@ -451,10 +454,10 @@ class TerminalTabbed(
}
}
override fun closeTerminalTab(tab: TerminalTab, disposable: Boolean) {
override fun closeTerminalTab(tab: TerminalTab, disposable: Boolean, reconnect: Boolean) {
for (i in 0 until tabs.size) {
if (tabs[i] == tab) {
removeTabAt(i, disposable)
removeTabAt(i, disposable, reconnect)
break
}
}

View File

@@ -6,7 +6,7 @@ interface TerminalTabbedManager {
fun getSelectedTerminalTab(): TerminalTab?
fun getTerminalTabs(): List<TerminalTab>
fun setSelectedTerminalTab(tab: TerminalTab)
fun closeTerminalTab(tab: TerminalTab, disposable: Boolean = true)
fun closeTerminalTab(tab: TerminalTab, disposable: Boolean = true, reconnect: Boolean = false)
fun refreshTerminalTabs()
fun indexOfTerminalTab(tab: TerminalTab): Int
}

View File

@@ -56,7 +56,12 @@ class OpenHostAction : AnAction() {
if (tab == null) return
terminalTabbedManager.addTerminalTab(tab)
if (evt.tabIndex >= 0) {
terminalTabbedManager.addTerminalTab(evt.tabIndex, tab)
} else {
terminalTabbedManager.addTerminalTab(tab)
}
if (tab is PtyHostTerminalTab) {
tab.start()
}

View File

@@ -29,8 +29,10 @@ class KeymapPanel : JPanel(BorderLayout()) {
private val copyBtn = JButton(Icons.copy)
private val renameBtn = JButton(Icons.edit)
private val deleteBtn = JButton(Icons.delete)
private val infoBtn = JButton(Icons.questionMark)
private val database get() = DatabaseManager.getInstance()
private val allowKeyCodes = mutableSetOf<Int>()
private val owner get() = SwingUtilities.getWindowAncestor(this)
init {
initView()
@@ -89,8 +91,8 @@ class KeymapPanel : JPanel(BorderLayout()) {
box.add(copyBtn)
box.add(renameBtn)
box.add(deleteBtn)
box.add(infoBtn)
box.add(Box.createHorizontalGlue())
box.border = BorderFactory.createEmptyBorder(0, 0, 6, 0)
add(box, BorderLayout.NORTH)
add(scrollPane, BorderLayout.CENTER)
@@ -105,6 +107,12 @@ class KeymapPanel : JPanel(BorderLayout()) {
}
})
infoBtn.addActionListener {
val color = UIManager.getColor("TextField.placeholderForeground")
val msg = I18n.getString("termora.settings.keymap.question", color.red, color.green, color.blue)
OptionPane.showMessageDialog(owner, msg)
}
copyBtn.addActionListener {
val keymap = getCurrentKeymap()
if (keymap != null) {

View File

@@ -185,6 +185,13 @@ class PluginPanel(val descriptor: PluginPluginDescriptor) : JPanel(), Disposable
}
}
MixpanelService.getInstance().push(
"uninstall-plugin", mapOf(
"pluginName" to descriptor.plugin.getName(),
"pluginVersion" to descriptor.version.toString(),
)
)
// 询问是否重启
TermoraRestarter.getInstance().scheduleRestart(owner)
} else {
@@ -227,6 +234,13 @@ class PluginPanel(val descriptor: PluginPluginDescriptor) : JPanel(), Disposable
}
}, button == updateButton)
MixpanelService.getInstance().push(
"${if (button == installButton) "install" else "update"}-plugin", mapOf(
"pluginName" to descriptor.plugin.getName(),
"pluginVersion" to descriptor.version.toString(),
)
)
withContext(Dispatchers.Swing) {
installed.add(descriptor.id)

View File

@@ -27,9 +27,14 @@ class CloneSessionTerminalTabbedContextMenuExtension private constructor() : Ter
cloneSession.addActionListener(object : AnAction() {
override fun actionPerformed(evt: AnActionEvent) {
val terminalTabbedManager = evt.getData(DataProviders.TerminalTabbedManager) ?: return
val index = terminalTabbedManager.indexOfTerminalTab(tab)
val handler = c.copy(channel = null)
val newTab = SSHTerminalTab(windowScope, tab.host, handler)
terminalTabbedManager.addTerminalTab(newTab)
if (index >= 0) {
terminalTabbedManager.addTerminalTab(index + 1, newTab)
} else {
terminalTabbedManager.addTerminalTab(newTab)
}
newTab.start()
}
})

View File

@@ -46,6 +46,7 @@ class TransferAnAction : AnAction(I18n.getString("termora.transport.sftp"), Icon
val panel = tabbed.getTransportPanel(i) ?: continue
if (panel.host.id == host.id) {
tabbed.selectedIndex = i
terminalTabbedManager.setSelectedTerminalTab(sftpTab)
return
}
}

View File

@@ -1,7 +1,7 @@
package app.termora.transfer
import app.termora.WindowScope
import app.termora.plugin.Extension
import java.awt.Window
import java.nio.file.FileSystem
import java.nio.file.Path
import javax.swing.JMenuItem
@@ -14,7 +14,7 @@ internal interface TransportContextMenuExtension : Extension {
* @param fileSystem 为 null 表示可能已经断线,处于不可用状态
*/
fun createJMenuItem(
windowScope: WindowScope,
window: Window,
fileSystem: FileSystem?,
popupMenu: TransportPopupMenu,
files: List<Pair<Path, TransportTableModel.Attributes>>

View File

@@ -108,7 +108,7 @@ internal class TransportPopupMenu(
for (extension in extensionManager.getExtensions(TransportContextMenuExtension::class.java)) {
try {
val menu = extension.createJMenuItem(
ApplicationScope.forWindowScope(owner),
owner,
fileSystem,
this,
files

View File

@@ -1,7 +1,6 @@
package app.termora.transfer.internal.sftp
import app.termora.I18n
import app.termora.WindowScope
import app.termora.actions.AnAction
import app.termora.actions.AnActionEvent
import app.termora.randomUUID
@@ -9,6 +8,7 @@ import app.termora.transfer.*
import org.apache.commons.lang3.StringUtils
import org.apache.sshd.common.file.util.MockPath
import org.apache.sshd.sftp.client.fs.SftpFileSystem
import java.awt.Window
import java.nio.file.FileSystem
import java.nio.file.Path
import javax.swing.JMenu
@@ -23,7 +23,7 @@ internal class CompressTransportContextMenuExtension private constructor() : Tra
}
override fun createJMenuItem(
windowScope: WindowScope,
window: Window,
fileSystem: FileSystem?,
popupMenu: TransportPopupMenu,
files: List<Pair<Path, TransportTableModel.Attributes>>

View File

@@ -1,13 +1,13 @@
package app.termora.transfer.internal.sftp
import app.termora.I18n
import app.termora.WindowScope
import app.termora.actions.AnAction
import app.termora.actions.AnActionEvent
import app.termora.randomUUID
import app.termora.transfer.*
import org.apache.commons.lang3.StringUtils
import org.apache.sshd.sftp.client.fs.SftpFileSystem
import java.awt.Window
import java.nio.file.FileSystem
import java.nio.file.Path
import javax.swing.JMenu
@@ -21,7 +21,7 @@ internal class ExtractTransportContextMenuExtension private constructor() : Tran
}
override fun createJMenuItem(
windowScope: WindowScope,
window: Window,
fileSystem: FileSystem?,
popupMenu: TransportPopupMenu,
files: List<Pair<Path, TransportTableModel.Attributes>>

View File

@@ -3,11 +3,11 @@ package app.termora.transfer.internal.sftp
import app.termora.I18n
import app.termora.Icons
import app.termora.OptionPane
import app.termora.WindowScope
import app.termora.transfer.TransportContextMenuExtension
import app.termora.transfer.TransportPopupMenu
import app.termora.transfer.TransportTableModel
import org.apache.sshd.sftp.client.fs.SftpFileSystem
import java.awt.Window
import java.nio.file.FileSystem
import java.nio.file.Path
import javax.swing.JMenuItem
@@ -19,7 +19,7 @@ internal class RmrfTransportContextMenuExtension private constructor() : Transpo
}
override fun createJMenuItem(
windowScope: WindowScope,
window: Window,
fileSystem: FileSystem?,
popupMenu: TransportPopupMenu,
files: List<Pair<Path, TransportTableModel.Attributes>>
@@ -31,7 +31,7 @@ internal class RmrfTransportContextMenuExtension private constructor() : Transpo
val rmrfMenu = JMenuItem("rm -rf", Icons.warningIntroduction)
rmrfMenu.addActionListener {
if (OptionPane.showConfirmDialog(
windowScope.window,
window,
I18n.getString("termora.transport.table.contextmenu.rm-warning"),
messageType = JOptionPane.ERROR_MESSAGE
) == JOptionPane.YES_OPTION

View File

@@ -114,7 +114,7 @@ termora.settings.keymap=Keymap
termora.settings.keymap.shortcut=Shortcut
termora.settings.keymap.action=Action
termora.settings.keymap.already-exists=The shortcut [{0}] is already in use by [{1}]
termora.settings.keymap.question=Select a line, press the <font color=rgb({0},{1},{2})> ⌫ (Backspace)</font> key to remove the shortcut
termora.settings.sftp.edit-command=Edit Command
termora.settings.sftp.db-click-behavior=Double-click

View File

@@ -99,7 +99,7 @@ termora.settings.keymap=Горяиче клавиши
termora.settings.keymap.shortcut=Комбинация
termora.settings.keymap.action=Команда
termora.settings.keymap.already-exists=Комбинация [{0}] уже используется [{1}]
termora.settings.keymap.question=Выберите строку и нажмите <font color=rgb({0},{1},{2})> ⌫ (клавишу Backspace)</font>, чтобы удалить сочетание клавиш
termora.settings.sftp.edit-command=Редактировать команду
termora.settings.sftp.db-click-behavior=двойной щелчок

View File

@@ -125,7 +125,7 @@ termora.settings.keymap=键盘
termora.settings.keymap.shortcut=快捷键
termora.settings.keymap.action=操作
termora.settings.keymap.already-exists=快捷键 [{0}] 已经被 [{1}] 占用
termora.settings.keymap.question=选中一行,按下 <font color=rgb({0},{1},{2})> ⌫ (退格键)</font> 移除快捷键
termora.settings.sftp.edit-command=编辑命令
termora.settings.sftp.db-click-behavior=双击行为

View File

@@ -55,6 +55,7 @@ termora.settings.keymap=鍵盤
termora.settings.keymap.shortcut=快捷鍵
termora.settings.keymap.action=操作
termora.settings.keymap.already-exists=快捷鍵 [{0}] 已經被 [{1}] 占用
termora.settings.keymap.question=選取一行,按下 <font color=rgb({0},{1},{2})> ⌫ (退格鍵)</font> 移除快速鍵
termora.settings.sftp.edit-command=編輯命令
termora.settings.sftp.db-click-behavior=按兩下行為

View File

@@ -1,5 +1,5 @@
FROM linuxserver/openssh-server:9.3_p2-r1-ls147
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories \
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories \
&& apk update && apk add wget tmux gcc zip p7zip g++ git make zsh htop stress-ng inetutils-telnet xclock xcalc xorg-server xinit && wget https://ohse.de/uwe/releases/lrzsz-0.12.20.tar.gz \
&& tar -xf lrzsz-0.12.20.tar.gz && cd lrzsz-0.12.20 && ./configure && make && make install \
&& ln -s /usr/local/bin/lrz /usr/local/bin/rz && ln -s /usr/local/bin/lsz /usr/local/bin/sz
@@ -7,3 +7,6 @@ RUN sed -i 's/#AllowAgentForwarding yes/AllowAgentForwarding yes/g' /etc/ssh/ssh
RUN sed -i 's/AllowTcpForwarding no/AllowTcpForwarding yes/g' /etc/ssh/sshd_config
RUN sed -i 's/GatewayPorts no/GatewayPorts yes/g' /etc/ssh/sshd_config
RUN sed -i 's/X11Forwarding no/X11Forwarding yes/g' /etc/ssh/sshd_config
# docker build -t sshd .
# docker run --rm -it -e SUDO_ACCESS=true -e PASSWORD_ACCESS=true -e USER_PASSWORD=123456 -p 2222:2222 sshd