@electerm/electerm-react 5.5.25 → 5.5.35

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.
Files changed (41) hide show
  1. package/README.md +5 -5
  2. package/client/common/bookmark-schemas.js +3 -0
  3. package/client/common/constants.js +2 -0
  4. package/client/common/create-title.jsx +5 -1
  5. package/client/common/default-setting.js +2 -0
  6. package/client/common/parse-vv.js +119 -25
  7. package/client/common/vv-to-tab.js +120 -0
  8. package/client/components/ai/agent-tool-call-card.jsx +19 -4
  9. package/client/components/ai/agent.js +33 -16
  10. package/client/components/ai/ai-chat-history-item.jsx +12 -48
  11. package/client/components/ai/ai-chat.jsx +82 -25
  12. package/client/components/ai/ai-config-props.js +12 -1
  13. package/client/components/ai/ai-config.jsx +14 -0
  14. package/client/components/ai/ai-context-indicator.jsx +83 -0
  15. package/client/components/ai/ai-context.js +272 -0
  16. package/client/components/ai/ai-output.jsx +4 -0
  17. package/client/components/ai/ai-presets.js +1 -1
  18. package/client/components/ai/ai.styl +105 -6
  19. package/client/components/ai/copy-icon.jsx +31 -0
  20. package/client/components/bookmark-form/common/vv-file-field.jsx +3 -2
  21. package/client/components/bookmark-form/config/spice.js +21 -0
  22. package/client/components/footer/cmd-history.jsx +279 -65
  23. package/client/components/footer/cmd-history.styl +68 -1
  24. package/client/components/footer/footer-entry.jsx +7 -1
  25. package/client/components/layout/layout.jsx +12 -19
  26. package/client/components/main/main.jsx +14 -1
  27. package/client/components/quick-commands/qm.styl +58 -0
  28. package/client/components/quick-commands/quick-commands-box.jsx +180 -83
  29. package/client/components/quick-commands/quick-commands-select.jsx +11 -0
  30. package/client/components/setting-panel/setting-common.jsx +2 -0
  31. package/client/components/side-panel-r/side-panel-r.jsx +11 -4
  32. package/client/components/sidebar/bookmark-select.jsx +14 -2
  33. package/client/components/sidebar/history-item.jsx +20 -4
  34. package/client/components/sidebar/history.jsx +7 -0
  35. package/client/components/tree-list/tree-list.jsx +31 -3
  36. package/client/store/common.js +120 -32
  37. package/client/store/init-state.js +8 -0
  38. package/client/store/load-data.js +72 -0
  39. package/client/store/store.js +6 -0
  40. package/client/store/watch.js +3 -1
  41. package/package.json +1 -1
@@ -1,9 +1,20 @@
1
1
  .cmd-history-popover-content
2
+ position relative
2
3
  max-height 300px
3
4
  width 300px
4
- overflow hidden
5
+ // deliberately not clipped: the item action menu is a child of the panel, but
6
+ // it hangs below its row and may reach past the panel's edge
5
7
  display flex
6
8
  flex-direction column
9
+ // Docked in the right side panel the panel *is* the container, so the history
10
+ // fills it instead of sizing itself like a popup. The parent
11
+ // (.right-side-panel-content) is an absolutely positioned box with top and
12
+ // bottom set, so 100% resolves to a real height and the list scrolls inside
13
+ // it rather than scrolling the panel.
14
+ &.cmd-history-in-panel
15
+ width 100%
16
+ max-height none
17
+ height 100%
7
18
  .cmd-history-search
8
19
  flex-shrink 0
9
20
  position sticky
@@ -16,6 +27,19 @@
16
27
  justify-content space-between
17
28
  padding-bottom 8px
18
29
 
30
+ .cmd-history-header-actions
31
+ display flex
32
+ align-items center
33
+ gap 6px
34
+
35
+ // The footer trigger stays lit while the history is docked in the right panel,
36
+ // same contract as the info and AI icons
37
+ .cmd-history-trigger
38
+ &.active
39
+ color var(--success)
40
+ .anticon
41
+ color var(--success)
42
+
19
43
  .cmd-history-sort
20
44
  display flex
21
45
  align-items center
@@ -24,6 +48,13 @@
24
48
  .cmd-history-sort-label
25
49
  user-select none
26
50
 
51
+ .cmd-history-move-icon
52
+ font-size 14px
53
+ padding 4px
54
+ cursor pointer
55
+ &:hover
56
+ color var(--primary)
57
+
27
58
  .cmd-history-clear-icon
28
59
  font-size 14px
29
60
  padding 4px
@@ -77,6 +108,8 @@
77
108
  .cmd-history-item-more
78
109
  cursor pointer
79
110
  padding 2px 4px
111
+ &.active
112
+ color var(--primary)
80
113
 
81
114
  .cmd-history-item-copy
82
115
  cursor pointer
@@ -84,6 +117,40 @@
84
117
  &:hover
85
118
  color blue
86
119
 
120
+ // The item action menu is part of this panel: it is rendered inside
121
+ // `.cmd-history-popover-content` (not in a body-level portal) so that it can
122
+ // never outlive the popover. It hangs below the row whose ⋯ icon was clicked —
123
+ // the component lifts it above that row when it would fall off the window.
124
+ .cmd-history-item-menu
125
+ position absolute
126
+ right 8px
127
+ z-index 10
128
+ min-width 156px
129
+ padding 4px 0
130
+ border-radius 4px
131
+ background var(--main)
132
+ border 1px solid var(--main-darker)
133
+ box-shadow 0 2px 8px rgba(0, 0, 0, 0.35)
134
+
135
+ .cmd-history-menu-item
136
+ display flex
137
+ align-items center
138
+ gap 8px
139
+ padding 5px 10px
140
+ cursor pointer
141
+ white-space nowrap
142
+ &:hover
143
+ background var(--main-lighter)
144
+ &.danger
145
+ color var(--error)
146
+
147
+ .cmd-history-menu-icon
148
+ font-size 13px
149
+ line-height 1
150
+
151
+ .cmd-history-menu-label
152
+ flex 1
153
+
87
154
  .multi-tab-run-cmd
88
155
  font-size 12px
89
156
  padding 6px 8px
@@ -72,9 +72,15 @@ export default auto(function FooterEntry (props) {
72
72
  }
73
73
 
74
74
  function renderQuickCommands () {
75
+ const { rightPanelVisible, rightPanelTab, quickCommandsInRightPanel } = props.store
76
+ // the Q button only owns the right panel while the panel lives there; in
77
+ // the footer it toggles the floating box instead, which shows its own state
78
+ const active = quickCommandsInRightPanel &&
79
+ rightPanelVisible &&
80
+ rightPanelTab === 'quickCommands'
75
81
  return (
76
82
  <div className='terminal-footer-unit terminal-footer-qm'>
77
- <Qm />
83
+ <Qm active={active} />
78
84
  </div>
79
85
  )
80
86
  }
@@ -37,6 +37,7 @@ export default auto(function Layout (props) {
37
37
  height,
38
38
  isMobile,
39
39
  pinnedQuickCommandBar,
40
+ quickCommandsInRightPanel,
40
41
  leftSidePanelWidth,
41
42
  leftSideBarWidth,
42
43
  pinned,
@@ -49,7 +50,14 @@ export default auto(function Layout (props) {
49
50
  shortcutBarKbOffset
50
51
  } = props.store
51
52
  const monitorHeight = isRemoteMonitorBarVisible(props.store) ? remoteMonitorBarHeight : 0
52
- const h = height - footerHeight - monitorHeight - (inActiveTerminal && pinnedQuickCommandBar ? quickCommandBoxHeight : 0) - (shortcutBarVisible ? shortcutBarHeight + shortcutBarKbOffset : 0) + resizeTrigger
53
+ // a pin left on while the panel is docked in the right side panel reserves
54
+ // nothing: the footer box is not rendered then (quick-commands-box.jsx)
55
+ const qmPinnedHeight = inActiveTerminal &&
56
+ pinnedQuickCommandBar &&
57
+ !quickCommandsInRightPanel
58
+ ? quickCommandBoxHeight
59
+ : 0
60
+ const h = height - footerHeight - monitorHeight - qmPinnedHeight - (shortcutBarVisible ? shortcutBarHeight + shortcutBarKbOffset : 0) + resizeTrigger
53
61
  const l = pinned && !isMobile ? leftSideBarWidth + leftSidePanelWidth : leftSideBarWidth
54
62
  const r = rightPanelVisible && rightPanelPinned && !isMobile ? rightPanelWidth : 0
55
63
  return {
@@ -141,24 +149,9 @@ export default auto(function Layout (props) {
141
149
  const footerProps = {
142
150
  store
143
151
  }
144
- const qmProps = pick(store, [
145
- 'quickCommandTags',
146
- 'qmSortByFrequency',
147
- 'openQuickCommandBar',
148
- 'pinnedQuickCommandBar',
149
- 'qmSortByFrequency',
150
- 'inActiveTerminal',
151
- 'leftSidePanelWidth',
152
- 'leftSideBarWidth',
153
- 'openedSideBar',
154
- 'currentQuickCommands',
155
- // the quick-command popup mirrors the footer's horizontal extent, so it
156
- // needs the same right-panel state the footer uses
157
- 'rightPanelVisible',
158
- 'rightPanelPinned',
159
- 'rightPanelWidth',
160
- 'isMobile'
161
- ])
152
+ const qmProps = {
153
+ store
154
+ }
162
155
  const sessionsProps = {
163
156
  styles: styles.wrapStyles,
164
157
  sizes,
@@ -29,6 +29,8 @@ import { isAIDisabled } from '../../common/ai-feature'
29
29
  import { ConfigProvider } from 'antd'
30
30
  import { NotificationContainer } from '../common/notification'
31
31
  import RightSidePanel from '../side-panel-r/side-panel-r'
32
+ import CmdHistory from '../footer/cmd-history'
33
+ import QuickCommandsFooterBox from '../quick-commands/quick-commands-box'
32
34
  import ConnectionHoppingWarning from './connection-hopping-warnning'
33
35
  import SshConfigLoadNotify from '../ssh-config/ssh-config-load-notify'
34
36
  import LoadSshConfigs from '../ssh-config/load-ssh-configs'
@@ -94,6 +96,7 @@ export default auto(function Index (props) {
94
96
  store.checkForDbUpgrade()
95
97
  store.handleGetSerials()
96
98
  store.checkPendingDeepLink()
99
+ store.checkPendingVvFile()
97
100
  }, [])
98
101
 
99
102
  // Track the actual input modality rather than a static capability probe:
@@ -128,6 +131,7 @@ export default auto(function Index (props) {
128
131
  pinned,
129
132
  isSecondInstance,
130
133
  pinnedQuickCommandBar,
134
+ quickCommandsInRightPanel,
131
135
  installSrc,
132
136
  fileTransfers,
133
137
  uiThemeConfig,
@@ -155,7 +159,9 @@ export default auto(function Index (props) {
155
159
  // would restyle the mobile layout.
156
160
  pinned: pinned && !store.isMobile,
157
161
  'not-win': !isWin,
158
- 'qm-pinned': pinnedQuickCommandBar,
162
+ // the pinned quick-command box only shapes the layout while it is actually
163
+ // in the footer — docked in the right panel it renders there instead
164
+ 'qm-pinned': pinnedQuickCommandBar && !quickCommandsInRightPanel,
159
165
  fullscreen,
160
166
  // terminal fullscreen keeps the footer visible (rdp/vnc/spice fullscreen
161
167
  // does not — the footer would be an empty bar there)
@@ -282,6 +288,7 @@ export default auto(function Index (props) {
282
288
  showAIConfig: store.showAIConfig,
283
289
  rightPanelTab,
284
290
  agentRunning: store.agentRunning,
291
+ aiContextInfo: store.aiContextInfo,
285
292
  currentChatSessionId: store.currentChatSessionId,
286
293
  showChatSessions: store.showChatSessions
287
294
  }
@@ -358,6 +365,12 @@ export default auto(function Index (props) {
358
365
  <RightSidePanel {...rightPanelProps}>
359
366
  {!isAIDisabled() && <AIChat {...aiChatProps} />}
360
367
  <TerminalInfo key={store.activeTabId} store={store} {...deepCopy(store.terminalInfoProps)} />
368
+ {/* the cmd history panel has two homes; the footer popover is the
369
+ other one, and both are the same component (see cmd-history.jsx) */}
370
+ <CmdHistory store={store} inline />
371
+ {/* same for the quick command panel: the footer box is the other home
372
+ (see quick-commands-box.jsx) */}
373
+ <QuickCommandsFooterBox store={store} inline />
361
374
  </RightSidePanel>
362
375
  <SshConfigLoadNotify {...sshConfigProps} />
363
376
  <LoadSshConfigs
@@ -2,6 +2,58 @@
2
2
  max-height calc(100vh - 100px)
3
3
  overflow-y scroll
4
4
  padding 20px 0 10px 0
5
+
6
+ // Docked in the right side panel the panel *is* the container, so the quick
7
+ // commands fill it instead of sizing themselves like the footer box. The parent
8
+ // (.right-side-panel-content) is an absolutely positioned box with top and
9
+ // bottom set, so 100% resolves to a real height and the list scrolls inside it
10
+ // rather than scrolling the panel.
11
+ .qm-panel-in-right-panel
12
+ display flex
13
+ flex-direction column
14
+ width 100%
15
+ height 100%
16
+ > .pd2
17
+ display flex
18
+ flex-direction column
19
+ height 100%
20
+ min-height 0
21
+ width 100%
22
+ .qm-flex
23
+ flex-shrink 0
24
+ // the footer box is as wide as the footer; the right panel is not, and the
25
+ // search + label + sort + actions row does not fit in one line there
26
+ flex-wrap wrap
27
+ // ...so give the search a line of its own and let it fill it, instead of
28
+ // sitting at its footer width with a gap beside it. `flex-basis: 100%` is what
29
+ // forces the wrap; the controls then share the line below.
30
+ .qm-search-input
31
+ flex 1 1 100%
32
+ max-width none
33
+ .qm-list-wrap
34
+ flex 1
35
+ min-height 0
36
+ max-height none
37
+ overflow-y auto
38
+
39
+ .qm-panel-actions
40
+ display flex
41
+ align-items center
42
+ gap 6px
43
+
44
+ .qm-move-icon
45
+ font-size 14px
46
+ padding 4px
47
+ cursor pointer
48
+ &:hover
49
+ color var(--primary)
50
+
51
+ // The footer Q stays lit while the panel is docked in the right panel, same
52
+ // contract as the info, AI and cmd history icons
53
+ .quick-command-trigger
54
+ &.active
55
+ color var(--success)
56
+
5
57
  .qm-wrap-tooltip
6
58
  background var(--main)
7
59
  position absolute
@@ -80,6 +132,12 @@
80
132
  width 100%
81
133
  .qm-label-select
82
134
  width 100%
135
+ // The docked panel's search claims a whole row with `flex-basis: 100%`, which
136
+ // is a width only while `.qm-flex` is a row. Here it is a column, so the same
137
+ // basis would be 100% of the *height* and would push the controls off the
138
+ // panel. Same specificity as the rule above it, declared later, so it wins.
139
+ .qm-panel-in-right-panel .qm-search-input
140
+ flex none
83
141
 
84
142
  // On mobile, stack the quick-command row (name / delay / command / remove)
85
143
  // vertically instead of side by side — the horizontal Space.Compact layout
@@ -1,17 +1,29 @@
1
1
  /**
2
- * quick commands footer selection wrap
2
+ * quick commands panel
3
+ *
4
+ * The panel has two homes: the floating footer box (default) and the right side
5
+ * panel. Which one it is in is a stored preference
6
+ * (store.quickCommandsInRightPanel), so the same component renders both —
7
+ * `inline` drops the floating box and lets the right panel's own container wrap
8
+ * the panel body.
3
9
  */
4
10
 
5
11
  import { useState } from 'react'
6
- import { quickCommandLabelsLsKey, pinnedQuickCommandBarKey } from '../../common/constants'
7
- import { sortBy } from 'lodash-es'
12
+ import { auto } from 'manate/react'
13
+ import {
14
+ quickCommandLabelsLsKey,
15
+ pinnedQuickCommandBarKey
16
+ } from '../../common/constants'
17
+ import { sortBy, pick } from 'lodash-es'
8
18
  import { Button, Input, Select, Space, Flex } from 'antd'
9
19
  import * as ls from '../../common/safe-local-storage'
10
20
  import CmdItem from './quick-command-item'
11
21
  import {
12
22
  EditOutlined,
13
23
  CloseCircleOutlined,
14
- PushpinOutlined
24
+ PushpinOutlined,
25
+ VerticalLeftOutlined,
26
+ VerticalAlignBottomOutlined
15
27
  } from '@ant-design/icons'
16
28
  import classNames from 'classnames'
17
29
  import onDropFunc from './on-drop'
@@ -22,9 +34,42 @@ const e = window.translate
22
34
  const addQuickCommands = 'addQuickCommands'
23
35
  const { Option } = Select
24
36
 
25
- export default function QuickCommandsFooterBox (props) {
37
+ export default auto(function QuickCommandsFooterBox (props) {
38
+ const { store, inline } = props
26
39
  const [keyword, setKeyword] = useState('')
27
40
  const [label, setLabel] = useState(ls.getItem(quickCommandLabelsLsKey, ''))
41
+ // read through the store so the panel re-renders on its own: it is rendered
42
+ // in two different places (footer box and right panel), and only one of them
43
+ // gets props from the layout
44
+ const {
45
+ openQuickCommandBar,
46
+ pinnedQuickCommandBar,
47
+ qmSortByFrequency,
48
+ quickCommandsInRightPanel,
49
+ inActiveTerminal,
50
+ leftSidePanelWidth,
51
+ leftSideBarWidth,
52
+ openedSideBar,
53
+ rightPanelVisible,
54
+ rightPanelPinned,
55
+ rightPanelWidth,
56
+ isMobile
57
+ } = pick(store, [
58
+ 'openQuickCommandBar',
59
+ 'pinnedQuickCommandBar',
60
+ 'qmSortByFrequency',
61
+ 'quickCommandsInRightPanel',
62
+ 'inActiveTerminal',
63
+ 'leftSidePanelWidth',
64
+ 'leftSideBarWidth',
65
+ 'openedSideBar',
66
+ 'rightPanelVisible',
67
+ 'rightPanelPinned',
68
+ 'rightPanelWidth',
69
+ 'isMobile'
70
+ ])
71
+ const all = store.currentQuickCommands
72
+ const quickCommandTags = store.quickCommandTags
28
73
 
29
74
  function handleTogglePinned () {
30
75
  const current = !window.store.pinnedQuickCommandBar
@@ -58,12 +103,12 @@ export default function QuickCommandsFooterBox (props) {
58
103
  setLabel(v)
59
104
  }
60
105
 
61
- // function filterFunc (v, opt) {
62
- // const c = opt.props.children.toLowerCase()
63
- // const m = opt.props.cmd.toLowerCase()
64
- // const vv = v.toLowerCase()
65
- // return c.includes(vv) || m.includes(vv)
66
- // }
106
+ // Dock the panel into the right side panel. The footer box unmounts on the
107
+ // way out (it is gated on quickCommandsInRightPanel), so there is nothing to
108
+ // clean up here — unlike the cmd history popover, which has to be closed.
109
+ function handleMoveToRightPanel () {
110
+ window.store.moveQuickCommandsToRightPanel()
111
+ }
67
112
 
68
113
  function onDragOver (e) {
69
114
  e.preventDefault()
@@ -114,9 +159,6 @@ export default function QuickCommandsFooterBox (props) {
114
159
  }
115
160
 
116
161
  function renderItem (item) {
117
- const {
118
- qmSortByFrequency
119
- } = props
120
162
  return (
121
163
  <CmdItem
122
164
  item={item}
@@ -156,26 +198,69 @@ export default function QuickCommandsFooterBox (props) {
156
198
  })
157
199
  }
158
200
 
159
- const {
160
- openQuickCommandBar,
161
- pinnedQuickCommandBar,
162
- qmSortByFrequency,
163
- inActiveTerminal,
164
- leftSidePanelWidth,
165
- leftSideBarWidth,
166
- openedSideBar,
167
- rightPanelVisible,
168
- rightPanelPinned,
169
- rightPanelWidth,
170
- isMobile
171
- } = props
172
- if ((!openQuickCommandBar && !pinnedQuickCommandBar) || !inActiveTerminal) {
173
- return null
201
+ // The two "move" icons are mirror images and sit in the same slot: the footer
202
+ // box offers to dock the panel into the right side panel, the docked panel
203
+ // offers to hand it back to the footer. Same glyph pair as the cmd history
204
+ // panel (cmd-history.jsx), so the two panels read the same way.
205
+ // Note VerticalLeftOutlined is the one whose bar is on the right (arrow
206
+ // pointing right); VerticalAlignBottomOutlined points at the footer.
207
+ function renderMoveIcon () {
208
+ if (inline) {
209
+ return (
210
+ <VerticalAlignBottomOutlined
211
+ className='qm-move-icon pointer'
212
+ title={e('moveToFooter')}
213
+ onClick={() => window.store.moveQuickCommandsToFooter()}
214
+ />
215
+ )
216
+ }
217
+ return (
218
+ <VerticalLeftOutlined
219
+ className='qm-move-icon pointer'
220
+ title={e('moveToRightPanel')}
221
+ onClick={handleMoveToRightPanel}
222
+ />
223
+ )
174
224
  }
175
- const all = props.currentQuickCommands
176
- // if (!all.length) {
177
- // return renderNoCmd()
178
- // }
225
+
226
+ // Docked, the right panel owns the pin and the close button (both live in its
227
+ // title bar), so the panel keeps only the actions that are about the quick
228
+ // commands themselves. A pin left over from the footer is kept in the store
229
+ // and comes back with the panel, so nothing is silently lost.
230
+ function renderActions () {
231
+ const tp = pinnedQuickCommandBar
232
+ ? 'primary'
233
+ : 'text'
234
+ return (
235
+ <div className='qm-panel-actions mg2l'>
236
+ {renderMoveIcon()}
237
+ <Space.Compact>
238
+ {
239
+ !inline && (
240
+ <Button
241
+ onClick={handleTogglePinned}
242
+ icon={<PushpinOutlined />}
243
+ type={tp}
244
+ />
245
+ )
246
+ }
247
+ <Button
248
+ onClick={window.store.handleOpenQuickCommandsSetting}
249
+ icon={<EditOutlined />}
250
+ />
251
+ {
252
+ !inline && (
253
+ <Button
254
+ onClick={handleClose}
255
+ icon={<CloseCircleOutlined />}
256
+ />
257
+ )
258
+ }
259
+ </Space.Compact>
260
+ </div>
261
+ )
262
+ }
263
+
179
264
  const keyword0 = keyword.toLowerCase()
180
265
  const filtered = filterArray(all, keyword0, label)
181
266
  const sorted = qmSortByFrequency
@@ -188,9 +273,6 @@ export default function QuickCommandsFooterBox (props) {
188
273
  className: 'qm-label-select',
189
274
  allowClear: true
190
275
  }
191
- const tp = pinnedQuickCommandBar
192
- ? 'primary'
193
- : 'text'
194
276
  const cls = classNames('qm-list-wrap')
195
277
  const type = qmSortByFrequency ? 'primary' : 'default'
196
278
  // Mirrors the footer's left offset. Mobile reserves nothing for the side
@@ -214,56 +296,71 @@ export default function QuickCommandsFooterBox (props) {
214
296
  className: 'qm-wrap-tooltip',
215
297
  style: qmStyle
216
298
  }
299
+
300
+ const content = (
301
+ <div className='pd2'>
302
+ <Flex justify='space-between' className='qm-flex'>
303
+ <Input.Search
304
+ value={keyword}
305
+ onChange={handleChange}
306
+ placeholder=''
307
+ className='qm-search-input'
308
+ />
309
+ <Flex gap='small'>
310
+ <Select
311
+ {...sprops}
312
+ >
313
+ {quickCommandTags.map(
314
+ renderTag
315
+ )}
316
+ </Select>
317
+ <Button
318
+ type={type}
319
+ onClick={window.store.handleSortByFrequency}
320
+ >
321
+ {e('sortByFrequency')}
322
+ </Button>
323
+ </Flex>
324
+ {renderActions()}
325
+ </Flex>
326
+ <div className={cls}>
327
+ {sorted.map(renderItem)}
328
+ {
329
+ !sorted.length && renderNoCmd()
330
+ }
331
+ </div>
332
+ </div>
333
+ )
334
+
335
+ // docked in the right side panel: no floating box, no pin/close of its own —
336
+ // the right panel decides when this is on screen, via store.rightPanelTab
337
+ if (inline) {
338
+ if (store.rightPanelTab !== 'quickCommands') {
339
+ return null
340
+ }
341
+ return (
342
+ <div className='qm-panel-in-right-panel'>
343
+ {content}
344
+ </div>
345
+ )
346
+ }
347
+
348
+ // Dormant while the panel is docked: the box would otherwise float over the
349
+ // terminal with nothing to show for it, and a pin left on would keep
350
+ // reserving the strip of height it occupies (see layout.jsx).
351
+ if (
352
+ quickCommandsInRightPanel ||
353
+ (!openQuickCommandBar && !pinnedQuickCommandBar) ||
354
+ !inActiveTerminal
355
+ ) {
356
+ return null
357
+ }
358
+
217
359
  return (
218
360
  <div
219
361
  {...qmProps}
220
362
  >
221
- <div className='pd2'>
222
- <Flex justify='space-between' className='qm-flex'>
223
- <Input.Search
224
- value={keyword}
225
- onChange={handleChange}
226
- placeholder=''
227
- className='qm-search-input'
228
- />
229
- <Flex gap='small'>
230
- <Select
231
- {...sprops}
232
- >
233
- {props.quickCommandTags.map(
234
- renderTag
235
- )}
236
- </Select>
237
- <Button
238
- type={type}
239
- onClick={window.store.handleSortByFrequency}
240
- >
241
- {e('sortByFrequency')}
242
- </Button>
243
- </Flex>
244
- <Space.Compact className='mg2l'>
245
- <Button
246
- onClick={handleTogglePinned}
247
- icon={<PushpinOutlined />}
248
- type={tp}
249
- />
250
- <Button
251
- onClick={window.store.handleOpenQuickCommandsSetting}
252
- icon={<EditOutlined />}
253
- />
254
- <Button
255
- onClick={handleClose}
256
- icon={<CloseCircleOutlined />}
257
- />
258
- </Space.Compact>
259
- </Flex>
260
- <div className={cls}>
261
- {sorted.map(renderItem)}
262
- {
263
- !sorted.length && renderNoCmd()
264
- }
265
- </div>
266
- </div>
363
+ {content}
267
364
  </div>
268
365
  )
269
- }
366
+ })
@@ -5,6 +5,11 @@
5
5
  * on touch (mobile) and mouse (desktop) — the old hover-to-open model was
6
6
  * unreliable on Android, where a tap synthesizes mouseleave and cancels the
7
7
  * open timer.
8
+ *
9
+ * The panel has two homes (see quick-commands-box.jsx): the footer box and the
10
+ * right side panel. While it is docked, this button is repurposed to open and
11
+ * close that panel, exactly like the info and AI icons do — and `active` (fed
12
+ * by the footer, which is already reactive) keeps it lit while it is showing.
8
13
  */
9
14
 
10
15
  import { PureComponent } from 'react'
@@ -14,15 +19,21 @@ import './qm.styl'
14
19
  export default class QuickCommandsFooter extends PureComponent {
15
20
  handleClick = () => {
16
21
  const { store } = window
22
+ if (store.quickCommandsInRightPanel) {
23
+ store.toggleQuickCommandsPanel()
24
+ return
25
+ }
17
26
  store.openQuickCommandBar = !store.openQuickCommandBar
18
27
  }
19
28
 
20
29
  render () {
30
+ const { active } = this.props
21
31
  return (
22
32
  <div className='fleft relative quick-command-trigger-wrap'>
23
33
  <Button
24
34
  size='small'
25
35
  type='text'
36
+ className={'quick-command-trigger' + (active ? ' active' : '')}
26
37
  onClick={this.handleClick}
27
38
  >
28
39
  Q
@@ -574,7 +574,9 @@ export default class SettingCommon extends Component {
574
574
  'allowMultiInstance',
575
575
  'disableDeveloperTool',
576
576
  'switchTabOnHover',
577
+ 'doubleClickToOpenBookmark',
577
578
  'disableTabIndex',
579
+ 'onlyShowTitleInTab',
578
580
  'disableShortcutBar',
579
581
  'debug'
580
582
  ]