@electerm/electerm-react 3.15.152 → 3.15.153

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.
@@ -197,6 +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
201
  export const baseUpdateCheckUrls = [
201
202
  packInfo.homepage,
202
203
  'https://gitee.com/github-zxdong262/electerm/raw/gh-pages'
@@ -1,14 +1,23 @@
1
1
  /**
2
2
  * batch input module
3
+ *
4
+ * Unified click-to-open behavior for mobile and desktop:
5
+ * - click the compact trigger -> expand the textarea + tab-select panel
6
+ * - click outside the panel -> collapse it
7
+ * - press Enter in the textarea -> send `cmd` to all selected tabs
8
+ *
9
+ * On desktop the compact trigger is rendered as a read-only Input that only
10
+ * serves as a clickable affordance — it is never a real input. On mobile it
11
+ * is a small "B" button to save horizontal space. The open/close logic is
12
+ * identical for both.
3
13
  */
4
14
 
5
- import { Component } from 'react'
15
+ import { Component, createRef } from 'react'
6
16
  import {
7
- AutoComplete,
17
+ Button,
8
18
  Input
9
19
  } from 'antd'
10
20
  import {
11
- batchInputLsKey,
12
21
  terminalWebType,
13
22
  terminalRdpType,
14
23
  terminalVncType
@@ -24,97 +33,71 @@ export default class BatchInput extends Component {
24
33
  super(props)
25
34
  this.state = {
26
35
  cmd: '',
27
- open: false,
28
- enter: false
36
+ open: false
29
37
  }
38
+ this.outerRef = createRef()
39
+ this.textAreaRef = createRef()
40
+ }
41
+
42
+ componentDidMount () {
43
+ document.addEventListener('mousedown', this.handleDocClick)
30
44
  }
31
45
 
32
46
  componentWillUnmount () {
33
- clearTimeout(this.timer)
47
+ document.removeEventListener('mousedown', this.handleDocClick)
34
48
  }
35
49
 
36
- handleEnter = (e) => {
37
- const { batchInputSelectedTabIds } = window.store
38
- const { cmd } = this.state
39
- if (!cmd.trim()) {
50
+ // Collapse the panel when the user clicks anywhere outside of it.
51
+ // Clicks inside an antd Popover (e.g. the tab-select popover, which renders
52
+ // in a portal outside this node) are treated as "inside" so interacting with
53
+ // tab selection does not collapse the panel.
54
+ handleDocClick = (ev) => {
55
+ if (!this.state.open) {
40
56
  return
41
57
  }
42
- window.store.addBatchInput(cmd)
43
- this.props.input(cmd, Array.from(batchInputSelectedTabIds))
44
- this.setState({
45
- cmd: '',
46
- open: false
47
- })
48
- e.stopPropagation()
49
- }
50
-
51
- handleChange = (v = '') => {
52
- let vv = v.replace(/^\d+:/, '').replace(/\n$/, '')
53
- if (vv === batchInputLsKey) {
54
- window.store.clearBatchInput()
55
- vv = ''
58
+ const node = this.outerRef.current
59
+ if (node && node.contains(ev.target)) {
60
+ return
56
61
  }
57
- this.setState({
58
- cmd: vv,
59
- open: false
60
- })
61
- }
62
-
63
- handleClick = () => {
64
- this.setState({
65
- open: true
66
- })
67
- window.store.filterBatchInputSelectedTabIds()
68
- }
69
-
70
- handleBlur = () => {
71
- this.setState({
72
- open: false
73
- })
74
- }
75
-
76
- mapper = (v, i) => {
77
- return {
78
- value: `${i}:${v}`,
79
- label: v
62
+ if (ev.target && ev.target.closest && ev.target.closest('.ant-popover')) {
63
+ return
80
64
  }
65
+ this.setState({ open: false })
81
66
  }
82
67
 
83
- renderClear = () => {
84
- return {
85
- value: batchInputLsKey,
86
- label: e('clear')
68
+ handleToggle = () => {
69
+ const willOpen = !this.state.open
70
+ if (willOpen) {
71
+ window.store.filterBatchInputSelectedTabIds()
87
72
  }
73
+ this.setState({ open: willOpen }, () => {
74
+ if (willOpen && this.textAreaRef.current) {
75
+ this.textAreaRef.current.focus()
76
+ }
77
+ })
88
78
  }
89
79
 
90
- buildOptions = () => {
91
- const arr = this.props.batchInputs.map(this.mapper)
92
- if (arr.length) {
93
- return [
94
- ...arr,
95
- this.renderClear()
96
- ]
80
+ handleSubmit = (ev) => {
81
+ const { batchInputSelectedTabIds } = this.props
82
+ const { cmd } = this.state
83
+ if (!cmd.trim()) {
84
+ return
97
85
  }
98
- return []
99
- }
100
-
101
- handleMouseEnter = () => {
102
- clearTimeout(this.timer)
86
+ window.store.addBatchInput(cmd)
87
+ this.props.input(cmd, Array.from(batchInputSelectedTabIds))
103
88
  this.setState({
104
- enter: true
89
+ cmd: '',
90
+ open: false
105
91
  })
92
+ ev?.stopPropagation()
106
93
  }
107
94
 
108
- leave = () => {
95
+ handleChange = (ev) => {
109
96
  this.setState({
110
- enter: false
97
+ cmd: ev.target.value
111
98
  })
112
99
  }
113
100
 
114
- handleMouseLeave = () => {
115
- this.timer = setTimeout(this.leave, 5000)
116
- }
117
-
118
101
  getTabs = () => {
119
102
  const { activeTabId } = this.props
120
103
  return deepCopy(this.props.tabs.filter(tab => {
@@ -122,43 +105,54 @@ export default class BatchInput extends Component {
122
105
  tab.type !== terminalRdpType &&
123
106
  tab.type !== terminalVncType
124
107
  })).sort((a, b) => {
125
- // Current tab goes first
108
+ // current tab goes first
126
109
  if (a.id === activeTabId) return -1
127
110
  if (b.id === activeTabId) return 1
128
111
  return 0
129
112
  })
130
113
  }
131
114
 
132
- render () {
133
- const { cmd, open, enter } = this.state
134
- const {
135
- batchInputSelectedTabIds
136
- } = this.props
137
- const opts = {
138
- options: this.buildOptions(),
139
- value: cmd,
140
- onChange: this.handleChange,
141
- defaultOpen: false,
142
- open,
143
- className: 'batch-input-wrap'
115
+ renderTrigger () {
116
+ const { isMobile } = this.props
117
+ if (isMobile) {
118
+ return (
119
+ <Button
120
+ size='small'
121
+ type='text'
122
+ onClick={this.handleToggle}
123
+ >
124
+ B
125
+ </Button>
126
+ )
144
127
  }
128
+ // Desktop: an input-shaped, read-only affordance. It is NOT a real input —
129
+ // clicking it expands the panel where the user actually types.
130
+ return (
131
+ <Input
132
+ size='small'
133
+ readOnly
134
+ placeholder={e('batchInput')}
135
+ className='batch-input-holder'
136
+ style={{ cursor: 'pointer' }}
137
+ onClick={this.handleToggle}
138
+ />
139
+ )
140
+ }
141
+
142
+ render () {
143
+ const { open } = this.state
144
+ const { batchInputSelectedTabIds } = this.props
145
145
  const cls = classNames(
146
146
  'batch-input-outer',
147
- {
148
- 'bi-show': open || enter
149
- }
147
+ { 'bi-show': open }
150
148
  )
151
- const inputProps = {
152
- size: 'small',
153
- placeholder: e('batchInput'),
154
- className: 'batch-input-holder'
155
- }
156
149
  const textAreaProps = {
157
- onPressEnter: this.handleEnter,
158
- onClick: this.handleClick,
159
- onBlur: this.handleBlur,
150
+ ref: this.textAreaRef,
151
+ onPressEnter: this.handleSubmit,
152
+ onChange: this.handleChange,
153
+ value: this.state.cmd,
160
154
  size: 'small',
161
- autoSize: { minRows: 1 },
155
+ autoSize: { minRows: 2, maxRows: 6 },
162
156
  placeholder: e('batchInput'),
163
157
  allowClear: true
164
158
  }
@@ -173,22 +167,15 @@ export default class BatchInput extends Component {
173
167
  return (
174
168
  <span
175
169
  className={cls}
176
- onMouseEnter={this.handleMouseEnter}
177
- onMouseLeave={this.handleMouseLeave}
170
+ ref={this.outerRef}
178
171
  >
179
172
  <span className='bi-compact'>
180
- <Input
181
- {...inputProps}
182
- />
173
+ {this.renderTrigger()}
183
174
  </span>
184
175
  <span className='bi-full'>
185
- <AutoComplete
186
- {...opts}
187
- >
188
- <Input.TextArea
189
- {...textAreaProps}
190
- />
191
- </AutoComplete>
176
+ <Input.TextArea
177
+ {...textAreaProps}
178
+ />
192
179
  <TabSelect {...tabSelectProps} />
193
180
  </span>
194
181
  </span>
@@ -1,8 +1,9 @@
1
1
  import { auto } from 'manate/react'
2
2
  import {
3
- Select
3
+ Select,
4
+ Dropdown
4
5
  } from 'antd'
5
- import { InfoCircleOutlined } from '@ant-design/icons'
6
+ import { InfoCircleOutlined, TranslationOutlined } from '@ant-design/icons'
6
7
  import './footer.styl'
7
8
  import { statusMap } from '../../common/constants'
8
9
  import BatchInput from './batch-input'
@@ -54,9 +55,9 @@ export default auto(function FooterEntry (props) {
54
55
  const batchProps = {
55
56
  input: batchInput,
56
57
  tabs: store.tabs,
57
- batchInputs: store.batchInputs,
58
58
  batchInputSelectedTabIds: store.batchInputSelectedTabIds,
59
- activeTabId: store.activeTabId
59
+ activeTabId: store.activeTabId,
60
+ isMobile: store.isMobile
60
61
  }
61
62
  return (
62
63
  <div className='terminal-footer-unit terminal-footer-center'>
@@ -96,6 +97,28 @@ export default auto(function FooterEntry (props) {
96
97
  size: 'small',
97
98
  popupMatchSelectWidth: false
98
99
  }
100
+ if (props.store.isMobile) {
101
+ const items = encodes.map(k => {
102
+ return {
103
+ key: k,
104
+ label: k.toUpperCase(),
105
+ onClick: () => handleSwitchEncoding(k)
106
+ }
107
+ })
108
+ return (
109
+ <div className='terminal-footer-unit terminal-footer-info'>
110
+ <Dropdown
111
+ menu={{ items }}
112
+ placement='topRight'
113
+ trigger={['click']}
114
+ >
115
+ <TranslationOutlined
116
+ className='pointer font18 mobile-encode-trigger'
117
+ />
118
+ </Dropdown>
119
+ </div>
120
+ )
121
+ }
99
122
  return (
100
123
  <div className='terminal-footer-unit terminal-footer-info'>
101
124
  <div className='fleft relative'>
@@ -39,22 +39,26 @@
39
39
  // margin so the history icon stays roughly equidistant from AI and Q
40
40
  margin-left 3px
41
41
  .batch-input-outer
42
+ position relative
42
43
  .bi-full
43
44
  display none
44
45
  &.bi-show
45
- line-height 20px
46
- .bi-full
47
- display inline
46
+ // hide the trigger while the popup is open so we don't show two
47
+ // "batch input" fields at once (desktop trigger is an input-shaped affordance)
48
48
  .bi-compact
49
49
  display none
50
+ .bi-full
51
+ display flex
52
+ flex-direction column
53
+ gap 6px
54
+ position absolute
55
+ bottom 100%
56
+ left 0
57
+ z-index 1000
58
+ background var(--main)
59
+ padding 8px 10px
60
+ box-shadow 0 -2px 8px rgba(0, 0, 0, 0.2)
61
+ border-radius 4px 4px 0 0
62
+ min-width 360px
50
63
  .batch-input-holder
51
- width 100px
52
- @media (max-width: 500px)
53
- .batch-input-outer
54
- &.bi-show
55
- position fixed
56
- left 100px
57
- right 0
58
- bottom 0
59
- z-index 100
60
- background var(--main)
64
+ width 100px
@@ -118,7 +118,8 @@ export default auto(function Layout (props) {
118
118
  'leftSidebarWidth',
119
119
  'addPanelWidth',
120
120
  'pinned',
121
- 'openedSideBar'
121
+ 'openedSideBar',
122
+ 'isMobile'
122
123
  ])
123
124
  }
124
125
  return (
@@ -113,7 +113,9 @@ export default auto(function Index (props) {
113
113
  'not-win': !isWin,
114
114
  'qm-pinned': pinnedQuickCommandBar,
115
115
  fullscreen,
116
- 'is-main': !isSecondInstance
116
+ 'is-main': !isSecondInstance,
117
+ 'is-mobile': store.isMobile,
118
+ 'is-desktop': !store.isMobile
117
119
  })
118
120
  const ext1 = {
119
121
  className: cls
@@ -111,7 +111,7 @@ export default class QuickCommandsItem extends PureComponent {
111
111
  title={this.renderTooltip()}
112
112
  placement='top'
113
113
  mouseEnterDelay={0.5}
114
- overlayClassName='qm-tooltip-overlay'
114
+ classNames={{ root: 'qm-tooltip-overlay' }}
115
115
  >
116
116
  <Button
117
117
  key={id}
@@ -594,6 +594,9 @@ export default class SessionWrapper extends Component {
594
594
  }
595
595
 
596
596
  renderSplitToggle = () => {
597
+ if (window.store.isMobile) {
598
+ return null
599
+ }
597
600
  if (!this.canSplitView() || this.isNotTerminalType()) {
598
601
  return null
599
602
  }
@@ -673,8 +676,8 @@ export default class SessionWrapper extends Component {
673
676
  onClick={() => this.onChangePane(types[i])}
674
677
  >
675
678
  <span className='type-tab-txt'>
676
- <span className='w500'>{e(type)}</span>
677
- <span className='l500'>{simpleMapper[type]}</span>
679
+ <span className='w800'>{e(type)}</span>
680
+ <span className='l800'>{simpleMapper[type]}</span>
678
681
  <span className='type-tab-line' />
679
682
  </span>
680
683
  </span>
@@ -182,6 +182,7 @@ export default auto(function SettingModalWrap (props) {
182
182
  visible={show}
183
183
  useSystemTitleBar={useSystemTitleBar}
184
184
  innerWidth={innerWidth}
185
+ isMobile={props.store.isMobile}
185
186
  >
186
187
  {renderTabs()}
187
188
  </SettingModal>
@@ -16,6 +16,18 @@ export default class SettingWrap extends Component {
16
16
  )
17
17
  }
18
18
 
19
+ renderRightClose () {
20
+ if (this.props.isMobile) {
21
+ return null
22
+ }
23
+ return (
24
+ <CloseCircleOutlined
25
+ className='close-setting-wrap-icon close-setting-wrap'
26
+ onClick={this.props.onCancel}
27
+ />
28
+ )
29
+ }
30
+
19
31
  render () {
20
32
  const pops = {
21
33
  open: this.props.visible,
@@ -29,10 +41,7 @@ export default class SettingWrap extends Component {
29
41
  <Drawer
30
42
  {...pops}
31
43
  >
32
- <CloseCircleOutlined
33
- className='close-setting-wrap-icon close-setting-wrap'
34
- onClick={this.props.onCancel}
35
- />
44
+ {this.renderRightClose()}
36
45
  <CloseCircleOutlined
37
46
  className='close-setting-wrap alt-close-setting-wrap'
38
47
  onClick={this.props.onCancel}
@@ -37,7 +37,7 @@ export default memo(function TransferList (props) {
37
37
  const popProps = {
38
38
  placement: 'right',
39
39
  destroyOnHidden: true,
40
- overlayClassName: 'transfer-list-card',
40
+ classNames: { root: 'transfer-list-card' },
41
41
  content: <TransferModal {...transferModalProps} />
42
42
  }
43
43
  return (
@@ -83,6 +83,9 @@ export default class Tabs extends Component {
83
83
  }
84
84
 
85
85
  isOverflow = () => {
86
+ if (this.props.isMobile || !this.domRef.current) {
87
+ return false
88
+ }
86
89
  const { tabs = [] } = this.props
87
90
  const len = tabs.length
88
91
  const addBtnWidth = 22
@@ -120,6 +123,9 @@ export default class Tabs extends Component {
120
123
  }
121
124
 
122
125
  adjustScroll = () => {
126
+ if (this.props.isMobile || !this.domRef.current) {
127
+ return
128
+ }
123
129
  const { tabs, currentBatchTabId, batch } = this.props
124
130
  const index = tabs.findIndex(t => t.id === currentBatchTabId)
125
131
  const tabsDomWith = Array.from(
@@ -139,6 +145,9 @@ export default class Tabs extends Component {
139
145
  }
140
146
 
141
147
  handleScrollLeft = () => {
148
+ if (!this.domRef.current) {
149
+ return
150
+ }
142
151
  let { scrollLeft } = this.domRef.current
143
152
  scrollLeft = scrollLeft - tabMargin - tabWidth
144
153
  if (scrollLeft < 0) {
@@ -148,6 +157,9 @@ export default class Tabs extends Component {
148
157
  }
149
158
 
150
159
  handleScrollRight = () => {
160
+ if (!this.domRef.current) {
161
+ return
162
+ }
151
163
  let { scrollLeft } = this.domRef.current
152
164
  scrollLeft = scrollLeft + tabMargin + tabWidth
153
165
  if (scrollLeft < 0) {
@@ -349,8 +361,69 @@ export default class Tabs extends Component {
349
361
  return null
350
362
  }
351
363
 
364
+ renderMobileTabsDropdown (items) {
365
+ return (
366
+ <Dropdown
367
+ menu={{ items }}
368
+ placement='bottomRight'
369
+ trigger={['click']}
370
+ >
371
+ <DownOutlined className='tabs-dd-icon' />
372
+ </Dropdown>
373
+ )
374
+ }
375
+
376
+ renderMobileTabsExtra (items) {
377
+ return (
378
+ <div className='mobile-tabs-extra'>
379
+ {this.renderAddBtn()}
380
+ {
381
+ items && items.length
382
+ ? this.renderMobileTabsDropdown(items)
383
+ : null
384
+ }
385
+ </div>
386
+ )
387
+ }
388
+
389
+ renderMobileTabs () {
390
+ const { tabs = [], currentBatchTabId } = this.props
391
+ const currentTab = tabs.find(t => t.id === currentBatchTabId)
392
+ const items = tabs.map((t, i) => {
393
+ return {
394
+ key: i + '##' + t.id,
395
+ label: (
396
+ <span><TabTitle tab={t} /></span>
397
+ ),
398
+ onClick: () => this.handleClickMenu({ key: i + '##' + t.id })
399
+ }
400
+ })
401
+ const tabProps = {
402
+ ...this.props,
403
+ tab: currentTab,
404
+ isLast: true,
405
+ addTab: this.handleTabAdd,
406
+ tabIndex: 0
407
+ }
408
+ const currentTabEl = currentTab
409
+ ? <Tab {...tabProps} key={currentTab.id} />
410
+ : null
411
+ return (
412
+ <div className='tabs mobile-tabs' ref={this.tabsRef}>
413
+ <div className='tabs-inner mobile-tabs-inner'>
414
+ {currentTabEl}
415
+ </div>
416
+ {this.renderMobileTabsExtra(items)}
417
+ {this.renderWindowControl()}
418
+ </div>
419
+ )
420
+ }
421
+
352
422
  renderTabs () {
353
423
  const { overflow } = this.state
424
+ if (this.props.isMobile) {
425
+ return this.renderMobileTabs()
426
+ }
354
427
  return (
355
428
  <div className='tabs' ref={this.tabsRef}>
356
429
  {this.renderContent()}
@@ -1,8 +1,102 @@
1
- .l500
1
+ .l800
2
2
  display none !important
3
3
 
4
- @media (max-width: 500px)
5
- .w500
4
+ @media (max-width: 800px)
5
+ .w800
6
6
  display none !important
7
- .l500
8
- display inline !important
7
+ .l800
8
+ display inline !important
9
+
10
+ // ---------------------------------------------------------------------------
11
+ // Mobile UI driven by store.isMobile (is-mobile class on root)
12
+ // Breakpoint: 800px (see mobileBreakpoint in constants.js)
13
+ // ---------------------------------------------------------------------------
14
+ .is-mobile
15
+ // ---- 1. Tabs: show only current tab + dropdown, close icon always visible
16
+ .tabs.mobile-tabs
17
+ display flex
18
+ align-items center
19
+ overflow hidden
20
+ // leave room for window controls on desktop platforms resized narrow
21
+ &.not-system-ui.not-mac.not-webapp
22
+ .tabs.mobile-tabs
23
+ padding-right 94px
24
+ .mobile-tabs-inner
25
+ flex 1
26
+ min-width 0
27
+ overflow hidden
28
+ display flex
29
+ align-items center
30
+ margin-left 0 !important
31
+ width auto !important
32
+ .tab
33
+ flex 1
34
+ max-width none
35
+ min-width 0
36
+ width auto
37
+ text-align left
38
+ // close icon always visible on mobile
39
+ .tab-close
40
+ display block
41
+ .mobile-tabs-extra
42
+ flex-shrink 0
43
+ display flex
44
+ align-items center
45
+ padding 0 10px
46
+ line-height 36px
47
+ z-index 20
48
+ .tabs-add-btn
49
+ margin 0
50
+ &.empty
51
+ font-size 20px
52
+ margin 0
53
+ .tabs-dd-icon
54
+ margin-left 8px
55
+ font-size 14px
56
+ color var(--text)
57
+ &:hover
58
+ color var(--text-light)
59
+ // tabs-extra must not render on mobile (defensive css too)
60
+ .tabs-extra
61
+ display none !important
62
+
63
+ // ---- 2. Footer batch input: show "B" button, click to expand
64
+ // match the quick-commands "Q" button: no flex-grow, same small margin
65
+ .terminal-footer-center
66
+ flex-grow 0
67
+ margin-left 3px
68
+ .batch-input-outer
69
+ &.bi-show
70
+ .bi-full
71
+ // full-width fixed popup above the footer (mobile is narrow)
72
+ position fixed
73
+ left 0
74
+ right 0
75
+ bottom 36px
76
+ z-index 200
77
+ background var(--main)
78
+ padding 8px 10px
79
+ box-shadow 0 -2px 8px rgba(0, 0, 0, 0.2)
80
+ width auto
81
+ min-width 0
82
+ border-radius 0
83
+
84
+ // ---- 3. Footer encode select: show icon, click to open dropdown
85
+ // match antd Button small icon size (~14px) used by sibling icons
86
+ .mobile-encode-trigger,
87
+ .terminal-info-icon
88
+ font-size 14px !important
89
+ line-height 36px
90
+ padding 0 4px
91
+ &:hover
92
+ color var(--text-light)
93
+
94
+ // ---- 4. Modal: full width / full bleed on mobile
95
+ .custom-modal-container
96
+ padding 0
97
+ .custom-modal-content
98
+ width 100% !important
99
+ max-width 100vw
100
+ margin-top 0
101
+ min-height 100vh
102
+ border-radius 0
@@ -11,7 +11,9 @@ import {
11
11
  addPanelWidthLsKey,
12
12
  dismissDelKeyTipLsKey,
13
13
  connectionMap,
14
- lastAiChatSessionIdKey
14
+ lastAiChatSessionIdKey,
15
+ mobileBreakpoint,
16
+ splitMap
15
17
  } from '../common/constants'
16
18
  import * as ls from '../common/safe-local-storage'
17
19
  import { refs, refsStatic } from '../components/common/ref'
@@ -58,14 +60,21 @@ export default Store => {
58
60
  Store.prototype.onResize = debounce(async function () {
59
61
  const { width, height } = await window.pre.runGlobalAsync('getScreenSize')
60
62
  const isMaximized = window.pre.runSync('isMaximized')
63
+ const w = window.innerWidth
64
+ const isMobile = w <= mobileBreakpoint
61
65
  const update = {
62
66
  height: window.innerHeight,
63
- innerWidth: window.innerWidth,
67
+ innerWidth: w,
64
68
  screenWidth: width,
65
69
  screenHeight: height,
66
- isMaximized
70
+ isMaximized,
71
+ isMobile
67
72
  }
68
73
  window.store.storeAssign(update)
74
+ // Force single-column layout on mobile
75
+ if (isMobile && window.store.layout !== splitMap.c1) {
76
+ window.store.setLayout(splitMap.c1)
77
+ }
69
78
  window.pre.runGlobalAsync('setWindowSize', {
70
79
  ...update,
71
80
  height: window.outerHeight
@@ -23,7 +23,8 @@ import {
23
23
  resolutionsLsKey,
24
24
  syncServerDataKey,
25
25
  splitMap,
26
- lastAiChatSessionIdKey
26
+ lastAiChatSessionIdKey,
27
+ mobileBreakpoint
27
28
  } from '../common/constants'
28
29
  import * as ls from '../common/safe-local-storage'
29
30
  import { exclude } from 'manate'
@@ -200,6 +201,7 @@ export default () => {
200
201
  height: 500,
201
202
  isMaximized: window.pre.runSync('isMaximized'),
202
203
  hasNodePty: window.pre.runSync('nodePtyCheck'),
204
+ isMobile: window.innerWidth <= mobileBreakpoint,
203
205
  fullscreen: false,
204
206
  hideDelKeyTip: ls.getItem(dismissDelKeyTipLsKey) === 'y',
205
207
  tabsHeight: 36,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electerm/electerm-react",
3
- "version": "3.15.152",
3
+ "version": "3.15.153",
4
4
  "description": "react components src for electerm",
5
5
  "main": "./client/components/main/main.jsx",
6
6
  "license": "MIT",