@electerm/electerm-react 3.15.167 → 3.15.173
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 +4 -4
- package/client/components/session/session.jsx +2 -2
- package/client/components/shortcuts/shortcuts-defaults.js +1 -1
- package/client/components/tabs/no-session.jsx +2 -4
- package/client/components/tabs/no-session.styl +7 -0
- package/client/components/terminal/terminal.jsx +113 -6
- package/client/css/mobile.styl +10 -9
- package/package.json +1 -1
|
@@ -14,9 +14,9 @@ const buildConst = (props) => {
|
|
|
14
14
|
}, {})
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export const logoPath1 = logoPath1Ref.replace(/^\//, '')
|
|
18
|
-
export const logoPath2 = logoPath2Ref.replace(/^\//, '')
|
|
19
|
-
export const logoPath3 = logoPath3Ref.replace(/^\//, '')
|
|
17
|
+
export const logoPath1 = logoPath1Ref.replace(/^\//, '') + '?ver=' + packInfo.version
|
|
18
|
+
export const logoPath2 = logoPath2Ref.replace(/^\//, '') + '?ver=' + packInfo.version
|
|
19
|
+
export const logoPath3 = logoPath3Ref.replace(/^\//, '') + '?ver=' + packInfo.version
|
|
20
20
|
export const maxEditFileSize = 1024 * 3000
|
|
21
21
|
export const defaultBookmarkGroupId = 'default'
|
|
22
22
|
export const newBookmarkIdPrefix = 'new-bookmark'
|
|
@@ -197,7 +197,7 @@ export const commonLineEndings = commonTxLineEndings
|
|
|
197
197
|
|
|
198
198
|
export const maxBatchInput = 30
|
|
199
199
|
export const windowControlWidth = 94
|
|
200
|
-
export const mobileBreakpoint =
|
|
200
|
+
export const mobileBreakpoint = 600
|
|
201
201
|
export const baseUpdateCheckUrls = [
|
|
202
202
|
packInfo.homepage,
|
|
203
203
|
'https://gitee.com/github-zxdong262/electerm/raw/gh-pages'
|
|
@@ -677,8 +677,8 @@ export default class SessionWrapper extends Component {
|
|
|
677
677
|
onClick={() => this.onChangePane(types[i])}
|
|
678
678
|
>
|
|
679
679
|
<span className='type-tab-txt'>
|
|
680
|
-
<span className='
|
|
681
|
-
<span className='
|
|
680
|
+
<span className='type-tab-full'>{e(type)}</span>
|
|
681
|
+
<span className='type-tab-short'>{simpleMapper[type]}</span>
|
|
682
682
|
<span className='type-tab-line' />
|
|
683
683
|
</span>
|
|
684
684
|
</span>
|
|
@@ -25,7 +25,7 @@ export default function NoSessionPanel ({ height, onNewTab, onNewSsh, batch }) {
|
|
|
25
25
|
? (
|
|
26
26
|
<Button
|
|
27
27
|
onClick={onNewTab}
|
|
28
|
-
className='
|
|
28
|
+
className='add-new-tab-btn'
|
|
29
29
|
>
|
|
30
30
|
{e('newTab')}
|
|
31
31
|
</Button>
|
|
@@ -33,17 +33,15 @@ export default function NoSessionPanel ({ height, onNewTab, onNewSsh, batch }) {
|
|
|
33
33
|
: null
|
|
34
34
|
return (
|
|
35
35
|
<div className='no-sessions electerm-logo-bg' {...props}>
|
|
36
|
-
<div className='
|
|
36
|
+
<div className='no-session-btns'>
|
|
37
37
|
{newTabDom}
|
|
38
38
|
<Button
|
|
39
39
|
onClick={onNewSsh}
|
|
40
|
-
className='mg1r mg1b'
|
|
41
40
|
>
|
|
42
41
|
{e('newBookmark')}
|
|
43
42
|
</Button>
|
|
44
43
|
<Button
|
|
45
44
|
onClick={handleCreateAIBookmark}
|
|
46
|
-
className='mg1r mg1b'
|
|
47
45
|
icon={<RobotOutlined />}
|
|
48
46
|
>
|
|
49
47
|
{e('createBookmarkByAI')}
|
|
@@ -148,9 +148,14 @@ class Term extends Component {
|
|
|
148
148
|
this.props.themeConfig,
|
|
149
149
|
prevProps.themeConfig
|
|
150
150
|
)
|
|
151
|
-
|
|
152
|
-
|
|
151
|
+
// Also detect theme ID changes. Two different themes might share the
|
|
152
|
+
// same terminal colour config but have different UI colours (--main),
|
|
153
|
+
// which means the WebGL background needs to change even though
|
|
154
|
+
// themeConfig (terminal colours) is identical.
|
|
155
|
+
const themeIdChanged = prevProps.config?.theme !== this.props.config?.theme
|
|
156
|
+
if ((themeChanged || themeIdChanged) && this.term) {
|
|
153
157
|
this.registerTerminalColorQueryHandlers(this.term, this.props.themeConfig)
|
|
158
|
+
this.applyTerminalTheme(true)
|
|
154
159
|
}
|
|
155
160
|
}
|
|
156
161
|
|
|
@@ -163,6 +168,8 @@ class Term extends Component {
|
|
|
163
168
|
this.term.parent = null
|
|
164
169
|
}
|
|
165
170
|
this.disposeTerminalColorQueryHandlers()
|
|
171
|
+
window.cancelAnimationFrame(this.timers.themeRaf)
|
|
172
|
+
this.timers.themeRaf = null
|
|
166
173
|
Object.keys(this.timers).forEach(k => {
|
|
167
174
|
clearTimeout(this.timers[k])
|
|
168
175
|
this.timers[k] = null
|
|
@@ -185,6 +192,10 @@ class Term extends Component {
|
|
|
185
192
|
this.fitAddon = null
|
|
186
193
|
this.cmdAddon = null
|
|
187
194
|
this.imageAddon = null
|
|
195
|
+
this.webglContextLossDisposable?.dispose?.()
|
|
196
|
+
this.webglContextLossDisposable = null
|
|
197
|
+
this.webglAddon = null
|
|
198
|
+
this.webglRecovering = false
|
|
188
199
|
}
|
|
189
200
|
|
|
190
201
|
terminalConfigProps = [
|
|
@@ -242,6 +253,18 @@ class Term extends Component {
|
|
|
242
253
|
}
|
|
243
254
|
}
|
|
244
255
|
|
|
256
|
+
// Handle renderer type changes (dom <-> webGL) by reloading the
|
|
257
|
+
// renderer and refreshing the theme so the background color is
|
|
258
|
+
// correct for the new renderer.
|
|
259
|
+
if (
|
|
260
|
+
prevProps.config.rendererType !== props.config.rendererType &&
|
|
261
|
+
this.term
|
|
262
|
+
) {
|
|
263
|
+
this.reloadWebglRenderer('renderer type change')
|
|
264
|
+
.then(() => this.applyTerminalTheme())
|
|
265
|
+
.catch(e => console.error('renderer type change failed', e))
|
|
266
|
+
}
|
|
267
|
+
|
|
245
268
|
// Check for shell integration related config changes
|
|
246
269
|
const prevShowSuggestions = prevProps.config.showCmdSuggestions
|
|
247
270
|
const currShowSuggestions = props.config.showCmdSuggestions
|
|
@@ -1127,15 +1150,59 @@ class Term extends Component {
|
|
|
1127
1150
|
if (config.rendererType === rendererTypes.webGL) {
|
|
1128
1151
|
try {
|
|
1129
1152
|
const WebglAddon = await loadWebglAddon()
|
|
1130
|
-
|
|
1153
|
+
const webglAddon = new WebglAddon()
|
|
1154
|
+
this.webglAddon = webglAddon
|
|
1155
|
+
// On macOS native fullscreen the GPU/WebGL context can be lost when
|
|
1156
|
+
// the window migrates across Spaces. Without a listener xterm keeps
|
|
1157
|
+
// drawing into a dead context and every terminal goes black while the
|
|
1158
|
+
// rest of the UI stays alive. Rebuild the addon to recover.
|
|
1159
|
+
this.webglContextLossDisposable = webglAddon.onContextLoss(this.handleWebglContextLoss)
|
|
1160
|
+
term.loadAddon(webglAddon)
|
|
1131
1161
|
} catch (e) {
|
|
1132
1162
|
console.error('render with webgl failed, fallback to dom renderer')
|
|
1133
1163
|
console.error(e)
|
|
1134
1164
|
// built-in DOM renderer is used (no addon loaded)
|
|
1165
|
+
this.webglAddon = null
|
|
1135
1166
|
}
|
|
1136
1167
|
}
|
|
1137
1168
|
}
|
|
1138
1169
|
|
|
1170
|
+
reloadWebglRenderer = (reason = 'reload') => {
|
|
1171
|
+
console.warn(`webgl renderer ${reason}, rebuilding`)
|
|
1172
|
+
try {
|
|
1173
|
+
this.webglContextLossDisposable?.dispose?.()
|
|
1174
|
+
this.webglContextLossDisposable = null
|
|
1175
|
+
} catch (e) {
|
|
1176
|
+
console.error(e)
|
|
1177
|
+
}
|
|
1178
|
+
try {
|
|
1179
|
+
this.webglAddon?.dispose?.()
|
|
1180
|
+
} catch (e) {
|
|
1181
|
+
console.error(e)
|
|
1182
|
+
}
|
|
1183
|
+
this.webglAddon = null
|
|
1184
|
+
const { term } = this
|
|
1185
|
+
const { config } = this.props
|
|
1186
|
+
return this.loadRenderer(term, config)
|
|
1187
|
+
.then(() => {
|
|
1188
|
+
term.refresh(0, term.rows - 1)
|
|
1189
|
+
})
|
|
1190
|
+
.catch(e => {
|
|
1191
|
+
console.error(`webgl renderer ${reason} failed`, e)
|
|
1192
|
+
})
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
handleWebglContextLoss = (webglAddon = this.webglAddon) => {
|
|
1196
|
+
if (this.webglRecovering || !webglAddon) {
|
|
1197
|
+
return
|
|
1198
|
+
}
|
|
1199
|
+
this.webglRecovering = true
|
|
1200
|
+
this.reloadWebglRenderer('context loss')
|
|
1201
|
+
.finally(() => {
|
|
1202
|
+
this.webglRecovering = false
|
|
1203
|
+
})
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1139
1206
|
terminalColorQueryDisposables = []
|
|
1140
1207
|
|
|
1141
1208
|
disposeTerminalColorQueryHandlers = () => {
|
|
@@ -1145,9 +1212,15 @@ class Term extends Component {
|
|
|
1145
1212
|
|
|
1146
1213
|
getVisibleTerminalBackground = () => {
|
|
1147
1214
|
const uiThemeConfig = window.store?.getUiThemeConfig?.() || {}
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1215
|
+
// The store value (uiThemeConfig.main) is always immediately up-to-date
|
|
1216
|
+
// when the theme changes, because it reads directly from store.config.theme.
|
|
1217
|
+
// The CSS --main variable lags behind because UiTheme's useEffect runs
|
|
1218
|
+
// asynchronously after componentDidUpdate. So we prioritise the store
|
|
1219
|
+
// value, and only fall back to CSS (for custom-CSS edge cases) or the
|
|
1220
|
+
// terminal theme background (last resort).
|
|
1221
|
+
const root = document.documentElement
|
|
1222
|
+
const cssMain = root && window.getComputedStyle
|
|
1223
|
+
? window.getComputedStyle(root).getPropertyValue('--main').trim()
|
|
1151
1224
|
: ''
|
|
1152
1225
|
return uiThemeConfig.main || cssMain || this.props.themeConfig.background
|
|
1153
1226
|
}
|
|
@@ -1182,6 +1255,32 @@ class Term extends Component {
|
|
|
1182
1255
|
)
|
|
1183
1256
|
}
|
|
1184
1257
|
|
|
1258
|
+
/**
|
|
1259
|
+
* Apply the current renderer theme to the terminal and trigger a repaint.
|
|
1260
|
+
* When `deferred` is true (WebGL mode), a second repaint is scheduled on
|
|
1261
|
+
* the next animation frame so the theme picks up CSS --main changes that
|
|
1262
|
+
* UiTheme's useEffect applies asynchronously after componentDidUpdate.
|
|
1263
|
+
* The optional `term` parameter is used during initTerminal, where
|
|
1264
|
+
* this.term hasn't been assigned yet.
|
|
1265
|
+
*/
|
|
1266
|
+
applyTerminalTheme = (deferred = false, term = this.term) => {
|
|
1267
|
+
if (!term || this.onClose) {
|
|
1268
|
+
return
|
|
1269
|
+
}
|
|
1270
|
+
term.options.theme = this.getRendererThemeConfig(this.props.themeConfig)
|
|
1271
|
+
term.refresh(0, term.rows - 1)
|
|
1272
|
+
if (deferred && this.props.config.rendererType === rendererTypes.webGL) {
|
|
1273
|
+
window.cancelAnimationFrame(this.timers.themeRaf)
|
|
1274
|
+
this.timers.themeRaf = window.requestAnimationFrame(() => {
|
|
1275
|
+
if (!this.term || this.onClose) {
|
|
1276
|
+
return
|
|
1277
|
+
}
|
|
1278
|
+
this.term.options.theme = this.getRendererThemeConfig(this.props.themeConfig)
|
|
1279
|
+
this.term.refresh(0, this.term.rows - 1)
|
|
1280
|
+
})
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1185
1284
|
initTerminal = async () => {
|
|
1186
1285
|
const { themeConfig, tab = {}, config = {} } = this.props
|
|
1187
1286
|
const tc = this.getRendererThemeConfig(themeConfig)
|
|
@@ -1205,6 +1304,14 @@ class Term extends Component {
|
|
|
1205
1304
|
term.open(this.domRef.current, true)
|
|
1206
1305
|
this.registerTerminalColorQueryHandlers(term, themeConfig)
|
|
1207
1306
|
await this.loadRenderer(term, config)
|
|
1307
|
+
// Re-apply the theme after the renderer is loaded. The Terminal was
|
|
1308
|
+
// constructed before UiTheme's useEffect ran, so the initial theme
|
|
1309
|
+
// may have a stale background. Pass `term` directly because this.term
|
|
1310
|
+
// is not assigned yet. Use deferred=true so a second repaint picks up
|
|
1311
|
+
// any CSS --main changes applied by UiTheme's useEffect.
|
|
1312
|
+
if (config.rendererType === rendererTypes.webGL) {
|
|
1313
|
+
this.applyTerminalTheme(true, term)
|
|
1314
|
+
}
|
|
1208
1315
|
|
|
1209
1316
|
const FitAddon = await loadFitAddon()
|
|
1210
1317
|
this.fitAddon = new FitAddon()
|
package/client/css/mobile.styl
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
@media (max-width: 800px)
|
|
5
|
-
.w800
|
|
6
|
-
display none !important
|
|
7
|
-
.l800
|
|
8
|
-
display inline !important
|
|
1
|
+
// Session pane tab label: show full name by default, short label on mobile
|
|
2
|
+
.type-tab-short
|
|
3
|
+
display none
|
|
9
4
|
|
|
10
5
|
// ---------------------------------------------------------------------------
|
|
11
6
|
// Mobile UI driven by store.isMobile (is-mobile class on root)
|
|
12
|
-
// Breakpoint:
|
|
7
|
+
// Breakpoint: 600px (see mobileBreakpoint in constants.js)
|
|
13
8
|
// ---------------------------------------------------------------------------
|
|
14
9
|
.is-mobile
|
|
15
10
|
// ---- 1. Tabs: show only current tab + dropdown, close icon always visible
|
|
@@ -151,3 +146,9 @@
|
|
|
151
146
|
// matching left gap so it never extends past the screen edge.
|
|
152
147
|
.upgrade-panel
|
|
153
148
|
max-width calc(100vw - 20px)
|
|
149
|
+
|
|
150
|
+
// ---- 9. Session pane tab label: hide full name, show short label
|
|
151
|
+
.type-tab-full
|
|
152
|
+
display none
|
|
153
|
+
.type-tab-short
|
|
154
|
+
display inline
|