Skip to content

Commit 1f34e2b

Browse files
committed
Ver -> 5.5.15
1 parent a2702ed commit 1f34e2b

12 files changed

Lines changed: 160 additions & 65 deletions

File tree

‎build/replace/src/client/electerm-react/common/download.jsx‎

Lines changed: 0 additions & 43 deletions
This file was deleted.

‎package-lock.json‎

Lines changed: 10 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "electerm-android",
3-
"version": "5.5.10",
3+
"version": "5.5.15",
44
"description": "electerm for Android — a free and open-source ssh/sftp/telnet/RDP/VNC/Spice/ftp client, built on the electerm-web codebase with Capacitor.",
55
"main": "src/app/app.js",
66
"type": "module",
@@ -48,7 +48,7 @@
4848
"preferGlobal": true,
4949
"devDependencies": {
5050
"@ant-design/icons": "^6.2.5",
51-
"@electerm/electerm-react": "^5.5.9",
51+
"@electerm/electerm-react": "^5.5.15",
5252
"@electerm/electerm-resource": "2.2.1",
5353
"@fontsource/maple-mono": "^5.2.6",
5454
"@novnc/novnc": "^1.7.0",
@@ -92,7 +92,7 @@
9292
"@electerm/electerm-locales": "^2.3.19",
9393
"@electerm/electerm-themes": "^1.0.2",
9494
"@electerm/ftp-srv": "1.0.5",
95-
"@electerm/ssh2": "1.22.0",
95+
"@electerm/ssh2": "1.22.1",
9696
"@xterm/headless": "6.1.0-beta.292",
9797
"axios": "1.18.1",
9898
"basic-ftp": "6.0.1",
@@ -145,6 +145,7 @@
145145
"WebSocket",
146146
"FileReader",
147147
"CustomEvent",
148+
"ResizeObserver",
148149
"onmessage",
149150
"self"
150151
],

‎src/app/common/default-setting.js‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ export default {
8585
dragDropBehavior: 'ask',
8686
switchTabOnHover: false,
8787
disableShortcutBar: false,
88+
disableConfirmForLargeClipboardContent: false,
8889
leftSideBarIcons: [
8990
'newBookmark',
9091
'quickConnect',

‎src/app/common/runtime-constants.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export const extIconPath = isDev
2727
? '/node_modules/electerm-icons/icons/'
2828
: '/icons/'
2929
export const defaultUserName = 'default_user'
30-
export const minWindowWidth = 590
30+
export const minWindowWidth = 320
3131
export const minWindowHeight = 400
3232
export const defaultLang = 'en_us'
3333
export const tempDir = os.tmpdir()

‎src/app/lib/watch-file.js‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ const onWatch = _.debounce(() => {
1919
}, 300, { leading: false, trailing: true })
2020

2121
export const watchFile = (path) => {
22+
const prev = globalState.get('watchFilePath')
23+
if (prev && prev !== path) {
24+
fs.unwatchFile(prev, onWatch)
25+
}
2226
globalState.set('watchFilePath', path)
2327
fs.watchFile(path, onWatch)
2428
}
@@ -29,8 +33,8 @@ export const unwatchFile = (path) => {
2933
}
3034

3135
const cleanWatchFile = () => {
32-
globalState.set('watchFilePath', '')
3336
const filePath = globalState.get('watchFilePath')
37+
globalState.set('watchFilePath', '')
3438
if (!filePath) {
3539
return
3640
}

‎src/app/routes/ws.js‎

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,28 @@ export function wsRoutes (app) {
148148
ws.send(JSON.stringify(data))
149149
}
150150

151-
// In the WebSocket setup, replace the data handler:
152-
term.on('data', function (data) {
151+
// Detach listeners from a previous WS connection for the same term.
152+
// Reconnects create a new WS but reuse the same term instance; without
153+
// this each reconnect stacks another 'data' handler, duplicating output
154+
// and pinning closed sockets/buffers in memory.
155+
if (term._wsDataHandler && typeof term.off === 'function') {
156+
try {
157+
term.off('data', term._wsDataHandler)
158+
} catch (_) {}
159+
}
160+
if (term._wsPortDataHandler && term.port) {
161+
try {
162+
term.port.removeListener('data', term._wsPortDataHandler)
163+
} catch (_) {}
164+
}
165+
if (term._wsCloseHandler && typeof term.off === 'function') {
166+
try {
167+
term.off('close', term._wsCloseHandler)
168+
term.off('exit', term._wsCloseHandler)
169+
} catch (_) {}
170+
}
171+
172+
const onTermData = function (data) {
153173
// Check if zmodem session is active and handle data
154174
if (zmodemManager.isActive(pid)) {
155175
// Let zmodem handle the data, but still log it
@@ -247,25 +267,58 @@ export function wsRoutes (app) {
247267
if (!sendTimeout) {
248268
sendTimeout = setTimeout(flushBufferedData, flushIntervalMs - elapsed)
249269
}
250-
})
270+
}
271+
term._wsDataHandler = onTermData
272+
term.on('data', onTermData)
251273

252274
// For serial terminals, register a raw data listener directly on the port to
253275
// feed binary XMODEM data to xmodemManager without rxLineEnding transformation.
254276
if (term.port) {
255-
term.port.on('data', function (rawData) {
277+
const onPortData = function (rawData) {
256278
if (xmodemManager.isActive(pid)) {
257279
term.writeLog(rawData)
258280
xmodemManager.handleData(pid, rawData, term, ws)
259281
}
260-
})
282+
}
283+
term._wsPortDataHandler = onPortData
284+
term.port.on('data', onPortData)
261285
}
262286

287+
let onCloseCalled = false
263288
function onClose () {
289+
if (onCloseCalled) return
290+
onCloseCalled = true
291+
// Detach this connection's listeners so a reconnect does not stack
292+
// duplicates on the reused term instance.
293+
if (term._wsDataHandler && typeof term.off === 'function') {
294+
try {
295+
term.off('data', term._wsDataHandler)
296+
} catch (_) {}
297+
}
298+
if (term._wsDataHandler === onTermData) {
299+
term._wsDataHandler = null
300+
}
301+
if (term._wsPortDataHandler && term.port) {
302+
try {
303+
term.port.removeListener('data', term._wsPortDataHandler)
304+
} catch (_) {}
305+
term._wsPortDataHandler = null
306+
}
307+
if (typeof term.off === 'function') {
308+
try {
309+
term.off('close', onClose)
310+
term.off('exit', onClose)
311+
} catch (_) {}
312+
}
313+
if (term._wsCloseHandler === onClose) {
314+
term._wsCloseHandler = null
315+
}
264316
// Cancel any pending batched send
265317
if (sendTimeout) {
266318
clearTimeout(sendTimeout)
267319
sendTimeout = null
268320
}
321+
dataBuffer.length = 0
269322
// Clean up zmodem session
270323
zmodemManager.destroySession(pid)
271324
// Clean up trzsz session
@@ -284,6 +337,7 @@ export function wsRoutes (app) {
284337
// own ws close handler; whole-process cleanup is handled on app exit.
285338
}
286339

340+
term._wsCloseHandler = onClose
287341
term.on('close', onClose)
288342
if (term.isLocal && isWin) {
289343
term.on('exit', onClose)

‎src/app/server/session-base.js‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ export class TerminalBase {
3737

3838
cache = ''
3939
prevNewLine = true
40+
on () {
41+
// Implemented by subclasses (ssh/local/serial/telnet)
42+
}
43+
44+
off () {
45+
// Implemented by subclasses - removes a listener added via on()
46+
}
47+
4048
_initVtParser () {
4149
this._vtTerm = createVtParser(this.initOptions.cols || 4096)
4250
this._vtLastRow = 0

‎src/app/server/session-local.js‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,21 @@ class TerminalLocal extends TerminalBase {
9696
this.term.on(event, cb)
9797
}
9898

99+
off (event, cb) {
100+
try {
101+
if (!this.term) {
102+
return
103+
}
104+
if (typeof this.term.removeListener === 'function') {
105+
this.term.removeListener(event, cb)
106+
} else if (typeof this.term.off === 'function') {
107+
this.term.off(event, cb)
108+
}
109+
} catch (_) {
110+
// ignore removal errors during teardown
111+
}
112+
}
113+
99114
write (data) {
100115
this.term.write(data)
101116
}

‎src/app/server/session-serial.js‎

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ class TerminalSerial extends TerminalBase {
8181

8282
on (event, cb) {
8383
if (event === 'data' && this.rxLineEnding && this.rxLineEnding !== 'none') {
84-
this.port.on('data', (data) => {
84+
const wrapper = (data) => {
8585
const str = Buffer.isBuffer(data) ? data.toString('latin1') : String(data)
8686
let processed
8787
if (this.rxLineEnding === 'lf_to_crlf') {
@@ -92,12 +92,50 @@ class TerminalSerial extends TerminalBase {
9292
processed = str
9393
}
9494
cb(Buffer.isBuffer(data) ? Buffer.from(processed, 'latin1') : processed)
95-
})
95+
}
96+
if (!this._onWrappers) {
97+
this._onWrappers = new Map()
98+
}
99+
let list = this._onWrappers.get(cb)
100+
if (!list) {
101+
list = []
102+
this._onWrappers.set(cb, list)
103+
}
104+
list.push({ event, wrapper })
105+
this.port.on(event, wrapper)
96106
} else {
97107
this.port.on(event, cb)
98108
}
99109
}
100110

111+
off (event, cb) {
112+
try {
113+
const list = this._onWrappers?.get(cb)
114+
if (list) {
115+
const remaining = []
116+
for (const entry of list) {
117+
if (entry.event === event) {
118+
try {
119+
this.port?.removeListener?.(event, entry.wrapper)
120+
} catch (_) {}
121+
} else {
122+
remaining.push(entry)
123+
}
124+
}
125+
if (remaining.length) {
126+
this._onWrappers.set(cb, remaining)
127+
} else {
128+
this._onWrappers.delete(cb)
129+
}
130+
}
131+
try {
132+
this.port?.removeListener?.(event, cb)
133+
} catch (_) {}
134+
} catch (_) {
135+
// ignore removal errors during teardown
136+
}
137+
}
138+
101139
write (data) {
102140
try {
103141
const str = Buffer.isBuffer(data) ? data.toString('latin1') : String(data)

0 commit comments

Comments
 (0)