@electerm/electerm-react 2.8.16 → 2.10.26

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 (48) hide show
  1. package/client/common/constants.js +3 -3
  2. package/client/common/pre.js +1 -120
  3. package/client/components/ai/ai-config.jsx +26 -3
  4. package/client/components/ai/ai-history-item.jsx +46 -0
  5. package/client/components/ai/ai-history.jsx +104 -0
  6. package/client/components/bookmark-form/ai-bookmark-form.jsx +338 -0
  7. package/client/components/bookmark-form/bookmark-form.styl +1 -1
  8. package/client/components/bookmark-form/bookmark-schema.js +192 -0
  9. package/client/components/bookmark-form/common/ai-category-select.jsx +32 -0
  10. package/client/components/bookmark-form/common/category-select.jsx +2 -4
  11. package/client/components/bookmark-form/common/fields.jsx +0 -10
  12. package/client/components/bookmark-form/config/ftp.js +2 -0
  13. package/client/components/bookmark-form/config/rdp.js +0 -1
  14. package/client/components/bookmark-form/config/session-config.js +3 -1
  15. package/client/components/bookmark-form/config/spice.js +43 -0
  16. package/client/components/bookmark-form/config/vnc.js +1 -2
  17. package/client/components/bookmark-form/fix-bookmark-default.js +134 -0
  18. package/client/components/bookmark-form/index.jsx +74 -14
  19. package/client/components/common/notification.jsx +34 -2
  20. package/client/components/common/notification.styl +18 -2
  21. package/client/components/main/wrapper.styl +0 -7
  22. package/client/components/rdp/rdp-session.jsx +44 -11
  23. package/client/components/session/session.jsx +13 -3
  24. package/client/components/setting-panel/deep-link-control.jsx +3 -2
  25. package/client/components/setting-panel/keywords-transport.jsx +0 -1
  26. package/client/components/shortcuts/shortcut-editor.jsx +12 -36
  27. package/client/components/shortcuts/shortcut-handler.js +11 -5
  28. package/client/components/sidebar/index.jsx +11 -1
  29. package/client/components/spice/spice-session.jsx +296 -0
  30. package/client/components/spice/spice.styl +4 -0
  31. package/client/components/tabs/add-btn-menu.jsx +9 -2
  32. package/client/components/terminal/attach-addon-custom.js +20 -76
  33. package/client/components/terminal/terminal.jsx +34 -28
  34. package/client/components/terminal/transfer-client-base.js +232 -0
  35. package/client/components/terminal/trzsz-client.js +306 -0
  36. package/client/components/terminal/xterm-loader.js +109 -0
  37. package/client/components/terminal/zmodem-client.js +13 -166
  38. package/client/components/text-editor/simple-editor.jsx +1 -2
  39. package/client/components/vnc/vnc-session.jsx +1 -1
  40. package/client/entry/electerm.jsx +0 -2
  41. package/client/store/load-data.js +1 -1
  42. package/client/store/store.js +1 -1
  43. package/client/store/system-menu.js +10 -0
  44. package/package.json +1 -1
  45. package/client/common/trzsz.js +0 -46
  46. package/client/components/bookmark-form/common/wiki-alert.jsx +0 -9
  47. package/client/components/common/notification-with-details.jsx +0 -34
  48. package/client/components/terminal/fs.js +0 -59
@@ -14,4 +14,4 @@
14
14
  .item-item-use
15
15
  display inline-block
16
16
  .number-input
17
- min-width 210px
17
+ min-width 210px
@@ -0,0 +1,192 @@
1
+ const bookmarkSchema = {
2
+ ssh: {
3
+ type: 'ssh',
4
+ host: 'string (required) - hostname or IP address',
5
+ port: 'number (default: 22) - SSH port',
6
+ username: 'string (required) - SSH username',
7
+ password: 'string - password for authentication',
8
+ privateKey: 'string - private key content or path for key-based auth',
9
+ passphrase: 'string - passphrase for private key/cetificate',
10
+ certificate: 'string - certificate content',
11
+ authType: 'string - auth type (password|privateKey|profiles)',
12
+ profile: 'string - profile id to reuse saved auth',
13
+ title: 'string - bookmark title',
14
+ description: 'string - bookmark description',
15
+ startDirectoryRemote: 'string - remote starting directory',
16
+ startDirectoryLocal: 'string - local starting directory',
17
+ enableSsh: 'boolean - enable ssh, default is true',
18
+ enableSftp: 'boolean - enable sftp, default is true',
19
+ sshTunnels: 'array - ssh tunnel definitions (see sshTunnels items)',
20
+ connectionHoppings: 'array - connection hopping definitions',
21
+ useSshAgent: 'boolean - use SSH agent, default is true',
22
+ sshAgent: 'string - ssh agent path',
23
+ serverHostKey: 'array - server host key algorithms',
24
+ cipher: 'array - cipher list',
25
+ runScripts: 'array - run scripts after connected ({delay,script})',
26
+ quickCommands: 'array - quick commands ({name,command})',
27
+ proxy: 'string - proxy address (socks5://...)',
28
+ x11: 'boolean - enable x11 forwarding, default is false',
29
+ term: 'string - terminal type, default is xterm-256color, required',
30
+ displayRaw: 'boolean - display raw output, default is false',
31
+ encode: 'string - charset, default is utf8',
32
+ envLang: 'string - ENV LANG, default is en_US.UTF-8',
33
+ setEnv: 'string - environment variables, format: `KEY1=VALUE1 KEY2=VALUE2`',
34
+ color: 'string - tag color, like #000000',
35
+ interactiveValues: 'strings separated by newline'
36
+ },
37
+ sshTunnelsItem: {
38
+ sshTunnel: 'string - forwardRemoteToLocal|forwardLocalToRemote|dynamicForward',
39
+ sshTunnelLocalHost: 'string',
40
+ sshTunnelLocalPort: 'number',
41
+ sshTunnelRemoteHost: 'string',
42
+ sshTunnelRemotePort: 'number',
43
+ name: 'string - optional tunnel name'
44
+ },
45
+ connectionHoppingsItem: {
46
+ host: 'string',
47
+ port: 'number',
48
+ username: 'string',
49
+ password: 'string',
50
+ privateKey: 'string',
51
+ passphrase: 'string - passphrase',
52
+ certificate: 'string',
53
+ authType: 'string',
54
+ profile: 'string - profile id'
55
+ },
56
+ telnet: {
57
+ type: 'telnet',
58
+ host: 'string (required) - hostname or IP address',
59
+ port: 'number (default: 23) - Telnet port',
60
+ username: 'string - username',
61
+ password: 'string - password',
62
+ title: 'string - bookmark title',
63
+ description: 'string - bookmark description',
64
+ loginPrompt: 'string - login prompt regex',
65
+ passwordPrompt: 'string - password prompt regex',
66
+ runScripts: 'array - run scripts after connected ({delay,script})',
67
+ startDirectoryRemote: 'string - remote starting directory',
68
+ startDirectoryLocal: 'string - local starting directory',
69
+ profile: 'string - profile id',
70
+ proxy: 'string - proxy address (socks5://...)'
71
+ },
72
+ serial: {
73
+ type: 'serial',
74
+ path: 'string (required) - serial port path, e.g., /dev/ttyUSB0 or COM1',
75
+ baudRate: 'number (default: 9600) - baud rate',
76
+ dataBits: 'number (default: 8) - data bits',
77
+ stopBits: 'number (default: 1) - stop bits',
78
+ parity: 'string - "none", "even", "odd", "mark", "space"',
79
+ title: 'string - bookmark title',
80
+ rtscts: 'boolean - enable RTS/CTS flow control, default is false',
81
+ xon: 'boolean - enable XON flow control, default is false',
82
+ xoff: 'boolean - enable XOFF flow control, default is false',
83
+ xany: 'boolean - enable XANY flow control, default is false',
84
+ runScripts: 'array - run scripts after connected ({delay,script})',
85
+ description: 'string - bookmark description'
86
+ },
87
+ vnc: {
88
+ type: 'vnc',
89
+ host: 'string (required) - hostname or IP address',
90
+ port: 'number (default: 5900) - VNC port',
91
+ username: 'string - VNC username',
92
+ password: 'string - VNC password',
93
+ viewOnly: 'boolean - view only mode, default is false',
94
+ clipViewport: 'boolean - clip viewport to window, default is false',
95
+ scaleViewport: 'boolean - scale viewport to window, default is true',
96
+ qualityLevel: 'number (0-9) - VNC quality level, lower is faster, default is 3',
97
+ compressionLevel: 'number (0-9) - VNC compression level, lower is faster, default is 1',
98
+ shared: 'boolean - shared session, default is true',
99
+ proxy: 'string - proxy address (socks5://...)',
100
+ title: 'string - bookmark title',
101
+ description: 'string - bookmark description',
102
+ profile: 'string - profile id'
103
+ },
104
+ rdp: {
105
+ type: 'rdp',
106
+ host: 'string (required) - hostname or IP address',
107
+ port: 'number (default: 3389) - RDP port',
108
+ username: 'string - username',
109
+ password: 'string - password',
110
+ title: 'string - bookmark title',
111
+ description: 'string - bookmark description',
112
+ profile: 'string - profile id',
113
+ proxy: 'string - proxy address (socks5://...)',
114
+ domain: 'string - login domain'
115
+ },
116
+ ftp: {
117
+ type: 'ftp',
118
+ host: 'string (required) - hostname or IP address',
119
+ port: 'number (default: 21) - FTP port',
120
+ user: 'string - username',
121
+ secure: 'boolean - use secure FTP (FTPS), default is false',
122
+ password: 'string - password',
123
+ encode: 'string - charset for file names, default is utf-8',
124
+ title: 'string - bookmark title',
125
+ profile: 'string - profile id',
126
+ description: 'string - bookmark description'
127
+ },
128
+ web: {
129
+ type: 'web',
130
+ url: 'string (required) - website URL',
131
+ title: 'string - bookmark title',
132
+ description: 'string - bookmark description',
133
+ useragent: 'string - custom user agent'
134
+ },
135
+ local: {
136
+ type: 'local',
137
+ title: 'string - bookmark title',
138
+ description: 'string - bookmark description',
139
+ startDirectoryLocal: 'string - local starting directory',
140
+ runScripts: 'array - run scripts after connected ({delay,script})'
141
+ },
142
+ spice: {
143
+ type: 'spice',
144
+ host: 'string (required) - hostname or IP address',
145
+ port: 'number (default: 5900) - Spice port',
146
+ password: 'string - Spice password',
147
+ title: 'string - bookmark title',
148
+ viewOnly: 'boolean - view only mode, default is false',
149
+ scaleViewport: 'boolean - scale viewport to window, default is true',
150
+ description: 'string - bookmark description',
151
+ profile: 'string - profile id',
152
+ proxy: 'string - proxy address (socks5://...)'
153
+ }
154
+ }
155
+
156
+ export function buildPrompt (description) {
157
+ const lang = window.store.config.languageAI || window.store.getLangName()
158
+ const schemaDescription = Object.entries(bookmarkSchema)
159
+ .map(([type, fields]) => {
160
+ const fieldList = Object.entries(fields)
161
+ .map(([key, desc]) => ` ${key}: ${desc}`)
162
+ .join('\n')
163
+ return ` ${type}:\n${fieldList}`
164
+ })
165
+ .join('\n\n')
166
+
167
+ return `You are a bookmark configuration generator. Based on the user's natural language description, generate bookmark configurations in JSON format.
168
+
169
+ Available bookmark types and their fields:
170
+ ${schemaDescription}
171
+
172
+ Important rules:
173
+ 1. Analyze the user's description to determine the most appropriate connection type
174
+ 2. For SSH connections, use type "ssh" and default port 22 unless specified
175
+ 3. For Telnet connections, use type "telnet" and default port 23 unless specified
176
+ 4. For VNC connections, use type "vnc" and default port 5900 unless specified
177
+ 5. For RDP connections, use type "rdp" and default port 3389 unless specified
178
+ 6. For FTP connections, use type "ftp" and default port 21 unless specified
179
+ 7. For Serial connections, use type "serial"
180
+ 8. For Web/Browser connections, use type "web" with a URL field
181
+ 9. For Local terminal, use type "local"
182
+ 10. Only include fields that are relevant to the connection type
183
+ 11. Always include a meaningful title if not specified
184
+ 12. Respond ONLY with valid JSON, no markdown formatting or explanations
185
+ 13. Reply in ${lang} language
186
+
187
+ User description: ${description}
188
+
189
+ Generate the bookmark JSON:`
190
+ }
191
+
192
+ export default bookmarkSchema
@@ -0,0 +1,32 @@
1
+ import { TreeSelect } from 'antd'
2
+ import formatBookmarkGroups from './bookmark-group-tree-format'
3
+
4
+ const e = window.translate
5
+
6
+ export default function AICategorySelect ({
7
+ bookmarkGroups = [],
8
+ value,
9
+ onChange
10
+ }) {
11
+ const tree = formatBookmarkGroups(bookmarkGroups)
12
+
13
+ const handleChange = (categoryId) => {
14
+ if (onChange) {
15
+ onChange(categoryId)
16
+ }
17
+ }
18
+
19
+ return (
20
+ <div className='pd1b'>
21
+ <label className='iblock mg1r'>{e('bookmarkCategory')}</label>
22
+ <TreeSelect
23
+ value={value}
24
+ treeData={tree}
25
+ treeDefaultExpandAll
26
+ showSearch
27
+ onChange={handleChange}
28
+ style={{ minWidth: 200 }}
29
+ />
30
+ </div>
31
+ )
32
+ }
@@ -14,7 +14,7 @@ const e = window.translate
14
14
  export default function BookmarkCategorySelect ({
15
15
  bookmarkGroups = [],
16
16
  form,
17
- formItemLayout,
17
+ formItemLayout = defaultFormItemLayout,
18
18
  name = 'category',
19
19
  onChange,
20
20
  formData = {} // Add formData prop to access bookmark ID
@@ -67,11 +67,9 @@ export default function BookmarkCategorySelect ({
67
67
  }
68
68
  }
69
69
 
70
- const layout = formItemLayout || defaultFormItemLayout
71
-
72
70
  return (
73
71
  <FormItem
74
- {...layout}
72
+ {...formItemLayout}
75
73
  label={e('bookmarkCategory')}
76
74
  name={name}
77
75
  >
@@ -21,12 +21,6 @@ import SshHostSelector from './ssh-host-selector.jsx'
21
21
  import SshAuthTypeSelector from './ssh-auth-type-selector.jsx'
22
22
  import SshAuthSelector from './ssh-auth-selector.jsx'
23
23
  import CategorySelect from './category-select.jsx'
24
- import WikiAlert from './wiki-alert.jsx'
25
- import {
26
- rdpWikiLink,
27
- vncWikiLink
28
- } from '../../../common/constants.js'
29
-
30
24
  const Fragment = React.Fragment
31
25
  const FormItem = Form.Item
32
26
 
@@ -126,10 +120,6 @@ export function renderFormItem (item, formItemLayout, form, ctxProps, index) {
126
120
  return <Alert key={name} type='info' {...item.props} />
127
121
  case 'warning':
128
122
  return <Alert key={name} type='warning' {...item.props} />
129
- case 'rdpWarning':
130
- return <WikiAlert key={name} wikiUrl={rdpWikiLink} />
131
- case 'vncWarning':
132
- return <WikiAlert key={name} wikiUrl={vncWikiLink} />
133
123
  case 'categorySelect':
134
124
  return (
135
125
  <CategorySelect
@@ -15,6 +15,7 @@ const ftpConfig = {
15
15
  user: '',
16
16
  password: '',
17
17
  secure: false,
18
+ encode: 'utf-8',
18
19
  ...getAuthTypeDefault(props)
19
20
  })
20
21
  },
@@ -32,6 +33,7 @@ const ftpConfig = {
32
33
  { type: 'input', name: 'user', label: () => e('username') },
33
34
  { type: 'password', name: 'password', label: () => e('password') },
34
35
  { type: 'switch', name: 'secure', label: () => e('secure'), valuePropName: 'checked' },
36
+ commonFields.encode,
35
37
  commonFields.type
36
38
  ]
37
39
  }
@@ -21,7 +21,6 @@ const rdpConfig = {
21
21
  key: 'auth',
22
22
  label: e('auth'),
23
23
  fields: [
24
- { type: 'rdpWarning', name: 'rdpWarning' },
25
24
  commonFields.category,
26
25
  commonFields.colorTitle,
27
26
  { type: 'input', name: 'host', label: () => e('host'), rules: [{ required: true, message: e('host') + ' required' }] },
@@ -8,6 +8,7 @@ import serial from './serial'
8
8
  import local from './local'
9
9
  import rdp from './rdp'
10
10
  import ftp from './ftp'
11
+ import spice from './spice'
11
12
 
12
13
  const sessionConfig = {
13
14
  [connectionMap.ssh]: ssh,
@@ -17,7 +18,8 @@ const sessionConfig = {
17
18
  [connectionMap.vnc]: vnc,
18
19
  [connectionMap.rdp]: rdp,
19
20
  [connectionMap.ftp]: ftp,
20
- [connectionMap.web]: web
21
+ [connectionMap.web]: web,
22
+ [connectionMap.spice]: spice
21
23
  }
22
24
 
23
25
  export default sessionConfig
@@ -0,0 +1,43 @@
1
+ import { formItemLayout } from '../../../common/form-layout.js'
2
+ import { terminalSpiceType } from '../../../common/constants.js'
3
+ import { createBaseInitValues, getAuthTypeDefault } from '../common/init-values.js'
4
+ import { isEmpty } from 'lodash-es'
5
+ import { commonFields } from './common-fields.js'
6
+
7
+ const e = window.translate
8
+
9
+ const spiceConfig = {
10
+ key: 'spice',
11
+ type: terminalSpiceType,
12
+ initValues: (props) => {
13
+ return createBaseInitValues(props, terminalSpiceType, {
14
+ port: 5900,
15
+ viewOnly: false,
16
+ scaleViewport: true,
17
+ connectionHoppings: [],
18
+ ...getAuthTypeDefault(props)
19
+ })
20
+ },
21
+ layout: formItemLayout,
22
+ tabs: () => [
23
+ {
24
+ key: 'auth',
25
+ label: e('auth'),
26
+ fields: [
27
+ commonFields.category,
28
+ commonFields.colorTitle,
29
+ { type: 'input', name: 'host', label: () => e('host'), rules: [{ required: true, message: e('host') + ' required' }] },
30
+ commonFields.port,
31
+ { type: 'switch', name: 'viewOnly', label: () => e('viewOnly'), valuePropName: 'checked' },
32
+ { type: 'switch', name: 'scaleViewport', label: () => e('scaleViewport'), valuePropName: 'checked' },
33
+ { type: 'profileItem', name: '__profile__', label: '', profileFilter: d => !isEmpty(d.spice) },
34
+ commonFields.password,
35
+ commonFields.description,
36
+ commonFields.proxy,
37
+ commonFields.type
38
+ ]
39
+ }
40
+ ]
41
+ }
42
+
43
+ export default spiceConfig
@@ -13,7 +13,7 @@ const vncConfig = {
13
13
  return createBaseInitValues(props, terminalVncType, {
14
14
  port: 5900,
15
15
  viewOnly: false,
16
- clipViewport: true,
16
+ clipViewport: false,
17
17
  scaleViewport: true,
18
18
  qualityLevel: 3, // 0-9, lower = faster performance, default 6
19
19
  compressionLevel: 1, // 0-9, lower = faster performance, default 2
@@ -28,7 +28,6 @@ const vncConfig = {
28
28
  key: 'auth',
29
29
  label: e('auth'),
30
30
  fields: [
31
- { type: 'vncWarning', name: 'vncWarning' },
32
31
  commonFields.category,
33
32
  commonFields.colorTitle,
34
33
  { type: 'input', name: 'host', label: () => e('host'), rules: [{ required: true, message: e('host') + ' required' }] },
@@ -0,0 +1,134 @@
1
+ import bookmarkSchema from './bookmark-schema.js'
2
+
3
+ const defaultValues = {
4
+ ssh: {
5
+ port: 22,
6
+ enableSsh: true,
7
+ enableSftp: true,
8
+ useSshAgent: true,
9
+ x11: false,
10
+ term: 'xterm-256color',
11
+ displayRaw: false,
12
+ encode: 'utf8',
13
+ envLang: 'en_US.UTF-8'
14
+ },
15
+ telnet: {
16
+ port: 23
17
+ },
18
+ serial: {
19
+ baudRate: 9600,
20
+ dataBits: 8,
21
+ stopBits: 1,
22
+ parity: 'none',
23
+ rtscts: false,
24
+ xon: false,
25
+ xoff: false,
26
+ xany: false
27
+ },
28
+ vnc: {
29
+ port: 5900,
30
+ viewOnly: false,
31
+ clipViewport: false,
32
+ scaleViewport: true,
33
+ qualityLevel: 3,
34
+ compressionLevel: 1,
35
+ shared: true
36
+ },
37
+ rdp: {
38
+ port: 3389
39
+ },
40
+ ftp: {
41
+ port: 21,
42
+ secure: false
43
+ }
44
+ }
45
+
46
+ const requiredFields = {
47
+ ssh: ['host', 'username', 'term'],
48
+ telnet: ['host'],
49
+ serial: ['path'],
50
+ vnc: ['host'],
51
+ rdp: ['host'],
52
+ ftp: ['host'],
53
+ web: ['url']
54
+ }
55
+
56
+ export function fixBookmarkData (data) {
57
+ if (!data || typeof data !== 'object') {
58
+ return data
59
+ }
60
+
61
+ const type = data.type || 'ssh'
62
+ const schema = bookmarkSchema[type]
63
+
64
+ if (!schema) {
65
+ return data
66
+ }
67
+
68
+ const fixed = { ...data }
69
+
70
+ if (!fixed.type) {
71
+ fixed.type = type
72
+ }
73
+
74
+ const defaults = defaultValues[type] || {}
75
+ for (const [key, value] of Object.entries(defaults)) {
76
+ if (fixed[key] === undefined || fixed[key] === null) {
77
+ fixed[key] = value
78
+ }
79
+ }
80
+
81
+ if (fixed.connectionHoppings?.length) {
82
+ fixed.hasHopping = true
83
+ }
84
+
85
+ return fixed
86
+ }
87
+
88
+ export function validateBookmarkData (data) {
89
+ if (!data || typeof data !== 'object') {
90
+ return {
91
+ valid: false,
92
+ errors: ['Invalid data format']
93
+ }
94
+ }
95
+
96
+ const type = data.type || 'ssh'
97
+ const required = requiredFields[type] || []
98
+ const errors = []
99
+
100
+ for (const field of required) {
101
+ if (!data[field]) {
102
+ errors.push(`Missing required field: ${field}`)
103
+ }
104
+ }
105
+
106
+ return {
107
+ valid: errors.length === 0,
108
+ errors
109
+ }
110
+ }
111
+
112
+ export function getMissingRequiredFields (data) {
113
+ if (!data || typeof data !== 'object') {
114
+ return []
115
+ }
116
+
117
+ const type = data.type || 'ssh'
118
+ const required = requiredFields[type] || []
119
+ const missing = []
120
+
121
+ for (const field of required) {
122
+ if (!data[field]) {
123
+ missing.push(field)
124
+ }
125
+ }
126
+
127
+ return missing
128
+ }
129
+
130
+ export default {
131
+ fixBookmarkData,
132
+ validateBookmarkData,
133
+ getMissingRequiredFields
134
+ }
@@ -2,7 +2,7 @@
2
2
  * Config-driven bookmark form (drop-in replacement)
3
3
  */
4
4
  import { PureComponent } from 'react'
5
- import { Radio } from 'antd'
5
+ import { Radio, Button } from 'antd'
6
6
  import {
7
7
  settingMap,
8
8
  connectionMap,
@@ -13,12 +13,14 @@ import {
13
13
  terminalLocalType,
14
14
  terminalTelnetType,
15
15
  terminalFtpType,
16
- newBookmarkIdPrefix
16
+ newBookmarkIdPrefix,
17
+ terminalSpiceType
17
18
  } from '../../common/constants'
18
19
  import { createTitleWithTag } from '../../common/create-title'
19
- import { LoadingOutlined, BookOutlined } from '@ant-design/icons'
20
+ import { LoadingOutlined, BookOutlined, RobotOutlined } from '@ant-design/icons'
20
21
  import sessionConfig from './config/session-config'
21
22
  import renderForm from './render-form'
23
+ import AIBookmarkForm from './ai-bookmark-form'
22
24
  import './bookmark-form.styl'
23
25
 
24
26
  const e = window.translate
@@ -34,27 +36,56 @@ export default class BookmarkIndex2 extends PureComponent {
34
36
  terminalSerialType,
35
37
  terminalRdpType,
36
38
  terminalVncType,
37
- terminalFtpType
39
+ terminalFtpType,
40
+ terminalSpiceType
38
41
  ].includes(initType)) {
39
42
  initType = connectionMap.ssh
40
43
  }
41
- this.state = { ready: false, bookmarkType: initType }
44
+ const v = this.getInitAiModeState()
45
+ this.state = {
46
+ ready: v,
47
+ bookmarkType: initType,
48
+ aiMode: v
49
+ }
42
50
  }
43
51
 
44
52
  componentDidMount () {
45
- this.timer = setTimeout(() => this.setState({ ready: true }), 75)
53
+ this.timer = setTimeout(() => {
54
+ this.setState({ ready: true })
55
+ }, 75)
46
56
  }
47
57
 
48
58
  componentWillUnmount () {
49
59
  clearTimeout(this.timer)
50
60
  }
51
61
 
62
+ getInitAiModeState () {
63
+ const v = window.et.openBookmarkWithAIMode
64
+ if (v !== true) {
65
+ return false
66
+ }
67
+ delete window.et.openBookmarkWithAIMode
68
+ return true
69
+ }
70
+
52
71
  handleChange = (e) => {
53
72
  this.setState({ bookmarkType: e.target.value })
54
73
  }
55
74
 
75
+ handleCancelAiMode = () => {
76
+ this.setState({ aiMode: false })
77
+ }
78
+
79
+ handleToggleAIMode = () => {
80
+ if (window.store.aiConfigMissing()) {
81
+ window.store.toggleAIConfig()
82
+ return
83
+ }
84
+ this.setState(prev => ({ aiMode: !prev.aiMode }))
85
+ }
86
+
56
87
  renderTypes (bookmarkType, isNew, keys) {
57
- if (!isNew) return null
88
+ if (!isNew || this.state.aiMode) return null
58
89
  return (
59
90
  <Radio.Group
60
91
  buttonStyle='solid'
@@ -66,11 +97,7 @@ export default class BookmarkIndex2 extends PureComponent {
66
97
  >
67
98
  {keys.map(v => {
68
99
  const txt = v === 'ssh' ? 'Ssh/Sftp' : e(v)
69
- let sup = null
70
- if (v === connectionMap.vnc || v === connectionMap.rdp) {
71
- sup = <sup className='color-red'>Beta</sup>
72
- }
73
- return (<Radio.Button key={v} value={v}>{txt}{sup}</Radio.Button>)
100
+ return (<Radio.Button key={v} value={v}>{txt}</Radio.Button>)
74
101
  })}
75
102
  </Radio.Group>
76
103
  )
@@ -85,6 +112,38 @@ export default class BookmarkIndex2 extends PureComponent {
85
112
  )
86
113
  }
87
114
 
115
+ renderAIButton (isNew) {
116
+ if (!isNew || this.state.aiMode) {
117
+ return null
118
+ }
119
+ return (
120
+ <Button
121
+ size='small'
122
+ className='mg2l create-ai-btn'
123
+ icon={<RobotOutlined />}
124
+ onClick={this.handleToggleAIMode}
125
+ >
126
+ {e('createBookmarkByAI')}
127
+ </Button>
128
+ )
129
+ }
130
+
131
+ renderAiForm () {
132
+ return (
133
+ <AIBookmarkForm
134
+ onCancel={this.handleCancelAiMode}
135
+ />
136
+ )
137
+ }
138
+
139
+ renderForm () {
140
+ const { bookmarkType, aiMode } = this.state
141
+ if (aiMode) {
142
+ return this.renderAiForm()
143
+ }
144
+ return renderForm(bookmarkType, this.props)
145
+ }
146
+
88
147
  render () {
89
148
  const { formData } = this.props
90
149
  const { id = '' } = formData
@@ -102,15 +161,16 @@ export default class BookmarkIndex2 extends PureComponent {
102
161
  const keys = Object.keys(sessionConfig)
103
162
  return (
104
163
  <div className='form-wrap pd1x'>
105
- <div className='form-title pd1t pd1x pd2b'>
164
+ <div className='form-title pd1t pd1x pd2b bold'>
106
165
  <BookOutlined className='mg1r' />
107
166
  <span>
108
167
  {((!isNew ? e('edit') : e('new')) + ' ' + e(settingMap.bookmarks))}
109
168
  </span>
110
169
  {this.renderTitle(formData, isNew)}
111
170
  {this.renderTypes(bookmarkType, isNew, keys)}
171
+ {this.renderAIButton(isNew)}
112
172
  </div>
113
- {renderForm(bookmarkType, this.props)}
173
+ {this.renderForm()}
114
174
  </div>
115
175
  )
116
176
  }