@electerm/electerm-react 2.6.0 → 2.7.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/common/constants.js +1 -1
- package/client/common/create-title.jsx +2 -2
- package/client/common/download.jsx +14 -14
- package/client/components/common/zoom-control.jsx +58 -0
- package/client/components/rdp/rdp-session.jsx +264 -198
- package/client/components/sftp/sftp.styl +1 -1
- package/client/components/shortcuts/shortcut-handler.js +84 -0
- package/client/components/sys-menu/zoom.jsx +4 -31
- package/client/components/terminal/terminal.jsx +51 -45
- package/client/components/web/address-bar.jsx +35 -6
- package/client/components/web/web-auth-modal.jsx +69 -0
- package/client/components/web/web-session.jsx +60 -4
- package/client/views/index.pug +0 -2
- package/package.json +1 -1
- package/client/entry/rle.js +0 -2
- package/client/entry/rle.wasm +0 -0
|
@@ -242,7 +242,7 @@ export const proxyHelpLink = 'https://github.com/electerm/electerm/wiki/proxy-fo
|
|
|
242
242
|
export const regexHelpLink = 'https://github.com/electerm/electerm/wiki/Terminal-keywords-highlight-regular-expression-exmaples'
|
|
243
243
|
export const connectionHoppingWikiLink = 'https://github.com/electerm/electerm/wiki/Connection-Hopping-Behavior-Change-in-electerm-since-v1.50.65'
|
|
244
244
|
export const aiConfigWikiLink = 'https://github.com/electerm/electerm/wiki/AI-model-config-guide'
|
|
245
|
-
export const rdpWikiLink = 'https://github.com/electerm/electerm/wiki/RDP-
|
|
245
|
+
export const rdpWikiLink = 'https://github.com/electerm/electerm/wiki/RDP-limitation'
|
|
246
246
|
export const vncWikiLink = 'https://github.com/electerm/electerm/wiki/VNC-session-known-issues'
|
|
247
247
|
export const modals = {
|
|
248
248
|
hide: 0,
|
|
@@ -22,12 +22,12 @@ export default function createTitle (res, hide = true) {
|
|
|
22
22
|
return ''
|
|
23
23
|
}
|
|
24
24
|
const {
|
|
25
|
-
host, port, username, title, type,
|
|
25
|
+
host, port, username, title, type, url,
|
|
26
26
|
path, connectionHoppings, sshTunnels
|
|
27
27
|
} = res
|
|
28
28
|
const h = hide && window.store.config.hideIP ? maskHost(host) : host
|
|
29
29
|
const fixTitle = `${username || ''}@${h}:${port}`
|
|
30
|
-
const extra = host || path ? (path || fixTitle) : ''
|
|
30
|
+
const extra = host || path ? (path || fixTitle) : (url || '')
|
|
31
31
|
let f = title
|
|
32
32
|
? `${title}` + (extra ? ` - ${extra}` : '')
|
|
33
33
|
: extra
|
|
@@ -6,22 +6,22 @@ import ShowItem from '../components/common/show-item'
|
|
|
6
6
|
import { chooseSaveDirectory } from './choose-save-folder'
|
|
7
7
|
import { DownloadOutlined } from '@ant-design/icons'
|
|
8
8
|
|
|
9
|
-
function downloadForBrowser (filename, text) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
9
|
+
// function downloadForBrowser (filename, text) {
|
|
10
|
+
// const blob = new Blob([text], { type: 'text/plain;charset=utf-8' })
|
|
11
|
+
// const url = URL.createObjectURL(blob)
|
|
12
|
+
// const a = document.createElement('a')
|
|
13
|
+
// a.href = url
|
|
14
|
+
// a.download = filename
|
|
15
|
+
// document.body.appendChild(a)
|
|
16
|
+
// a.click()
|
|
17
|
+
// document.body.removeChild(a)
|
|
18
|
+
// URL.revokeObjectURL(url)
|
|
19
|
+
// }
|
|
20
20
|
|
|
21
21
|
export default async function download (filename, text) {
|
|
22
|
-
if (window.et.isWebApp) {
|
|
23
|
-
|
|
24
|
-
}
|
|
22
|
+
// if (window.et.isWebApp) {
|
|
23
|
+
// return downloadForBrowser(filename, text)
|
|
24
|
+
// }
|
|
25
25
|
const savePath = await chooseSaveDirectory()
|
|
26
26
|
if (!savePath) {
|
|
27
27
|
return
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import InputNumberConfirm from './input-number-confirm'
|
|
3
|
+
import {
|
|
4
|
+
Space
|
|
5
|
+
} from 'antd'
|
|
6
|
+
import {
|
|
7
|
+
MinusCircleOutlined,
|
|
8
|
+
PlusCircleOutlined
|
|
9
|
+
} from '@ant-design/icons'
|
|
10
|
+
|
|
11
|
+
export default function ZoomControl (props) {
|
|
12
|
+
const {
|
|
13
|
+
value,
|
|
14
|
+
onChange,
|
|
15
|
+
min = 0.25,
|
|
16
|
+
max = 5,
|
|
17
|
+
step = 0.25
|
|
18
|
+
} = props
|
|
19
|
+
|
|
20
|
+
const handleChange = (v) => {
|
|
21
|
+
onChange(v / 100)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const handleAdd = () => {
|
|
25
|
+
let next = value + step
|
|
26
|
+
if (next > max) next = max
|
|
27
|
+
onChange(next)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const handleMinus = () => {
|
|
31
|
+
let next = value - step
|
|
32
|
+
if (next < min) next = min
|
|
33
|
+
onChange(next)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<InputNumberConfirm
|
|
38
|
+
value={Math.round(value * 100)}
|
|
39
|
+
onChange={handleChange}
|
|
40
|
+
step={1}
|
|
41
|
+
min={min * 100}
|
|
42
|
+
max={max * 100}
|
|
43
|
+
suffix='%'
|
|
44
|
+
addonBefore={
|
|
45
|
+
<Space.Compact>
|
|
46
|
+
<PlusCircleOutlined
|
|
47
|
+
onClick={handleAdd}
|
|
48
|
+
className='mg1r pointer font16'
|
|
49
|
+
/>
|
|
50
|
+
<MinusCircleOutlined
|
|
51
|
+
onClick={handleMinus}
|
|
52
|
+
className='pointer font16 mg1r'
|
|
53
|
+
/>
|
|
54
|
+
</Space.Compact>
|
|
55
|
+
}
|
|
56
|
+
/>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
@@ -8,10 +8,8 @@ import {
|
|
|
8
8
|
} from '../../common/constants'
|
|
9
9
|
import {
|
|
10
10
|
Spin,
|
|
11
|
-
// Button,
|
|
12
11
|
Select
|
|
13
12
|
} from 'antd'
|
|
14
|
-
import { notification } from '../common/notification'
|
|
15
13
|
import {
|
|
16
14
|
ReloadOutlined,
|
|
17
15
|
EditOutlined
|
|
@@ -22,6 +20,25 @@ import resolutions from './resolutions'
|
|
|
22
20
|
|
|
23
21
|
const { Option } = Select
|
|
24
22
|
|
|
23
|
+
// IronRDP WASM module imports — loaded dynamically
|
|
24
|
+
async function loadWasmModule () {
|
|
25
|
+
if (window.ironRdp) return
|
|
26
|
+
console.debug('[RDP-CLIENT] Loading IronRDP WASM module...')
|
|
27
|
+
const mod = await import('ironrdp-wasm')
|
|
28
|
+
window.ironRdp = {
|
|
29
|
+
wasmInit: mod.default,
|
|
30
|
+
wasmSetup: mod.setup,
|
|
31
|
+
SessionBuilder: mod.SessionBuilder,
|
|
32
|
+
DesktopSize: mod.DesktopSize,
|
|
33
|
+
InputTransaction: mod.InputTransaction,
|
|
34
|
+
DeviceEvent: mod.DeviceEvent,
|
|
35
|
+
Extension: mod.Extension
|
|
36
|
+
}
|
|
37
|
+
await window.ironRdp.wasmInit()
|
|
38
|
+
window.ironRdp.wasmSetup('info')
|
|
39
|
+
console.debug('[RDP-CLIENT] IronRDP WASM module loaded and initialized')
|
|
40
|
+
}
|
|
41
|
+
|
|
25
42
|
export default class RdpSession extends PureComponent {
|
|
26
43
|
constructor (props) {
|
|
27
44
|
const id = `rdp-reso-${props.tab.host}`
|
|
@@ -30,10 +47,9 @@ export default class RdpSession extends PureComponent {
|
|
|
30
47
|
this.canvasRef = createRef()
|
|
31
48
|
this.state = {
|
|
32
49
|
loading: false,
|
|
33
|
-
bitmapProps: {},
|
|
34
|
-
aspectRatio: 4 / 3,
|
|
35
50
|
...resObj
|
|
36
51
|
}
|
|
52
|
+
this.session = null
|
|
37
53
|
}
|
|
38
54
|
|
|
39
55
|
componentDidMount () {
|
|
@@ -41,14 +57,24 @@ export default class RdpSession extends PureComponent {
|
|
|
41
57
|
}
|
|
42
58
|
|
|
43
59
|
componentWillUnmount () {
|
|
44
|
-
this.
|
|
45
|
-
delete this.socket
|
|
60
|
+
this.cleanup()
|
|
46
61
|
}
|
|
47
62
|
|
|
48
|
-
|
|
49
|
-
|
|
63
|
+
cleanup = () => {
|
|
64
|
+
console.debug('[RDP-CLIENT] cleanup() called')
|
|
65
|
+
if (this.session) {
|
|
66
|
+
try {
|
|
67
|
+
this.session.shutdown()
|
|
68
|
+
console.debug('[RDP-CLIENT] session.shutdown() called')
|
|
69
|
+
} catch (e) {
|
|
70
|
+
console.debug('[RDP-CLIENT] session.shutdown() error:', e)
|
|
71
|
+
}
|
|
72
|
+
this.session = null
|
|
73
|
+
}
|
|
50
74
|
}
|
|
51
75
|
|
|
76
|
+
runInitScript = () => {}
|
|
77
|
+
|
|
52
78
|
setStatus = status => {
|
|
53
79
|
const id = this.props.tab?.id
|
|
54
80
|
this.props.editTab(id, {
|
|
@@ -56,7 +82,7 @@ export default class RdpSession extends PureComponent {
|
|
|
56
82
|
})
|
|
57
83
|
}
|
|
58
84
|
|
|
59
|
-
remoteInit = async (
|
|
85
|
+
remoteInit = async () => {
|
|
60
86
|
this.setState({
|
|
61
87
|
loading: true
|
|
62
88
|
})
|
|
@@ -80,237 +106,279 @@ export default class RdpSession extends PureComponent {
|
|
|
80
106
|
termType: type,
|
|
81
107
|
...tab
|
|
82
108
|
})
|
|
109
|
+
|
|
110
|
+
console.debug('[RDP-CLIENT] Creating RDP session term, host=', tab.host, 'port=', tab.port)
|
|
83
111
|
const r = await createTerm(opts)
|
|
84
112
|
.catch(err => {
|
|
85
113
|
const text = err.message
|
|
86
114
|
handleErr({ message: text })
|
|
87
115
|
})
|
|
88
|
-
this.setState({
|
|
89
|
-
loading: false
|
|
90
|
-
})
|
|
91
116
|
if (!r) {
|
|
117
|
+
this.setState({ loading: false })
|
|
92
118
|
this.setStatus(statusMap.error)
|
|
119
|
+
console.error('[RDP-CLIENT] createTerm failed')
|
|
93
120
|
return
|
|
94
121
|
}
|
|
95
|
-
|
|
122
|
+
|
|
96
123
|
const {
|
|
97
124
|
pid, port
|
|
98
125
|
} = r
|
|
99
126
|
this.pid = pid
|
|
127
|
+
console.debug('[RDP-CLIENT] Term created, pid=', pid, 'port=', port)
|
|
128
|
+
|
|
129
|
+
// Build the WebSocket proxy address for IronRDP WASM
|
|
100
130
|
const hs = server
|
|
101
131
|
? server.replace(/https?:\/\//, '')
|
|
102
132
|
: `${host}:${port}`
|
|
103
133
|
const pre = server.startsWith('https') ? 'wss' : 'ws'
|
|
104
134
|
const { width, height } = this.state
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
fName = 'bitmap_decompress_15'
|
|
119
|
-
break
|
|
120
|
-
case 16:
|
|
121
|
-
fName = 'bitmap_decompress_16'
|
|
122
|
-
break
|
|
123
|
-
case 24:
|
|
124
|
-
fName = 'bitmap_decompress_24'
|
|
125
|
-
break
|
|
126
|
-
case 32:
|
|
127
|
-
fName = 'bitmap_decompress_32'
|
|
128
|
-
break
|
|
129
|
-
default:
|
|
130
|
-
throw new Error('invalid bitmap data format')
|
|
135
|
+
// IronRDP connects to the proxy address, which then proxies via RDCleanPath
|
|
136
|
+
// The WebSocket URL includes the pid and token for auth
|
|
137
|
+
const proxyAddress = `${pre}://${hs}/rdp/${pid}?token=${tokenElecterm}&width=${width}&height=${height}`
|
|
138
|
+
console.debug('[RDP-CLIENT] Proxy address:', proxyAddress)
|
|
139
|
+
|
|
140
|
+
// Load WASM module if not already loaded
|
|
141
|
+
try {
|
|
142
|
+
await loadWasmModule()
|
|
143
|
+
} catch (e) {
|
|
144
|
+
console.error('[RDP-CLIENT] Failed to load WASM module:', e)
|
|
145
|
+
this.setState({ loading: false })
|
|
146
|
+
this.setStatus(statusMap.error)
|
|
147
|
+
return
|
|
131
148
|
}
|
|
132
|
-
const rle = window.Module
|
|
133
|
-
const input = new Uint8Array(bitmap.data.data)
|
|
134
|
-
const inputPtr = rle._malloc(input.length)
|
|
135
|
-
const inputHeap = new Uint8Array(rle.HEAPU8.buffer, inputPtr, input.length)
|
|
136
|
-
inputHeap.set(input)
|
|
137
|
-
|
|
138
|
-
const outputWidth = bitmap.destRight - bitmap.destLeft + 1
|
|
139
|
-
const outputHeight = bitmap.destBottom - bitmap.destTop + 1
|
|
140
|
-
const ouputSize = outputWidth * outputHeight * 4
|
|
141
|
-
const outputPtr = rle._malloc(ouputSize)
|
|
142
|
-
|
|
143
|
-
const outputHeap = new Uint8Array(rle.HEAPU8.buffer, outputPtr, ouputSize)
|
|
144
|
-
|
|
145
|
-
rle.ccall(fName,
|
|
146
|
-
'number',
|
|
147
|
-
['number', 'number', 'number', 'number', 'number', 'number', 'number', 'number'],
|
|
148
|
-
[outputHeap.byteOffset, outputWidth, outputHeight, bitmap.width, bitmap.height, inputHeap.byteOffset, input.length]
|
|
149
|
-
)
|
|
150
149
|
|
|
151
|
-
|
|
150
|
+
this.setStatus(statusMap.success)
|
|
152
151
|
|
|
153
|
-
|
|
154
|
-
|
|
152
|
+
// Connect using IronRDP SessionBuilder
|
|
153
|
+
try {
|
|
154
|
+
const canvas = this.canvasRef.current
|
|
155
|
+
if (!canvas) {
|
|
156
|
+
console.error('[RDP-CLIENT] Canvas ref not available')
|
|
157
|
+
this.setState({ loading: false })
|
|
158
|
+
return
|
|
159
|
+
}
|
|
155
160
|
|
|
156
|
-
|
|
157
|
-
|
|
161
|
+
const rdpHost = tab.host
|
|
162
|
+
const rdpPort = tab.port || 3389
|
|
163
|
+
const destination = `${rdpHost}:${rdpPort}`
|
|
164
|
+
const username = tab.username || ''
|
|
165
|
+
const password = tab.password || ''
|
|
166
|
+
|
|
167
|
+
console.debug('[RDP-CLIENT] Building IronRDP session...')
|
|
168
|
+
console.debug('[RDP-CLIENT] destination:', destination)
|
|
169
|
+
console.debug('[RDP-CLIENT] username:', username)
|
|
170
|
+
console.debug('[RDP-CLIENT] proxyAddress:', proxyAddress)
|
|
171
|
+
console.debug('[RDP-CLIENT] desktopSize:', width, 'x', height)
|
|
172
|
+
|
|
173
|
+
const desktopSize = new window.ironRdp.DesktopSize(width, height)
|
|
174
|
+
const enableCredsspExt = new window.ironRdp.Extension('enable_credssp', false)
|
|
175
|
+
|
|
176
|
+
const builder = new window.ironRdp.SessionBuilder()
|
|
177
|
+
builder.username(username)
|
|
178
|
+
builder.password(password)
|
|
179
|
+
builder.destination(destination)
|
|
180
|
+
builder.proxyAddress(proxyAddress)
|
|
181
|
+
builder.authToken('none')
|
|
182
|
+
builder.desktopSize(desktopSize)
|
|
183
|
+
builder.renderCanvas(canvas)
|
|
184
|
+
builder.extension(enableCredsspExt)
|
|
185
|
+
|
|
186
|
+
// Cursor style callback
|
|
187
|
+
builder.setCursorStyleCallbackContext(canvas)
|
|
188
|
+
builder.setCursorStyleCallback(function (style) {
|
|
189
|
+
canvas.style.cursor = style || 'default'
|
|
190
|
+
})
|
|
158
191
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
192
|
+
console.debug('[RDP-CLIENT] Calling builder.connect()...')
|
|
193
|
+
this.session = await builder.connect()
|
|
194
|
+
|
|
195
|
+
const ds = this.session.desktopSize()
|
|
196
|
+
console.debug('[RDP-CLIENT] Connected! Desktop:', ds.width, 'x', ds.height)
|
|
197
|
+
|
|
198
|
+
// Update canvas size to match actual desktop size
|
|
199
|
+
canvas.width = ds.width
|
|
200
|
+
canvas.height = ds.height
|
|
201
|
+
|
|
202
|
+
this.setState({
|
|
203
|
+
loading: false
|
|
204
|
+
})
|
|
169
205
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
206
|
+
canvas.focus()
|
|
207
|
+
|
|
208
|
+
// Run the session event loop (renders frames, handles protocol)
|
|
209
|
+
console.debug('[RDP-CLIENT] Starting session.run() event loop')
|
|
210
|
+
this.session.run().then((info) => {
|
|
211
|
+
console.debug('[RDP-CLIENT] Session ended:', info.reason())
|
|
212
|
+
this.onSessionEnd()
|
|
213
|
+
}).catch((e) => {
|
|
214
|
+
console.error('[RDP-CLIENT] Session error:', this.formatError(e))
|
|
215
|
+
this.onSessionEnd()
|
|
216
|
+
})
|
|
217
|
+
} catch (e) {
|
|
218
|
+
console.error('[RDP-CLIENT] Connection failed:', this.formatError(e))
|
|
219
|
+
this.setState({ loading: false })
|
|
220
|
+
this.setStatus(statusMap.error)
|
|
177
221
|
}
|
|
178
222
|
}
|
|
179
223
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
224
|
+
formatError = (e) => {
|
|
225
|
+
if (e && typeof e === 'object' && '__wbg_ptr' in e) {
|
|
226
|
+
try {
|
|
227
|
+
const kindNames = {
|
|
228
|
+
0: 'General',
|
|
229
|
+
1: 'WrongPassword',
|
|
230
|
+
2: 'LogonFailure',
|
|
231
|
+
3: 'AccessDenied',
|
|
232
|
+
4: 'RDCleanPath',
|
|
233
|
+
5: 'ProxyConnect',
|
|
234
|
+
6: 'NegotiationFailure'
|
|
235
|
+
}
|
|
236
|
+
const kind = e.kind ? e.kind() : 'Unknown'
|
|
237
|
+
const bt = e.backtrace ? e.backtrace() : ''
|
|
238
|
+
return `[${kindNames[kind] || kind}] ${bt}`
|
|
239
|
+
} catch (_) {}
|
|
188
240
|
}
|
|
241
|
+
return e?.message || e?.toString() || String(e)
|
|
189
242
|
}
|
|
190
243
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
clientY
|
|
196
|
-
} = e
|
|
197
|
-
const {
|
|
198
|
-
left,
|
|
199
|
-
top
|
|
200
|
-
} = e.target.getBoundingClientRect()
|
|
201
|
-
const x = clientX - left
|
|
202
|
-
const y = clientY - top
|
|
203
|
-
const keyCode = this.getKeyCode(e)
|
|
204
|
-
const action = type.startsWith('key')
|
|
205
|
-
? 'sendKeyEventScancode'
|
|
206
|
-
: type === 'mousewheel'
|
|
207
|
-
? 'sendWheelEvent'
|
|
208
|
-
: 'sendPointerEvent'
|
|
209
|
-
const pressed = type === 'mousedown' || type === 'keydown'
|
|
210
|
-
let params = []
|
|
211
|
-
if (type.startsWith('mouse') || type.startsWith('context')) {
|
|
212
|
-
params = [x, y, keyCode, pressed]
|
|
213
|
-
} else if (type === 'wheel') {
|
|
214
|
-
const isHorizontal = false
|
|
215
|
-
const delta = isHorizontal ? e.deltaX : e.deltaY
|
|
216
|
-
const step = Math.round(Math.abs(delta) * 15 / 8)
|
|
217
|
-
params = [x, y, step, delta > 0, isHorizontal]
|
|
218
|
-
} else if (type === 'keydown' || type === 'keyup') {
|
|
219
|
-
params = [keyCode, pressed]
|
|
220
|
-
}
|
|
221
|
-
this.socket.send(JSON.stringify({
|
|
222
|
-
action,
|
|
223
|
-
params
|
|
224
|
-
}))
|
|
244
|
+
onSessionEnd = () => {
|
|
245
|
+
console.debug('[RDP-CLIENT] onSessionEnd called')
|
|
246
|
+
this.session = null
|
|
247
|
+
this.setStatus(statusMap.error)
|
|
225
248
|
}
|
|
226
249
|
|
|
227
|
-
|
|
228
|
-
let { data } = msg
|
|
229
|
-
data = JSON.parse(data)
|
|
230
|
-
if (data.action === 'session-rdp-connected') {
|
|
231
|
-
return this.setState({
|
|
232
|
-
loading: false
|
|
233
|
-
})
|
|
234
|
-
}
|
|
250
|
+
setupInputHandlers = () => {
|
|
235
251
|
const canvas = this.canvasRef.current
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
252
|
+
if (!canvas || this._inputHandlersSetup) return
|
|
253
|
+
this._inputHandlersSetup = true
|
|
254
|
+
console.debug('[RDP-CLIENT] Setting up input handlers')
|
|
255
|
+
|
|
256
|
+
canvas.addEventListener('keydown', (e) => {
|
|
257
|
+
e.preventDefault()
|
|
258
|
+
e.stopPropagation()
|
|
259
|
+
if (!this.session) return
|
|
260
|
+
const scancode = this.getScancode(e.code)
|
|
261
|
+
if (scancode === null) return
|
|
262
|
+
try {
|
|
263
|
+
const event = window.ironRdp.DeviceEvent.keyPressed(scancode)
|
|
264
|
+
const tx = new window.ironRdp.InputTransaction()
|
|
265
|
+
tx.addEvent(event)
|
|
266
|
+
this.session.applyInputs(tx)
|
|
267
|
+
} catch (err) {
|
|
268
|
+
console.error('[RDP-CLIENT] Key press error:', err)
|
|
269
|
+
}
|
|
270
|
+
})
|
|
246
271
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
272
|
+
canvas.addEventListener('keyup', (e) => {
|
|
273
|
+
e.preventDefault()
|
|
274
|
+
e.stopPropagation()
|
|
275
|
+
if (!this.session) return
|
|
276
|
+
const scancode = this.getScancode(e.code)
|
|
277
|
+
if (scancode === null) return
|
|
278
|
+
try {
|
|
279
|
+
const event = window.ironRdp.DeviceEvent.keyReleased(scancode)
|
|
280
|
+
const tx = new window.ironRdp.InputTransaction()
|
|
281
|
+
tx.addEvent(event)
|
|
282
|
+
this.session.applyInputs(tx)
|
|
283
|
+
} catch (err) {
|
|
284
|
+
console.error('[RDP-CLIENT] Key release error:', err)
|
|
285
|
+
}
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
canvas.addEventListener('mousemove', (e) => {
|
|
289
|
+
if (!this.session) return
|
|
290
|
+
try {
|
|
291
|
+
const rect = canvas.getBoundingClientRect()
|
|
292
|
+
const scaleX = canvas.width / rect.width
|
|
293
|
+
const scaleY = canvas.height / rect.height
|
|
294
|
+
const x = Math.round((e.clientX - rect.left) * scaleX)
|
|
295
|
+
const y = Math.round((e.clientY - rect.top) * scaleY)
|
|
296
|
+
const event = window.ironRdp.DeviceEvent.mouseMove(x, y)
|
|
297
|
+
const tx = new window.ironRdp.InputTransaction()
|
|
298
|
+
tx.addEvent(event)
|
|
299
|
+
this.session.applyInputs(tx)
|
|
300
|
+
} catch (err) {
|
|
301
|
+
// suppress frequent mouse errors
|
|
302
|
+
}
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
canvas.addEventListener('mousedown', (e) => {
|
|
306
|
+
e.preventDefault()
|
|
307
|
+
canvas.focus()
|
|
308
|
+
if (!this.session) return
|
|
309
|
+
try {
|
|
310
|
+
const event = window.ironRdp.DeviceEvent.mouseButtonPressed(e.button)
|
|
311
|
+
const tx = new window.ironRdp.InputTransaction()
|
|
312
|
+
tx.addEvent(event)
|
|
313
|
+
this.session.applyInputs(tx)
|
|
314
|
+
} catch (err) {
|
|
315
|
+
console.error('[RDP-CLIENT] Mouse down error:', err)
|
|
316
|
+
}
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
canvas.addEventListener('mouseup', (e) => {
|
|
320
|
+
e.preventDefault()
|
|
321
|
+
if (!this.session) return
|
|
322
|
+
try {
|
|
323
|
+
const event = window.ironRdp.DeviceEvent.mouseButtonReleased(e.button)
|
|
324
|
+
const tx = new window.ironRdp.InputTransaction()
|
|
325
|
+
tx.addEvent(event)
|
|
326
|
+
this.session.applyInputs(tx)
|
|
327
|
+
} catch (err) {
|
|
328
|
+
console.error('[RDP-CLIENT] Mouse up error:', err)
|
|
329
|
+
}
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
canvas.addEventListener('wheel', (e) => {
|
|
333
|
+
e.preventDefault()
|
|
334
|
+
if (!this.session) return
|
|
335
|
+
try {
|
|
336
|
+
if (e.deltaY !== 0) {
|
|
337
|
+
const amount = e.deltaY > 0 ? -1 : 1
|
|
338
|
+
const event = window.ironRdp.DeviceEvent.wheelRotations(true, amount, 1)
|
|
339
|
+
const tx = new window.ironRdp.InputTransaction()
|
|
340
|
+
tx.addEvent(event)
|
|
341
|
+
this.session.applyInputs(tx)
|
|
342
|
+
}
|
|
343
|
+
if (e.deltaX !== 0) {
|
|
344
|
+
const amount = e.deltaX > 0 ? -1 : 1
|
|
345
|
+
const event = window.ironRdp.DeviceEvent.wheelRotations(false, amount, 1)
|
|
346
|
+
const tx = new window.ironRdp.InputTransaction()
|
|
347
|
+
tx.addEvent(event)
|
|
348
|
+
this.session.applyInputs(tx)
|
|
349
|
+
}
|
|
350
|
+
} catch (err) {
|
|
351
|
+
console.error('[RDP-CLIENT] Wheel error:', err)
|
|
352
|
+
}
|
|
353
|
+
}, { passive: false })
|
|
354
|
+
|
|
355
|
+
canvas.addEventListener('contextmenu', (e) => e.preventDefault())
|
|
250
356
|
}
|
|
251
357
|
|
|
252
|
-
|
|
253
|
-
|
|
358
|
+
// Get PS/2 scancode from keyboard event code using existing code-scan module
|
|
359
|
+
getScancode = (code) => {
|
|
360
|
+
const sc = scanCode({ code })
|
|
361
|
+
return sc !== undefined ? sc : null
|
|
254
362
|
}
|
|
255
363
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
this.handleReInit()
|
|
364
|
+
handleEditResolutions = () => {
|
|
365
|
+
window.store.toggleResolutionEdit()
|
|
259
366
|
}
|
|
260
367
|
|
|
261
368
|
handleReInit = () => {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
]
|
|
268
|
-
}))
|
|
269
|
-
this.setState({
|
|
270
|
-
loading: true
|
|
271
|
-
})
|
|
369
|
+
console.debug('[RDP-CLIENT] handleReInit called')
|
|
370
|
+
this.cleanup()
|
|
371
|
+
this.props.reloadTab(
|
|
372
|
+
this.props.tab
|
|
373
|
+
)
|
|
272
374
|
}
|
|
273
375
|
|
|
274
376
|
handleReload = () => {
|
|
275
|
-
this.closeMsg()
|
|
276
377
|
this.props.reloadTab(
|
|
277
378
|
this.props.tab
|
|
278
379
|
)
|
|
279
380
|
}
|
|
280
381
|
|
|
281
|
-
handleEditResolutions = () => {
|
|
282
|
-
window.store.toggleResolutionEdit()
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
oncloseSocket = () => {
|
|
286
|
-
// this.setStatus(
|
|
287
|
-
// statusMap.error
|
|
288
|
-
// )
|
|
289
|
-
// this.warningKey = `open${Date.now()}`
|
|
290
|
-
// notification.warning({
|
|
291
|
-
// key: this.warningKey,
|
|
292
|
-
// message: e('socketCloseTip'),
|
|
293
|
-
// duration: 30,
|
|
294
|
-
// description: (
|
|
295
|
-
// <div className='pd2y'>
|
|
296
|
-
// <Button
|
|
297
|
-
// className='mg1r'
|
|
298
|
-
// type='primary'
|
|
299
|
-
// onClick={this.handleClickClose}
|
|
300
|
-
// >
|
|
301
|
-
// {m('close')}
|
|
302
|
-
// </Button>
|
|
303
|
-
// <Button
|
|
304
|
-
// icon={<ReloadOutlined />}
|
|
305
|
-
// onClick={this.handleReload}
|
|
306
|
-
// >
|
|
307
|
-
// {m('reload')}
|
|
308
|
-
// </Button>
|
|
309
|
-
// </div>
|
|
310
|
-
// )
|
|
311
|
-
// })
|
|
312
|
-
}
|
|
313
|
-
|
|
314
382
|
getAllRes = () => {
|
|
315
383
|
return [
|
|
316
384
|
...this.props.resolutions,
|
|
@@ -389,6 +457,11 @@ export default class RdpSession extends PureComponent {
|
|
|
389
457
|
)
|
|
390
458
|
}
|
|
391
459
|
|
|
460
|
+
componentDidUpdate () {
|
|
461
|
+
// Set up native input handlers after canvas is rendered
|
|
462
|
+
this.setupInputHandlers()
|
|
463
|
+
}
|
|
464
|
+
|
|
392
465
|
render () {
|
|
393
466
|
const { width: w, height: h } = this.props
|
|
394
467
|
const rdpProps = {
|
|
@@ -401,13 +474,6 @@ export default class RdpSession extends PureComponent {
|
|
|
401
474
|
const canvasProps = {
|
|
402
475
|
width,
|
|
403
476
|
height,
|
|
404
|
-
onMouseDown: this.handleCanvasEvent,
|
|
405
|
-
onMouseUp: this.handleCanvasEvent,
|
|
406
|
-
onMouseMove: this.handleCanvasEvent,
|
|
407
|
-
onKeyDown: this.handleCanvasEvent,
|
|
408
|
-
onKeyUp: this.handleCanvasEvent,
|
|
409
|
-
onWheel: this.handleCanvasEvent,
|
|
410
|
-
onContextMenu: this.handleCanvasEvent,
|
|
411
477
|
tabIndex: 0
|
|
412
478
|
}
|
|
413
479
|
return (
|
|
@@ -167,7 +167,6 @@
|
|
|
167
167
|
|
|
168
168
|
.sftp-has-pager
|
|
169
169
|
.sftp-table-content
|
|
170
|
-
position static
|
|
171
170
|
padding-bottom 42px
|
|
172
171
|
.pager-wrap
|
|
173
172
|
position absolute
|
|
@@ -175,6 +174,7 @@
|
|
|
175
174
|
left 0
|
|
176
175
|
width 100%
|
|
177
176
|
background var(--main)
|
|
177
|
+
height 40px
|
|
178
178
|
border-top 1px solid var(--main-darker)
|
|
179
179
|
|
|
180
180
|
.file-header-context-menu
|
|
@@ -3,6 +3,87 @@ import shortcutsDefaultsGen from './shortcuts-defaults.js'
|
|
|
3
3
|
import {
|
|
4
4
|
isMacJs
|
|
5
5
|
} from '../../common/constants'
|
|
6
|
+
import keyControlPressed from '../../common/key-control-pressed.js'
|
|
7
|
+
|
|
8
|
+
function sendInputData (ctx, data) {
|
|
9
|
+
if (!data) return
|
|
10
|
+
if (ctx.attachAddon && ctx.attachAddon._sendData) {
|
|
11
|
+
ctx.attachAddon._sendData(data)
|
|
12
|
+
}
|
|
13
|
+
// if (!ctx.onData) return
|
|
14
|
+
// if (splitChars) {
|
|
15
|
+
// for (const ch of data) {
|
|
16
|
+
// ctx.onData(ch)
|
|
17
|
+
// }
|
|
18
|
+
// return
|
|
19
|
+
// }
|
|
20
|
+
// ctx.onData(data)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function getSelectionReplaceInfo (term) {
|
|
24
|
+
if (!term || !term.hasSelection()) return null
|
|
25
|
+
if (term.buffer?.active?.type === 'alternate') return null
|
|
26
|
+
const getPos = term.getSelectionPosition?.bind(term)
|
|
27
|
+
if (!getPos) return null
|
|
28
|
+
const pos = getPos()
|
|
29
|
+
if (!pos || !pos.start || !pos.end) return null
|
|
30
|
+
const buffer = term.buffer.active
|
|
31
|
+
const cursorY = buffer.cursorY
|
|
32
|
+
if (pos.start.y !== cursorY || pos.end.y !== cursorY) return null
|
|
33
|
+
const startX = Math.min(pos.start.x, pos.end.x)
|
|
34
|
+
const endX = Math.max(pos.start.x, pos.end.x)
|
|
35
|
+
if (startX === endX) return null
|
|
36
|
+
return {
|
|
37
|
+
startX,
|
|
38
|
+
endX,
|
|
39
|
+
cursorX: buffer.cursorX
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function handleTerminalSelectionReplace (event, ctx) {
|
|
44
|
+
if (
|
|
45
|
+
!ctx.term ||
|
|
46
|
+
!ctx.term.hasSelection() ||
|
|
47
|
+
keyControlPressed(event)
|
|
48
|
+
) {
|
|
49
|
+
return false
|
|
50
|
+
}
|
|
51
|
+
const { key } = event
|
|
52
|
+
const isBackspace = key === 'Backspace'
|
|
53
|
+
const isDelete = key === 'Delete'
|
|
54
|
+
const isPrintable = key && key.length === 1
|
|
55
|
+
if (!isBackspace && !isDelete && !isPrintable) return false
|
|
56
|
+
|
|
57
|
+
if (event && event.preventDefault) {
|
|
58
|
+
event.preventDefault()
|
|
59
|
+
}
|
|
60
|
+
if (event && event.stopPropagation) {
|
|
61
|
+
event.stopPropagation()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const info = getSelectionReplaceInfo(ctx.term)
|
|
65
|
+
if (!info) return false
|
|
66
|
+
|
|
67
|
+
const { startX, endX, cursorX } = info
|
|
68
|
+
const move = startX - cursorX
|
|
69
|
+
if (move > 0) {
|
|
70
|
+
sendInputData(ctx, '\x1b[C'.repeat(move))
|
|
71
|
+
} else if (move < 0) {
|
|
72
|
+
sendInputData(ctx, '\x1b[D'.repeat(-move))
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const delCount = endX - startX
|
|
76
|
+
if (delCount > 0) {
|
|
77
|
+
sendInputData(ctx, '\x1b[3~'.repeat(delCount))
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (isPrintable) {
|
|
81
|
+
sendInputData(ctx, key)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
ctx.term.clearSelection()
|
|
85
|
+
return true
|
|
86
|
+
}
|
|
6
87
|
|
|
7
88
|
function buildConfig (config, filter = d => d) {
|
|
8
89
|
const defs = shortcutsDefaultsGen().filter(filter)
|
|
@@ -65,6 +146,9 @@ export function shortcutExtend (Cls) {
|
|
|
65
146
|
if (isInAntdInput()) {
|
|
66
147
|
return
|
|
67
148
|
}
|
|
149
|
+
if (handleTerminalSelectionReplace(event, this)) {
|
|
150
|
+
return false
|
|
151
|
+
}
|
|
68
152
|
if (
|
|
69
153
|
this.term &&
|
|
70
154
|
key === 'Backspace' &&
|
|
@@ -1,38 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
Space
|
|
4
|
-
} from 'antd'
|
|
5
|
-
import {
|
|
6
|
-
MinusCircleOutlined,
|
|
7
|
-
PlusCircleOutlined
|
|
8
|
-
} from '@ant-design/icons'
|
|
1
|
+
import ZoomControl from '../common/zoom-control'
|
|
9
2
|
|
|
10
3
|
export default function ZoomMenu (props) {
|
|
11
4
|
const { store } = window
|
|
12
|
-
const handleChange = (v) => {
|
|
13
|
-
store.zoom(v / 100)
|
|
14
|
-
}
|
|
15
|
-
|
|
16
5
|
return (
|
|
17
|
-
<
|
|
18
|
-
value={
|
|
19
|
-
onChange={
|
|
20
|
-
step={1}
|
|
21
|
-
min={25}
|
|
22
|
-
max={500}
|
|
23
|
-
suffix='%'
|
|
24
|
-
addonBefore={
|
|
25
|
-
<Space.Addon>
|
|
26
|
-
<PlusCircleOutlined
|
|
27
|
-
onClick={() => store.zoom(0.25, true)}
|
|
28
|
-
className='mg1r pointer font16'
|
|
29
|
-
/>
|
|
30
|
-
<MinusCircleOutlined
|
|
31
|
-
onClick={() => store.zoom(-0.25, true)}
|
|
32
|
-
className='pointer font16'
|
|
33
|
-
/>
|
|
34
|
-
</Space.Addon>
|
|
35
|
-
}
|
|
6
|
+
<ZoomControl
|
|
7
|
+
value={props.config.zoom}
|
|
8
|
+
onChange={(v) => store.zoom(v)}
|
|
36
9
|
/>
|
|
37
10
|
)
|
|
38
11
|
}
|
|
@@ -971,64 +971,70 @@ class Term extends Component {
|
|
|
971
971
|
?.openSuggestions(cursorPos, data)
|
|
972
972
|
}
|
|
973
973
|
|
|
974
|
+
/**
|
|
975
|
+
* Read current input directly from terminal buffer
|
|
976
|
+
* This is more reliable than tracking character-by-character
|
|
977
|
+
*/
|
|
974
978
|
getCurrentInput = () => {
|
|
975
|
-
|
|
979
|
+
if (!this.term) return ''
|
|
980
|
+
|
|
981
|
+
const buffer = this.term.buffer.active
|
|
982
|
+
const cursorY = buffer.cursorY
|
|
983
|
+
const cursorX = buffer.cursorX
|
|
984
|
+
|
|
985
|
+
// Get the current line from buffer (baseY + cursorY gives absolute position)
|
|
986
|
+
const absoluteY = buffer.baseY + cursorY
|
|
987
|
+
const line = buffer.getLine(absoluteY)
|
|
988
|
+
if (!line) return ''
|
|
989
|
+
|
|
990
|
+
// Get text from start of line up to cursor position
|
|
991
|
+
const lineText = line.translateToString(true, 0, cursorX)
|
|
992
|
+
|
|
993
|
+
// Try to extract command after prompt
|
|
994
|
+
// Common prompt endings with trailing space
|
|
995
|
+
const promptEndings = ['$ ', '# ', '> ', '% ', '] ', ') ']
|
|
996
|
+
|
|
997
|
+
let commandStart = 0
|
|
998
|
+
for (const ending of promptEndings) {
|
|
999
|
+
const idx = lineText.lastIndexOf(ending)
|
|
1000
|
+
if (idx !== -1 && idx + ending.length > commandStart) {
|
|
1001
|
+
commandStart = idx + ending.length
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
return lineText.slice(commandStart)
|
|
976
1006
|
}
|
|
977
1007
|
|
|
978
1008
|
setCurrentInput = (value) => {
|
|
979
1009
|
this.currentInput = value
|
|
980
1010
|
}
|
|
981
1011
|
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
// Handle Ctrl+U (clear line)
|
|
989
|
-
if (d === '\x15') {
|
|
990
|
-
this.currentInput = ''
|
|
991
|
-
return
|
|
992
|
-
}
|
|
993
|
-
// Handle Ctrl+W (delete word)
|
|
994
|
-
if (d === '\x17') {
|
|
995
|
-
this.currentInput = this.currentInput.replace(/\S*\s*$/, '')
|
|
996
|
-
return
|
|
997
|
-
}
|
|
998
|
-
// Handle Ctrl+C (cancel)
|
|
999
|
-
if (d === '\x03') {
|
|
1000
|
-
this.currentInput = ''
|
|
1001
|
-
return
|
|
1002
|
-
}
|
|
1003
|
-
// Handle Enter
|
|
1012
|
+
/**
|
|
1013
|
+
* Handle special input events for command history tracking
|
|
1014
|
+
* The actual input reading is done via getCurrentInput from buffer
|
|
1015
|
+
*/
|
|
1016
|
+
handleInputEvent = (d) => {
|
|
1017
|
+
// Handle Enter - add command to history
|
|
1004
1018
|
if (d === '\r' || d === '\n') {
|
|
1005
|
-
|
|
1006
|
-
if (
|
|
1007
|
-
this.manualCommandHistory.add(
|
|
1008
|
-
|
|
1009
|
-
window.store.addCmdHistory(this.currentInput.trim())
|
|
1019
|
+
const currentCmd = this.getCurrentInput()
|
|
1020
|
+
if (currentCmd && currentCmd.trim() && this.shouldUseManualHistory()) {
|
|
1021
|
+
this.manualCommandHistory.add(currentCmd.trim())
|
|
1022
|
+
window.store.addCmdHistory(currentCmd.trim())
|
|
1010
1023
|
}
|
|
1011
|
-
this.
|
|
1012
|
-
return
|
|
1013
|
-
}
|
|
1014
|
-
// Handle Escape and other control characters
|
|
1015
|
-
if (d.charCodeAt(0) < 32 && d !== '\t') {
|
|
1016
|
-
return
|
|
1017
|
-
}
|
|
1018
|
-
// Handle arrow keys and other escape sequences
|
|
1019
|
-
if (d.startsWith('\x1b')) {
|
|
1020
|
-
return
|
|
1024
|
+
this.closeSuggestions()
|
|
1021
1025
|
}
|
|
1022
|
-
// Regular character input - append to buffer
|
|
1023
|
-
this.currentInput += d
|
|
1024
1026
|
}
|
|
1025
1027
|
|
|
1026
1028
|
onData = (d) => {
|
|
1027
|
-
this.
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1029
|
+
this.handleInputEvent(d)
|
|
1030
|
+
if (this.props.config.showCmdSuggestions) {
|
|
1031
|
+
const data = this.getCurrentInput()
|
|
1032
|
+
if (data && d !== '\r' && d !== '\n') {
|
|
1033
|
+
const cursorPos = this.getCursorPosition()
|
|
1034
|
+
this.openSuggestions(cursorPos, data)
|
|
1035
|
+
} else {
|
|
1036
|
+
this.closeSuggestions()
|
|
1037
|
+
}
|
|
1032
1038
|
} else {
|
|
1033
1039
|
this.closeSuggestions()
|
|
1034
1040
|
}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Input,
|
|
3
|
-
Tooltip
|
|
3
|
+
Tooltip,
|
|
4
|
+
Dropdown,
|
|
5
|
+
Space
|
|
4
6
|
} from 'antd'
|
|
5
7
|
import { copy } from '../../common/clipboard'
|
|
6
8
|
import {
|
|
7
9
|
ReloadOutlined,
|
|
8
|
-
GlobalOutlined
|
|
10
|
+
GlobalOutlined,
|
|
11
|
+
EllipsisOutlined
|
|
9
12
|
} from '@ant-design/icons'
|
|
13
|
+
import ZoomControl from '../common/zoom-control'
|
|
10
14
|
|
|
11
15
|
export default function AddressBar (props) {
|
|
12
16
|
const {
|
|
@@ -14,7 +18,9 @@ export default function AddressBar (props) {
|
|
|
14
18
|
onReload,
|
|
15
19
|
onOpen,
|
|
16
20
|
title,
|
|
17
|
-
description
|
|
21
|
+
description,
|
|
22
|
+
zoom,
|
|
23
|
+
onZoom
|
|
18
24
|
} = props
|
|
19
25
|
const content = (
|
|
20
26
|
<>
|
|
@@ -25,6 +31,19 @@ export default function AddressBar (props) {
|
|
|
25
31
|
function handleClick () {
|
|
26
32
|
copy(url)
|
|
27
33
|
}
|
|
34
|
+
const items = [
|
|
35
|
+
{
|
|
36
|
+
key: 'zoom',
|
|
37
|
+
label: (
|
|
38
|
+
<div onClick={e => e.stopPropagation()}>
|
|
39
|
+
<ZoomControl
|
|
40
|
+
value={zoom}
|
|
41
|
+
onChange={onZoom}
|
|
42
|
+
/>
|
|
43
|
+
</div>
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
]
|
|
28
47
|
return (
|
|
29
48
|
<div className='web-address-bar pd1'>
|
|
30
49
|
<Tooltip
|
|
@@ -39,9 +58,19 @@ export default function AddressBar (props) {
|
|
|
39
58
|
/>
|
|
40
59
|
}
|
|
41
60
|
suffix={
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
|
|
61
|
+
<Space>
|
|
62
|
+
<GlobalOutlined
|
|
63
|
+
className='pointer'
|
|
64
|
+
onClick={onOpen}
|
|
65
|
+
title={window.translate('openInDefaultBrowser')}
|
|
66
|
+
/>
|
|
67
|
+
<Dropdown
|
|
68
|
+
menu={{ items }}
|
|
69
|
+
trigger={['click']}
|
|
70
|
+
>
|
|
71
|
+
<EllipsisOutlined className='pointer' />
|
|
72
|
+
</Dropdown>
|
|
73
|
+
</Space>
|
|
45
74
|
}
|
|
46
75
|
/>
|
|
47
76
|
</Tooltip>
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import React, { useState, useCallback } from 'react'
|
|
2
|
+
import { Input, Button } from 'antd'
|
|
3
|
+
import Modal from '../common/modal'
|
|
4
|
+
|
|
5
|
+
export default function WebAuthModal ({ authRequest, onAuthSubmit, onAuthCancel }) {
|
|
6
|
+
const [username, setUsername] = useState('')
|
|
7
|
+
const [password, setPassword] = useState('')
|
|
8
|
+
|
|
9
|
+
const handleSubmit = useCallback(() => {
|
|
10
|
+
onAuthSubmit(username, password)
|
|
11
|
+
setUsername('')
|
|
12
|
+
setPassword('')
|
|
13
|
+
}, [onAuthSubmit, username, password])
|
|
14
|
+
|
|
15
|
+
const handleCancel = useCallback(() => {
|
|
16
|
+
onAuthCancel()
|
|
17
|
+
setUsername('')
|
|
18
|
+
setPassword('')
|
|
19
|
+
}, [onAuthCancel])
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<Modal
|
|
23
|
+
open={!!authRequest}
|
|
24
|
+
title='Authentication Required'
|
|
25
|
+
width={400}
|
|
26
|
+
onCancel={handleCancel}
|
|
27
|
+
footer={null}
|
|
28
|
+
>
|
|
29
|
+
<div className='pd1y'>
|
|
30
|
+
<p>
|
|
31
|
+
<b>{authRequest?.host}</b> requires authentication
|
|
32
|
+
{authRequest?.realm ? ` (${authRequest.realm})` : ''}
|
|
33
|
+
</p>
|
|
34
|
+
<div className='pd1b'>
|
|
35
|
+
<div className='pd1b'>Username</div>
|
|
36
|
+
<Input
|
|
37
|
+
value={username}
|
|
38
|
+
onChange={e => setUsername(e.target.value)}
|
|
39
|
+
placeholder='Username'
|
|
40
|
+
autoFocus
|
|
41
|
+
/>
|
|
42
|
+
</div>
|
|
43
|
+
<div className='pd1b'>
|
|
44
|
+
<div className='pd1b'>Password</div>
|
|
45
|
+
<Input.Password
|
|
46
|
+
value={password}
|
|
47
|
+
onChange={e => setPassword(e.target.value)}
|
|
48
|
+
placeholder='Password'
|
|
49
|
+
onPressEnter={handleSubmit}
|
|
50
|
+
/>
|
|
51
|
+
</div>
|
|
52
|
+
<div className='pd1t alignright'>
|
|
53
|
+
<Button
|
|
54
|
+
className='mg1r'
|
|
55
|
+
onClick={handleCancel}
|
|
56
|
+
>
|
|
57
|
+
Cancel
|
|
58
|
+
</Button>
|
|
59
|
+
<Button
|
|
60
|
+
type='primary'
|
|
61
|
+
onClick={handleSubmit}
|
|
62
|
+
>
|
|
63
|
+
Login
|
|
64
|
+
</Button>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
</Modal>
|
|
68
|
+
)
|
|
69
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import AddressBar from './address-bar'
|
|
2
|
-
|
|
2
|
+
import WebAuthModal from './web-auth-modal'
|
|
3
|
+
import React, { useState, useRef, useEffect, useCallback } from 'react'
|
|
3
4
|
|
|
4
5
|
export default function WebSession (props) {
|
|
5
6
|
const {
|
|
@@ -8,6 +9,9 @@ export default function WebSession (props) {
|
|
|
8
9
|
height,
|
|
9
10
|
reloadTab
|
|
10
11
|
} = props
|
|
12
|
+
const [zoom, setZoom] = useState(1.0)
|
|
13
|
+
const [authRequest, setAuthRequest] = useState(null)
|
|
14
|
+
const webviewRef = useRef(null)
|
|
11
15
|
const urlRegex = /^[a-z\d.+-]+:\/\/[^\s/$.?#].[^\s]*$/i
|
|
12
16
|
|
|
13
17
|
const { url = '' } = tab
|
|
@@ -15,6 +19,8 @@ export default function WebSession (props) {
|
|
|
15
19
|
url,
|
|
16
20
|
title: tab.title,
|
|
17
21
|
description: tab.description,
|
|
22
|
+
zoom,
|
|
23
|
+
onZoom: handleZoom,
|
|
18
24
|
onOpen: () => {
|
|
19
25
|
window.openLink(tab.url)
|
|
20
26
|
},
|
|
@@ -25,6 +31,50 @@ export default function WebSession (props) {
|
|
|
25
31
|
}
|
|
26
32
|
}
|
|
27
33
|
|
|
34
|
+
function handleZoom (v) {
|
|
35
|
+
setZoom(v)
|
|
36
|
+
const el = webviewRef.current
|
|
37
|
+
if (!el) {
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
if (el.setZoomFactor) {
|
|
41
|
+
el.setZoomFactor(v)
|
|
42
|
+
} else {
|
|
43
|
+
el.style.zoom = v
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Handle HTTP Basic Auth requests from webview
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
if (!window.api || !window.api.onWebviewAuthRequest) {
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
const removeListener = window.api.onWebviewAuthRequest((data) => {
|
|
53
|
+
setAuthRequest(data)
|
|
54
|
+
})
|
|
55
|
+
return removeListener
|
|
56
|
+
}, [])
|
|
57
|
+
|
|
58
|
+
const handleAuthSubmit = useCallback((username, password) => {
|
|
59
|
+
if (!authRequest) return
|
|
60
|
+
window.api.sendWebviewAuthResponse({
|
|
61
|
+
id: authRequest.id,
|
|
62
|
+
username,
|
|
63
|
+
password
|
|
64
|
+
})
|
|
65
|
+
setAuthRequest(null)
|
|
66
|
+
}, [authRequest])
|
|
67
|
+
|
|
68
|
+
const handleAuthCancel = useCallback(() => {
|
|
69
|
+
if (!authRequest) return
|
|
70
|
+
window.api.sendWebviewAuthResponse({
|
|
71
|
+
id: authRequest.id,
|
|
72
|
+
username: '',
|
|
73
|
+
password: ''
|
|
74
|
+
})
|
|
75
|
+
setAuthRequest(null)
|
|
76
|
+
}, [authRequest])
|
|
77
|
+
|
|
28
78
|
// TODO: 支持自定义Header和Cookie
|
|
29
79
|
// useEffect(() => {
|
|
30
80
|
// const webview = document.querySelector('webview')
|
|
@@ -64,14 +114,15 @@ export default function WebSession (props) {
|
|
|
64
114
|
}
|
|
65
115
|
}
|
|
66
116
|
return (
|
|
67
|
-
<iframe {...iframeProps} />
|
|
117
|
+
<iframe {...iframeProps} ref={webviewRef} />
|
|
68
118
|
)
|
|
69
119
|
}
|
|
70
120
|
const viewProps = {
|
|
71
121
|
src: url,
|
|
72
122
|
style: {
|
|
73
123
|
width: (width - 10) + 'px',
|
|
74
|
-
height: (height + hOffset) + 'px'
|
|
124
|
+
height: (height + hOffset) + 'px',
|
|
125
|
+
background: '#fff'
|
|
75
126
|
},
|
|
76
127
|
disableblinkfeatures: 'true',
|
|
77
128
|
disablewebsecurity: 'true',
|
|
@@ -79,7 +130,7 @@ export default function WebSession (props) {
|
|
|
79
130
|
useragent: tab.useragent || 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
|
|
80
131
|
}
|
|
81
132
|
return (
|
|
82
|
-
<webview {...viewProps} />
|
|
133
|
+
<webview {...viewProps} ref={webviewRef} />
|
|
83
134
|
)
|
|
84
135
|
}
|
|
85
136
|
|
|
@@ -91,6 +142,11 @@ export default function WebSession (props) {
|
|
|
91
142
|
<div className='pd1'>
|
|
92
143
|
{renderView()}
|
|
93
144
|
</div>
|
|
145
|
+
<WebAuthModal
|
|
146
|
+
authRequest={authRequest}
|
|
147
|
+
onAuthSubmit={handleAuthSubmit}
|
|
148
|
+
onAuthCancel={handleAuthCancel}
|
|
149
|
+
/>
|
|
94
150
|
</div>
|
|
95
151
|
)
|
|
96
152
|
}
|
package/client/views/index.pug
CHANGED
|
@@ -47,7 +47,6 @@ html
|
|
|
47
47
|
img.iblock.logo-filter(src='images/electerm.png', alt='', height=80)
|
|
48
48
|
script.
|
|
49
49
|
window.et = !{JSON.stringify(_global)}
|
|
50
|
-
- var url1 = '/src/client/entry/rle.js'
|
|
51
50
|
- var url = '/src/client/entry/basic.js'
|
|
52
51
|
- if (isDev)
|
|
53
52
|
//- script(src='/external/react.development.js?' + version)
|
|
@@ -65,7 +64,6 @@ html
|
|
|
65
64
|
//- script(src='/external/react.production.min.js?' + version)
|
|
66
65
|
//- script(src='/external/react-dom.production.min.js?' + version)
|
|
67
66
|
- var url = src='/js/basic-' + version + '.js'
|
|
68
|
-
- var url1 = src='/js/rle-' + version + '.js'
|
|
69
67
|
script(src=url1, type='module')
|
|
70
68
|
script(src=url, type='module')
|
|
71
69
|
|
package/package.json
CHANGED
package/client/entry/rle.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
// This file and rle.wasm is generated by [**Emscripten**](https://github.com/kripken/emscripten) from [rle.c](https://raw.githubusercontent.com/citronneur/mstsc.js/master/obj/rle.c)
|
|
2
|
-
var Module=typeof Module!="undefined"?Module:{};window.Module=Module;var ENVIRONMENT_IS_WEB=typeof window=="object";var ENVIRONMENT_IS_WORKER=typeof importScripts=="function";var ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string";if(ENVIRONMENT_IS_NODE){}var moduleOverrides=Object.assign({},Module);var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary;if(ENVIRONMENT_IS_NODE){var fs=require("fs");var nodePath=require("path");scriptDirectory=__dirname+"/";read_=(filename,binary)=>{filename=isFileURI(filename)?new URL(filename):nodePath.normalize(filename);return fs.readFileSync(filename,binary?undefined:"utf8")};readBinary=filename=>{var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}return ret};readAsync=(filename,onload,onerror,binary=true)=>{filename=isFileURI(filename)?new URL(filename):nodePath.normalize(filename);fs.readFile(filename,binary?undefined:"utf8",(err,data)=>{if(err)onerror(err);else onload(binary?data.buffer:data)})};if(!Module["thisProgram"]&&process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}arguments_=process.argv.slice(2);if(typeof module!="undefined"){module["exports"]=Module}process.on("uncaughtException",ex=>{if(ex!=="unwind"&&!(ex instanceof ExitStatus)&&!(ex.context instanceof ExitStatus)){throw ex}});quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(scriptDirectory.startsWith("blob:")){scriptDirectory=""}else{scriptDirectory=scriptDirectory.substr(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}{read_=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=(url,onload,onerror)=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=()=>{if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.error.bind(console);Object.assign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var wasmMemory;var ABORT=false;var EXITSTATUS;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateMemoryViews(){var b=wasmMemory.buffer;Module["HEAP8"]=HEAP8=new Int8Array(b);Module["HEAP16"]=HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);Module["HEAPF64"]=HEAPF64=new Float64Array(b)}var __ATPRERUN__=[];var __ATINIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnInit(cb){__ATINIT__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function addRunDependency(id){runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)}function removeRunDependency(id){runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;EXITSTATUS=1;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);throw e}var dataURIPrefix="data:application/octet-stream;base64,";var isDataURI=filename=>filename.startsWith(dataURIPrefix);var isFileURI=filename=>filename.startsWith("file://");function findWasmBinary(){var f="rle.wasm";if(!isDataURI(f)){return locateFile(f)}return f}var wasmBinaryFile;function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}function getBinaryPromise(binaryFile){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)){if(typeof fetch=="function"&&!isFileURI(binaryFile)){return fetch(binaryFile,{credentials:"same-origin"}).then(response=>{if(!response["ok"]){throw`failed to load wasm binary file at '${binaryFile}'`}return response["arrayBuffer"]()}).catch(()=>getBinarySync(binaryFile))}else if(readAsync){return new Promise((resolve,reject)=>{readAsync(binaryFile,response=>resolve(new Uint8Array(response)),reject)})}}return Promise.resolve().then(()=>getBinarySync(binaryFile))}function instantiateArrayBuffer(binaryFile,imports,receiver){return getBinaryPromise(binaryFile).then(binary=>WebAssembly.instantiate(binary,imports)).then(receiver,reason=>{err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)})}function instantiateAsync(binary,binaryFile,imports,callback){if(!binary&&typeof WebAssembly.instantiateStreaming=="function"&&!isDataURI(binaryFile)&&!isFileURI(binaryFile)&&!ENVIRONMENT_IS_NODE&&typeof fetch=="function"){return fetch(binaryFile,{credentials:"same-origin"}).then(response=>{var result=WebAssembly.instantiateStreaming(response,imports);return result.then(callback,function(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(binaryFile,imports,callback)})})}return instantiateArrayBuffer(binaryFile,imports,callback)}function getWasmImports(){return{"a":wasmImports}}function createWasm(){var info=getWasmImports();function receiveInstance(instance,module){wasmExports=instance.exports;wasmMemory=wasmExports["b"];updateMemoryViews();addOnInit(wasmExports["c"]);removeRunDependency("wasm-instantiate");return wasmExports}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){receiveInstance(result["instance"])}if(Module["instantiateWasm"]){try{return Module["instantiateWasm"](info,receiveInstance)}catch(e){err(`Module.instantiateWasm callback failed with error: ${e}`);return false}}if(!wasmBinaryFile)wasmBinaryFile=findWasmBinary();instantiateAsync(wasmBinary,wasmBinaryFile,info,receiveInstantiationResult);return{}}function ExitStatus(status){this.name="ExitStatus";this.message=`Program terminated with exit(${status})`;this.status=status}var callRuntimeCallbacks=callbacks=>{while(callbacks.length>0){callbacks.shift()(Module)}};var noExitRuntime=Module["noExitRuntime"]||true;var stackRestore=val=>__emscripten_stack_restore(val);var stackSave=()=>_emscripten_stack_get_current();var abortOnCannotGrowMemory=requestedSize=>{abort("OOM")};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;abortOnCannotGrowMemory(requestedSize)};var getCFunc=ident=>{var func=Module["_"+ident];return func};var writeArrayToMemory=(array,buffer)=>{HEAP8.set(array,buffer)};var lengthBytesUTF8=str=>{var len=0;for(var i=0;i<str.length;++i){var c=str.charCodeAt(i);if(c<=127){len++}else if(c<=2047){len+=2}else if(c>=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx};var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);var stackAlloc=sz=>__emscripten_stack_alloc(sz);var stringToUTF8OnStack=str=>{var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8(str,ret,size);return ret};var UTF8Decoder=typeof TextDecoder!="undefined"?new TextDecoder("utf8"):undefined;var UTF8ArrayToString=(heapOrArray,idx,maxBytesToRead)=>{var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heapOrArray[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx<endPtr){var u0=heapOrArray[idx++];if(!(u0&128)){str+=String.fromCharCode(u0);continue}var u1=heapOrArray[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}var u2=heapOrArray[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u0=(u0&7)<<18|u1<<12|u2<<6|heapOrArray[idx++]&63}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}return str};var UTF8ToString=(ptr,maxBytesToRead)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):"";var ccall=(ident,returnType,argTypes,args,opts)=>{var toC={"string":str=>{var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=stringToUTF8OnStack(str)}return ret},"array":arr=>{var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string"){return UTF8ToString(ret)}if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i<args.length;i++){var converter=toC[argTypes[i]];if(converter){if(stack===0)stack=stackSave();cArgs[i]=converter(args[i])}else{cArgs[i]=args[i]}}}var ret=func(...cArgs);function onDone(ret){if(stack!==0)stackRestore(stack);return convertReturnValue(ret)}ret=onDone(ret);return ret};var wasmImports={a:_emscripten_resize_heap};var wasmExports=createWasm();var ___wasm_call_ctors=()=>(___wasm_call_ctors=wasmExports["c"])();var _bitmap_decompress_15=Module["_bitmap_decompress_15"]=(a0,a1,a2,a3,a4,a5,a6)=>(_bitmap_decompress_15=Module["_bitmap_decompress_15"]=wasmExports["d"])(a0,a1,a2,a3,a4,a5,a6);var _malloc=Module["_malloc"]=a0=>(_malloc=Module["_malloc"]=wasmExports["e"])(a0);var _free=Module["_free"]=a0=>(_free=Module["_free"]=wasmExports["f"])(a0);var _bitmap_decompress_16=Module["_bitmap_decompress_16"]=(a0,a1,a2,a3,a4,a5,a6)=>(_bitmap_decompress_16=Module["_bitmap_decompress_16"]=wasmExports["g"])(a0,a1,a2,a3,a4,a5,a6);var _bitmap_decompress_24=Module["_bitmap_decompress_24"]=(a0,a1,a2,a3,a4,a5,a6)=>(_bitmap_decompress_24=Module["_bitmap_decompress_24"]=wasmExports["h"])(a0,a1,a2,a3,a4,a5,a6);var _bitmap_decompress_32=Module["_bitmap_decompress_32"]=(a0,a1,a2,a3,a4,a5,a6)=>(_bitmap_decompress_32=Module["_bitmap_decompress_32"]=wasmExports["i"])(a0,a1,a2,a3,a4,a5,a6);var __emscripten_stack_restore=a0=>(__emscripten_stack_restore=wasmExports["k"])(a0);var __emscripten_stack_alloc=a0=>(__emscripten_stack_alloc=wasmExports["l"])(a0);var _emscripten_stack_get_current=()=>(_emscripten_stack_get_current=wasmExports["m"])();Module["ccall"]=ccall;var calledRun;dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(){if(runDependencies>0){return}preRun();if(runDependencies>0){return}function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}run();
|
package/client/entry/rle.wasm
DELETED
|
Binary file
|