@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.
@@ -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 = 800
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='w800'>{e(type)}</span>
681
- <span className='l800'>{simpleMapper[type]}</span>
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>
@@ -92,7 +92,7 @@ export default () => {
92
92
  },
93
93
  {
94
94
  name: 'terminal_paste',
95
- shortcut: 'ctrl+shift+v',
95
+ shortcut: 'ctrl+v,ctrl+shift+v',
96
96
  shortcutMac: 'meta+v',
97
97
  readonly: true
98
98
  },
@@ -25,7 +25,7 @@ export default function NoSessionPanel ({ height, onNewTab, onNewSsh, batch }) {
25
25
  ? (
26
26
  <Button
27
27
  onClick={onNewTab}
28
- className='mg1r mg1b add-new-tab-btn'
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='pd1b'>
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')}
@@ -8,6 +8,13 @@
8
8
  right 0
9
9
  bottom 0
10
10
  z-index 11
11
+ .no-session-btns
12
+ display flex
13
+ flex-wrap wrap
14
+ justify-content center
15
+ align-items center
16
+ gap 8px
17
+ padding-bottom 8px
11
18
  .no-session-history
12
19
  position absolute
13
20
  top 320px
@@ -148,9 +148,14 @@ class Term extends Component {
148
148
  this.props.themeConfig,
149
149
  prevProps.themeConfig
150
150
  )
151
- if (themeChanged && this.term) {
152
- this.term.options.theme = this.getRendererThemeConfig(this.props.themeConfig)
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
- term.loadAddon(new WebglAddon())
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
- const dom = this.domRef.current
1149
- const cssMain = dom && window.getComputedStyle
1150
- ? window.getComputedStyle(dom).getPropertyValue('--main').trim()
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()
@@ -1,15 +1,10 @@
1
- .l800
2
- display none !important
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: 800px (see mobileBreakpoint in constants.js)
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electerm/electerm-react",
3
- "version": "3.15.167",
3
+ "version": "3.15.173",
4
4
  "description": "react components src for electerm",
5
5
  "main": "./client/components/main/main.jsx",
6
6
  "license": "MIT",