@mseep/anything-analyzer 3.6.50

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 (172) hide show
  1. package/.codeartsdoer/.codebaseignore +0 -0
  2. package/.codeartsdoer/AGENTS.md +12 -0
  3. package/.github/workflows/build.yml +146 -0
  4. package/README.en.md +264 -0
  5. package/README.md +276 -0
  6. package/RELEASE_NOTES.md +16 -0
  7. package/USAGE.md +490 -0
  8. package/color-preview-r3.html +414 -0
  9. package/color-preview.html +414 -0
  10. package/dev-app-update.yml +3 -0
  11. package/electron-builder.yml +36 -0
  12. package/electron.vite.config.ts +40 -0
  13. package/package.json +53 -0
  14. package/report-2026-04-13-copilot-claude-sonnet-4.6.md +955 -0
  15. package/resources/doloffer-logo.png +0 -0
  16. package/resources/entitlements.mac.plist +12 -0
  17. package/resources/icon.ico +0 -0
  18. package/resources/icon.png +0 -0
  19. package/src/main/ai/ai-analyzer.ts +517 -0
  20. package/src/main/ai/crypto-script-extractor.ts +206 -0
  21. package/src/main/ai/data-assembler.ts +205 -0
  22. package/src/main/ai/llm-router.ts +1120 -0
  23. package/src/main/ai/prompt-builder.ts +349 -0
  24. package/src/main/ai/scene-detector.ts +302 -0
  25. package/src/main/capture/capture-engine.ts +130 -0
  26. package/src/main/capture/interaction-recorder.ts +171 -0
  27. package/src/main/capture/js-injector.ts +57 -0
  28. package/src/main/capture/replay-engine.ts +256 -0
  29. package/src/main/capture/storage-collector.ts +76 -0
  30. package/src/main/cdp/cdp-manager.ts +233 -0
  31. package/src/main/db/database.ts +41 -0
  32. package/src/main/db/migrations.ts +235 -0
  33. package/src/main/db/repositories.ts +574 -0
  34. package/src/main/fingerprint/http-spoofing.ts +48 -0
  35. package/src/main/fingerprint/presets.ts +173 -0
  36. package/src/main/fingerprint/profile-generator.ts +115 -0
  37. package/src/main/fingerprint/profile-store.ts +52 -0
  38. package/src/main/index.ts +260 -0
  39. package/src/main/ipc.ts +856 -0
  40. package/src/main/logger.ts +42 -0
  41. package/src/main/mcp/mcp-config.ts +66 -0
  42. package/src/main/mcp/mcp-manager.ts +155 -0
  43. package/src/main/mcp/mcp-server.ts +1038 -0
  44. package/src/main/prompt-templates.ts +170 -0
  45. package/src/main/proxy/ca-manager.ts +204 -0
  46. package/src/main/proxy/cert-download-page.ts +171 -0
  47. package/src/main/proxy/cert-installer.ts +242 -0
  48. package/src/main/proxy/mitm-proxy-config.ts +37 -0
  49. package/src/main/proxy/mitm-proxy-server.ts +1085 -0
  50. package/src/main/proxy/system-proxy.ts +248 -0
  51. package/src/main/session/session-manager.ts +724 -0
  52. package/src/main/tab-manager.ts +582 -0
  53. package/src/main/updater.ts +111 -0
  54. package/src/main/window.ts +235 -0
  55. package/src/preload/hook-script.ts +270 -0
  56. package/src/preload/index.ts +211 -0
  57. package/src/preload/interaction-hook.ts +286 -0
  58. package/src/preload/stealth-script.ts +302 -0
  59. package/src/preload/target-preload.ts +15 -0
  60. package/src/renderer/App.tsx +656 -0
  61. package/src/renderer/components/AiLogDetail.tsx +173 -0
  62. package/src/renderer/components/AiLogList.tsx +101 -0
  63. package/src/renderer/components/AiLogView.module.css +364 -0
  64. package/src/renderer/components/AiLogView.tsx +86 -0
  65. package/src/renderer/components/AnalyzeBar.module.css +79 -0
  66. package/src/renderer/components/AnalyzeBar.tsx +104 -0
  67. package/src/renderer/components/BrowserPanel.module.css +67 -0
  68. package/src/renderer/components/BrowserPanel.tsx +90 -0
  69. package/src/renderer/components/ControlBar.module.css +47 -0
  70. package/src/renderer/components/ControlBar.tsx +205 -0
  71. package/src/renderer/components/HookLog.tsx +132 -0
  72. package/src/renderer/components/InteractionLog.tsx +183 -0
  73. package/src/renderer/components/MCPServerModal.tsx +427 -0
  74. package/src/renderer/components/PromptTemplateModal.tsx +254 -0
  75. package/src/renderer/components/ReportView.module.css +413 -0
  76. package/src/renderer/components/ReportView.tsx +429 -0
  77. package/src/renderer/components/RequestDetail.module.css +191 -0
  78. package/src/renderer/components/RequestDetail.tsx +202 -0
  79. package/src/renderer/components/RequestLog.module.css +69 -0
  80. package/src/renderer/components/RequestLog.tsx +208 -0
  81. package/src/renderer/components/SessionList.module.css +245 -0
  82. package/src/renderer/components/SessionList.tsx +247 -0
  83. package/src/renderer/components/SettingsModal.tsx +100 -0
  84. package/src/renderer/components/StatusBar.module.css +44 -0
  85. package/src/renderer/components/StatusBar.tsx +102 -0
  86. package/src/renderer/components/StorageView.module.css +41 -0
  87. package/src/renderer/components/StorageView.tsx +178 -0
  88. package/src/renderer/components/TabBar.module.css +88 -0
  89. package/src/renderer/components/TabBar.tsx +70 -0
  90. package/src/renderer/components/Titlebar.module.css +254 -0
  91. package/src/renderer/components/Titlebar.tsx +169 -0
  92. package/src/renderer/components/settings/FingerprintSection.tsx +198 -0
  93. package/src/renderer/components/settings/GeneralSection.tsx +164 -0
  94. package/src/renderer/components/settings/LLMSection.tsx +148 -0
  95. package/src/renderer/components/settings/MCPServerSection.tsx +136 -0
  96. package/src/renderer/components/settings/MitmProxySection.tsx +320 -0
  97. package/src/renderer/components/settings/ProxySection.tsx +110 -0
  98. package/src/renderer/css-modules.d.ts +4 -0
  99. package/src/renderer/hooks/useCapture.ts +383 -0
  100. package/src/renderer/hooks/useConfirm.tsx +91 -0
  101. package/src/renderer/hooks/useSession.ts +136 -0
  102. package/src/renderer/hooks/useTabs.ts +103 -0
  103. package/src/renderer/i18n/en.ts +167 -0
  104. package/src/renderer/i18n/index.ts +47 -0
  105. package/src/renderer/i18n/zh.ts +170 -0
  106. package/src/renderer/index.html +12 -0
  107. package/src/renderer/main.tsx +15 -0
  108. package/src/renderer/styles/global.css +144 -0
  109. package/src/renderer/styles/themes/ayu-dark.css +59 -0
  110. package/src/renderer/styles/themes/catppuccin.css +59 -0
  111. package/src/renderer/styles/themes/discord.css +59 -0
  112. package/src/renderer/styles/themes/dracula.css +59 -0
  113. package/src/renderer/styles/themes/github-dark.css +59 -0
  114. package/src/renderer/styles/themes/gruvbox.css +59 -0
  115. package/src/renderer/styles/themes/index.css +11 -0
  116. package/src/renderer/styles/themes/light.css +59 -0
  117. package/src/renderer/styles/themes/nord.css +59 -0
  118. package/src/renderer/styles/themes/one-dark.css +59 -0
  119. package/src/renderer/styles/themes/tokyo-night.css +59 -0
  120. package/src/renderer/styles/tokens.css +137 -0
  121. package/src/renderer/theme.ts +31 -0
  122. package/src/renderer/ui/Badge.module.css +38 -0
  123. package/src/renderer/ui/Badge.tsx +36 -0
  124. package/src/renderer/ui/Button.module.css +142 -0
  125. package/src/renderer/ui/Button.tsx +46 -0
  126. package/src/renderer/ui/Collapse.module.css +49 -0
  127. package/src/renderer/ui/Collapse.tsx +57 -0
  128. package/src/renderer/ui/CopyableBlock.module.css +56 -0
  129. package/src/renderer/ui/CopyableBlock.tsx +42 -0
  130. package/src/renderer/ui/Empty.module.css +19 -0
  131. package/src/renderer/ui/Empty.tsx +34 -0
  132. package/src/renderer/ui/Icons.tsx +346 -0
  133. package/src/renderer/ui/Input.module.css +103 -0
  134. package/src/renderer/ui/Input.tsx +94 -0
  135. package/src/renderer/ui/InputNumber.module.css +68 -0
  136. package/src/renderer/ui/InputNumber.tsx +104 -0
  137. package/src/renderer/ui/Modal.module.css +83 -0
  138. package/src/renderer/ui/Modal.tsx +67 -0
  139. package/src/renderer/ui/Popconfirm.module.css +73 -0
  140. package/src/renderer/ui/Popconfirm.tsx +74 -0
  141. package/src/renderer/ui/Progress.module.css +35 -0
  142. package/src/renderer/ui/Progress.tsx +30 -0
  143. package/src/renderer/ui/Select.module.css +91 -0
  144. package/src/renderer/ui/Select.tsx +100 -0
  145. package/src/renderer/ui/Spinner.module.css +44 -0
  146. package/src/renderer/ui/Spinner.tsx +27 -0
  147. package/src/renderer/ui/Switch.module.css +39 -0
  148. package/src/renderer/ui/Switch.tsx +43 -0
  149. package/src/renderer/ui/Tabs.module.css +76 -0
  150. package/src/renderer/ui/Tabs.tsx +53 -0
  151. package/src/renderer/ui/Tag.module.css +66 -0
  152. package/src/renderer/ui/Tag.tsx +47 -0
  153. package/src/renderer/ui/Timeline.module.css +42 -0
  154. package/src/renderer/ui/Timeline.tsx +29 -0
  155. package/src/renderer/ui/Toast.module.css +99 -0
  156. package/src/renderer/ui/Toast.tsx +90 -0
  157. package/src/renderer/ui/Tooltip.module.css +26 -0
  158. package/src/renderer/ui/Tooltip.tsx +23 -0
  159. package/src/renderer/ui/VirtualTable.module.css +230 -0
  160. package/src/renderer/ui/VirtualTable.tsx +416 -0
  161. package/src/renderer/ui/index.ts +55 -0
  162. package/src/shared/types.ts +695 -0
  163. package/tests/main/ai/crypto-script-extractor.test.ts +281 -0
  164. package/tests/main/ai/llm-router.test.ts +1537 -0
  165. package/tests/main/ai/prompt-builder.test.ts +178 -0
  166. package/tests/main/ai/scene-detector.test.ts +212 -0
  167. package/tests/main/db/migrations.test.ts +134 -0
  168. package/tests/main/release-workflow.test.ts +59 -0
  169. package/tsconfig.json +7 -0
  170. package/tsconfig.node.json +23 -0
  171. package/tsconfig.web.json +24 -0
  172. package/vitest.config.ts +13 -0
@@ -0,0 +1,320 @@
1
+ import { useEffect, useState } from 'react'
2
+ import { InputNumber, Button, Switch, Badge, Tooltip, Modal, useToast } from '../../ui'
3
+ import {
4
+ IconShield,
5
+ IconExport,
6
+ IconDelete,
7
+ IconReload,
8
+ IconWifi,
9
+ } from '../../ui/Icons'
10
+ import type { MitmProxyConfig } from '@shared/types'
11
+
12
+ export default function MitmProxySection() {
13
+ const toast = useToast()
14
+ const [mitmEnabled, setMitmEnabled] = useState(false)
15
+ const [mitmPort, setMitmPort] = useState(8888)
16
+ const [mitmRunning, setMitmRunning] = useState(false)
17
+ const [mitmCaInstalled, setMitmCaInstalled] = useState(false)
18
+ const [mitmCaInitialized, setMitmCaInitialized] = useState(false)
19
+ const [mitmSystemProxy, setMitmSystemProxy] = useState(false)
20
+ const [mitmLoading, setMitmLoading] = useState(false)
21
+ const [regenConfirmOpen, setRegenConfirmOpen] = useState(false)
22
+ const [localIPs, setLocalIPs] = useState<string[]>([])
23
+
24
+ useEffect(() => {
25
+ window.electronAPI.getMitmProxyConfig().then(config => {
26
+ setMitmEnabled(config.enabled)
27
+ setMitmPort(config.port)
28
+ setMitmCaInstalled(config.caInstalled)
29
+ setMitmSystemProxy(config.systemProxy)
30
+ })
31
+ window.electronAPI.getMitmProxyStatus().then(status => {
32
+ setMitmRunning(status.running)
33
+ setMitmCaInitialized(status.caInitialized)
34
+ if (status.caInstalled !== undefined) setMitmCaInstalled(status.caInstalled)
35
+ if (status.systemProxyEnabled !== undefined) setMitmSystemProxy(status.systemProxyEnabled)
36
+ if (status.localIPs) setLocalIPs(status.localIPs)
37
+ })
38
+ }, [])
39
+
40
+ return (
41
+ <div style={{ display: 'flex', flexDirection: 'column', gap: 8, width: '100%' }}>
42
+ <div>
43
+ <Badge
44
+ color={mitmRunning ? 'var(--color-success)' : 'var(--text-muted)'}
45
+ label={mitmRunning ? '运行中' : '已停止'}
46
+ style={{ fontSize: 'var(--font-size-sm)' }}
47
+ />
48
+ </div>
49
+
50
+ <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
51
+ <span style={{ fontSize: 'var(--font-size-base)' }}>启用 MITM 代理</span>
52
+ <Switch checked={mitmEnabled} onChange={setMitmEnabled} />
53
+ </div>
54
+
55
+ {mitmEnabled && (
56
+ <>
57
+ <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
58
+ <span style={{ fontSize: 'var(--font-size-base)' }}>端口</span>
59
+ <InputNumber
60
+ min={1024}
61
+ max={65535}
62
+ value={mitmPort}
63
+ onChange={v => v !== null && setMitmPort(v)}
64
+ style={{ width: 120 }}
65
+ />
66
+ </div>
67
+
68
+ {/* System Proxy Toggle */}
69
+ <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
70
+ <Tooltip title="将系统 HTTP/HTTPS 代理指向 MITM 代理,无需手动配置即可捕获所有应用流量">
71
+ <span style={{ display: 'flex', alignItems: 'center', gap: 4, fontSize: 'var(--font-size-base)', cursor: 'default' }}>
72
+ <IconWifi size={14} />
73
+ 设为系统代理
74
+ </span>
75
+ </Tooltip>
76
+ <Switch
77
+ checked={mitmSystemProxy}
78
+ disabled={mitmLoading}
79
+ onChange={async (checked) => {
80
+ setMitmLoading(true)
81
+ try {
82
+ if (checked) {
83
+ const result = await window.electronAPI.enableMitmSystemProxy()
84
+ if (result.success) {
85
+ setMitmSystemProxy(true)
86
+ toast.success('已设为系统代理')
87
+ } else {
88
+ toast.error(result.error || '设置系统代理失败')
89
+ }
90
+ } else {
91
+ const result = await window.electronAPI.disableMitmSystemProxy()
92
+ if (result.success) {
93
+ setMitmSystemProxy(false)
94
+ toast.success('已取消系统代理')
95
+ } else {
96
+ toast.error(result.error || '取消系统代理失败')
97
+ }
98
+ }
99
+ } finally {
100
+ setMitmLoading(false)
101
+ }
102
+ }}
103
+ />
104
+ </div>
105
+ {mitmSystemProxy && (
106
+ <span style={{ fontSize: 'var(--font-size-sm)', color: 'var(--text-secondary)' }}>
107
+ 所有应用的流量将自动通过 MITM 代理,关闭应用时自动还原
108
+ </span>
109
+ )}
110
+
111
+ {/* CA Certificate Management */}
112
+ <div style={{ marginTop: 4 }}>
113
+ <span style={{ fontWeight: 600, fontSize: 'var(--font-size-base)' }}>CA 证书管理</span>
114
+ </div>
115
+
116
+ {!mitmCaInstalled ? (
117
+ <>
118
+ <div style={{
119
+ padding: '6px 12px',
120
+ background: 'var(--color-warning-bg)',
121
+ border: '1px solid var(--color-warning-border)',
122
+ borderLeft: '3px solid var(--color-warning)',
123
+ borderRadius: 4,
124
+ fontSize: 'var(--font-size-sm)',
125
+ color: 'var(--text-primary)',
126
+ }}>
127
+ ⚠ CA 证书未安装到系统,HTTPS 流量将无法拦截
128
+ </div>
129
+ <div style={{ display: 'flex', gap: 8 }}>
130
+ <Button
131
+ variant="primary"
132
+ icon={<IconShield size={14} />}
133
+ loading={mitmLoading}
134
+ onClick={async () => {
135
+ setMitmLoading(true)
136
+ try {
137
+ const result = await window.electronAPI.installMitmCA()
138
+ if (result.success) {
139
+ setMitmCaInstalled(true)
140
+ toast.success('CA 证书已安装到系统信任链')
141
+ } else {
142
+ toast.error(result.error || '安装失败')
143
+ }
144
+ } finally {
145
+ setMitmLoading(false)
146
+ }
147
+ }}
148
+ >
149
+ 一键安装 CA 证书
150
+ </Button>
151
+ <Button
152
+ icon={<IconExport size={14} />}
153
+ onClick={() => window.electronAPI.exportMitmCA()}
154
+ >
155
+ 导出
156
+ </Button>
157
+ </div>
158
+ </>
159
+ ) : (
160
+ <>
161
+ <div style={{
162
+ padding: '6px 12px',
163
+ background: 'var(--color-success-bg)',
164
+ border: '1px solid var(--color-success-border)',
165
+ borderLeft: '3px solid var(--color-success)',
166
+ borderRadius: 4,
167
+ fontSize: 'var(--font-size-sm)',
168
+ color: 'var(--text-primary)',
169
+ }}>
170
+ ✓ CA 证书已安装
171
+ </div>
172
+ <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
173
+ <Button
174
+ icon={<IconDelete size={14} />}
175
+ loading={mitmLoading}
176
+ onClick={async () => {
177
+ setMitmLoading(true)
178
+ try {
179
+ const result = await window.electronAPI.uninstallMitmCA()
180
+ if (result.success) {
181
+ setMitmCaInstalled(false)
182
+ toast.success('CA 证书已卸载')
183
+ } else {
184
+ toast.error(result.error || '卸载失败')
185
+ }
186
+ } finally {
187
+ setMitmLoading(false)
188
+ }
189
+ }}
190
+ >
191
+ 卸载证书
192
+ </Button>
193
+ <Button
194
+ icon={<IconExport size={14} />}
195
+ onClick={() => window.electronAPI.exportMitmCA()}
196
+ >
197
+ 导出
198
+ </Button>
199
+ <Button
200
+ variant="danger"
201
+ icon={<IconReload size={14} />}
202
+ loading={mitmLoading}
203
+ onClick={() => setRegenConfirmOpen(true)}
204
+ >
205
+ 重新生成 CA
206
+ </Button>
207
+ </div>
208
+ </>
209
+ )}
210
+
211
+ {/* Usage Instructions */}
212
+ {!mitmSystemProxy && (
213
+ <div style={{ marginTop: 4 }}>
214
+ <span style={{ fontSize: 'var(--font-size-sm)', color: 'var(--text-secondary)' }}>
215
+ 在外部浏览器/设备中配置 HTTP 代理为:
216
+ </span>
217
+ <div style={{ marginTop: 4, display: 'flex', flexDirection: 'column', gap: 2 }}>
218
+ {localIPs.length > 0 ? localIPs.map(ip => (
219
+ <code key={ip} style={{
220
+ background: 'var(--color-surface)',
221
+ padding: '2px 6px',
222
+ borderRadius: 4,
223
+ fontSize: 'var(--font-size-sm)',
224
+ fontFamily: 'var(--font-mono)',
225
+ userSelect: 'all',
226
+ }}>
227
+ {ip}:{mitmPort}
228
+ </code>
229
+ )) : (
230
+ <code style={{
231
+ background: 'var(--color-surface)',
232
+ padding: '2px 6px',
233
+ borderRadius: 4,
234
+ fontSize: 'var(--font-size-sm)',
235
+ fontFamily: 'var(--font-mono)',
236
+ }}>
237
+ http://&lt;本机IP&gt;:{mitmPort}
238
+ </code>
239
+ )}
240
+ </div>
241
+ </div>
242
+ )}
243
+
244
+ {/* Mobile cert download hint */}
245
+ <div style={{
246
+ marginTop: 4,
247
+ padding: '8px 12px',
248
+ background: 'var(--color-info-bg, rgba(59,130,246,0.08))',
249
+ border: '1px solid var(--color-info-border, rgba(59,130,246,0.2))',
250
+ borderRadius: 4,
251
+ fontSize: 'var(--font-size-sm)',
252
+ color: 'var(--text-secondary)',
253
+ lineHeight: 1.6,
254
+ }}>
255
+ 📱 手机安装证书:连接代理后,用浏览器访问{' '}
256
+ <code style={{
257
+ background: 'var(--color-surface)',
258
+ padding: '1px 5px',
259
+ borderRadius: 3,
260
+ fontFamily: 'var(--font-mono)',
261
+ fontSize: 'var(--font-size-sm)',
262
+ userSelect: 'all',
263
+ }}>
264
+ http://cert.anything.test
265
+ </code>
266
+ </div>
267
+ </>
268
+ )}
269
+
270
+ <Button variant="primary" block onClick={async () => {
271
+ const config: MitmProxyConfig = {
272
+ enabled: mitmEnabled,
273
+ port: mitmPort,
274
+ caInstalled: mitmCaInstalled,
275
+ systemProxy: mitmSystemProxy,
276
+ }
277
+ await window.electronAPI.saveMitmProxyConfig(config)
278
+ toast.success('MITM 代理设置已保存')
279
+ const status = await window.electronAPI.getMitmProxyStatus()
280
+ setMitmRunning(status.running)
281
+ setMitmCaInitialized(status.caInitialized)
282
+ }}>
283
+ 保存 MITM 代理设置
284
+ </Button>
285
+
286
+ {/* Confirm modal for regenerating CA */}
287
+ <Modal
288
+ open={regenConfirmOpen}
289
+ onClose={() => setRegenConfirmOpen(false)}
290
+ title="重新生成 CA"
291
+ footer={
292
+ <div style={{ display: 'flex', gap: 8 }}>
293
+ <Button onClick={() => setRegenConfirmOpen(false)}>取消</Button>
294
+ <Button
295
+ variant="danger"
296
+ onClick={async () => {
297
+ setRegenConfirmOpen(false)
298
+ setMitmLoading(true)
299
+ try {
300
+ await window.electronAPI.regenerateMitmCA()
301
+ setMitmCaInstalled(false)
302
+ setMitmRunning(false)
303
+ toast.success('CA 已重新生成,请重新安装证书')
304
+ } finally {
305
+ setMitmLoading(false)
306
+ }
307
+ }}
308
+ >
309
+ 确认
310
+ </Button>
311
+ </div>
312
+ }
313
+ >
314
+ <p style={{ margin: 0, fontSize: 'var(--font-size-base)' }}>
315
+ 重新生成后需要重新安装证书,已配置代理的设备将出现证书错误。确定继续?
316
+ </p>
317
+ </Modal>
318
+ </div>
319
+ )
320
+ }
@@ -0,0 +1,110 @@
1
+ import { useEffect, useState } from 'react'
2
+ import { Input, PasswordInput, Select, InputNumber, Button, useToast } from '../../ui'
3
+ import type { ProxyConfig } from '@shared/types'
4
+
5
+ const labelStyle: React.CSSProperties = {
6
+ display: 'block',
7
+ marginBottom: 4,
8
+ fontSize: 'var(--font-size-sm)',
9
+ color: 'var(--text-secondary)',
10
+ }
11
+
12
+ const fieldStyle: React.CSSProperties = {
13
+ marginBottom: 16,
14
+ }
15
+
16
+ export default function ProxySection() {
17
+ const toast = useToast()
18
+ const [proxyType, setProxyType] = useState<ProxyConfig['type']>('none')
19
+ const [host, setHost] = useState('')
20
+ const [port, setPort] = useState(1080)
21
+ const [username, setUsername] = useState('')
22
+ const [password, setPassword] = useState('')
23
+
24
+ useEffect(() => {
25
+ window.electronAPI.getProxyConfig().then(config => {
26
+ if (config) {
27
+ setProxyType(config.type)
28
+ setHost(config.host ?? '')
29
+ setPort(config.port ?? 1080)
30
+ setUsername(config.username ?? '')
31
+ setPassword(config.password ?? '')
32
+ }
33
+ })
34
+ }, [])
35
+
36
+ const handleSave = async () => {
37
+ if (proxyType !== 'none' && !host) {
38
+ toast.warning('请输入代理主机')
39
+ return
40
+ }
41
+ const config: ProxyConfig = { type: proxyType, host, port, username, password }
42
+ await window.electronAPI.saveProxyConfig(config)
43
+ toast.success('代理设置已保存并生效')
44
+ }
45
+
46
+ return (
47
+ <div>
48
+ <div style={fieldStyle}>
49
+ <label style={labelStyle}>代理类型</label>
50
+ <Select
51
+ value={proxyType}
52
+ onChange={v => setProxyType(v as ProxyConfig['type'])}
53
+ options={[
54
+ { label: '无代理 (直连)', value: 'none' },
55
+ { label: 'HTTP', value: 'http' },
56
+ { label: 'HTTPS', value: 'https' },
57
+ { label: 'SOCKS5', value: 'socks5' },
58
+ ]}
59
+ />
60
+ </div>
61
+
62
+ {proxyType && proxyType !== 'none' && (
63
+ <>
64
+ <div style={fieldStyle}>
65
+ <label style={labelStyle}>主机 *</label>
66
+ <Input
67
+ value={host}
68
+ onChange={e => setHost(e.target.value)}
69
+ placeholder="127.0.0.1"
70
+ />
71
+ </div>
72
+
73
+ <div style={fieldStyle}>
74
+ <label style={labelStyle}>端口 *</label>
75
+ <InputNumber
76
+ value={port}
77
+ onChange={v => v !== null && setPort(v)}
78
+ min={1}
79
+ max={65535}
80
+ style={{ width: '100%' }}
81
+ placeholder="1080"
82
+ />
83
+ </div>
84
+
85
+ <div style={fieldStyle}>
86
+ <label style={labelStyle}>用户名(可选)</label>
87
+ <Input
88
+ value={username}
89
+ onChange={e => setUsername(e.target.value)}
90
+ placeholder="留空则无认证"
91
+ />
92
+ </div>
93
+
94
+ <div style={fieldStyle}>
95
+ <label style={labelStyle}>密码(可选)</label>
96
+ <PasswordInput
97
+ value={password}
98
+ onChange={e => setPassword(e.target.value)}
99
+ placeholder="留空则无认证"
100
+ />
101
+ </div>
102
+ </>
103
+ )}
104
+
105
+ <Button variant="primary" block onClick={handleSave}>
106
+ 保存代理设置
107
+ </Button>
108
+ </div>
109
+ )
110
+ }
@@ -0,0 +1,4 @@
1
+ declare module '*.module.css' {
2
+ const classes: { readonly [key: string]: string }
3
+ export default classes
4
+ }