@huyooo/ai-chat-frontend-react 0.2.14 → 0.2.16

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 (76) hide show
  1. package/dist/index.css +0 -1
  2. package/dist/index.js +1 -5418
  3. package/package.json +4 -5
  4. package/dist/index.css.map +0 -1
  5. package/dist/index.js.map +0 -1
  6. package/src/adapter.ts +0 -68
  7. package/src/components/ChatPanel.tsx +0 -553
  8. package/src/components/common/ConfirmDialog.css +0 -136
  9. package/src/components/common/ConfirmDialog.tsx +0 -91
  10. package/src/components/common/CopyButton.css +0 -22
  11. package/src/components/common/CopyButton.tsx +0 -46
  12. package/src/components/common/IndexingSettings.css +0 -207
  13. package/src/components/common/IndexingSettings.tsx +0 -398
  14. package/src/components/common/SettingsPanel.css +0 -337
  15. package/src/components/common/SettingsPanel.tsx +0 -215
  16. package/src/components/common/Toast.css +0 -50
  17. package/src/components/common/Toast.tsx +0 -38
  18. package/src/components/common/ToggleSwitch.css +0 -52
  19. package/src/components/common/ToggleSwitch.tsx +0 -20
  20. package/src/components/header/ChatHeader.css +0 -285
  21. package/src/components/header/ChatHeader.tsx +0 -376
  22. package/src/components/input/AtFilePicker.css +0 -147
  23. package/src/components/input/AtFilePicker.tsx +0 -519
  24. package/src/components/input/ChatInput.css +0 -283
  25. package/src/components/input/ChatInput.tsx +0 -575
  26. package/src/components/input/DropdownSelector.css +0 -231
  27. package/src/components/input/DropdownSelector.tsx +0 -333
  28. package/src/components/input/ImagePreviewModal.css +0 -124
  29. package/src/components/input/ImagePreviewModal.tsx +0 -118
  30. package/src/components/input/at-views/AtBranchView.tsx +0 -34
  31. package/src/components/input/at-views/AtBrowserView.tsx +0 -34
  32. package/src/components/input/at-views/AtChatsView.tsx +0 -34
  33. package/src/components/input/at-views/AtDocsView.tsx +0 -34
  34. package/src/components/input/at-views/AtFilesView.tsx +0 -168
  35. package/src/components/input/at-views/AtTerminalsView.tsx +0 -34
  36. package/src/components/input/at-views/AtViewStyles.css +0 -143
  37. package/src/components/input/at-views/index.ts +0 -9
  38. package/src/components/message/ContentRenderer.css +0 -9
  39. package/src/components/message/MessageBubble.css +0 -193
  40. package/src/components/message/MessageBubble.tsx +0 -240
  41. package/src/components/message/PartsRenderer.css +0 -12
  42. package/src/components/message/PartsRenderer.tsx +0 -168
  43. package/src/components/message/WelcomeMessage.css +0 -221
  44. package/src/components/message/WelcomeMessage.tsx +0 -93
  45. package/src/components/message/parts/CollapsibleCard.css +0 -80
  46. package/src/components/message/parts/CollapsibleCard.tsx +0 -80
  47. package/src/components/message/parts/ErrorPart.css +0 -9
  48. package/src/components/message/parts/ErrorPart.tsx +0 -40
  49. package/src/components/message/parts/ImagePart.css +0 -49
  50. package/src/components/message/parts/ImagePart.tsx +0 -54
  51. package/src/components/message/parts/SearchPart.css +0 -44
  52. package/src/components/message/parts/SearchPart.tsx +0 -63
  53. package/src/components/message/parts/TextPart.css +0 -579
  54. package/src/components/message/parts/TextPart.tsx +0 -213
  55. package/src/components/message/parts/ThinkingPart.css +0 -9
  56. package/src/components/message/parts/ThinkingPart.tsx +0 -48
  57. package/src/components/message/parts/ToolCallPart.css +0 -246
  58. package/src/components/message/parts/ToolCallPart.tsx +0 -289
  59. package/src/components/message/parts/ToolResultPart.css +0 -67
  60. package/src/components/message/parts/index.ts +0 -13
  61. package/src/components/message/parts/visual-predicate.ts +0 -43
  62. package/src/components/message/parts/visual-render.ts +0 -19
  63. package/src/components/message/parts/visual.ts +0 -12
  64. package/src/components/message/welcome-types.ts +0 -46
  65. package/src/context/AutoRunConfigContext.tsx +0 -13
  66. package/src/context/ChatAdapterContext.tsx +0 -8
  67. package/src/context/ChatInputContext.tsx +0 -40
  68. package/src/context/RenderersContext.tsx +0 -35
  69. package/src/hooks/useChat.ts +0 -1569
  70. package/src/hooks/useImageUpload.ts +0 -345
  71. package/src/hooks/useVoiceInput.ts +0 -454
  72. package/src/hooks/useVoiceToTextInput.ts +0 -87
  73. package/src/index.ts +0 -151
  74. package/src/styles.css +0 -330
  75. package/src/types/index.ts +0 -196
  76. package/src/utils/fileIcon.ts +0 -49
@@ -1,337 +0,0 @@
1
- .settings-panel-overlay {
2
- position: fixed;
3
- top: 0;
4
- left: 0;
5
- right: 0;
6
- bottom: 0;
7
- background: rgba(0, 0, 0, 0.7);
8
- display: flex;
9
- align-items: center;
10
- justify-content: center;
11
- z-index: 1000;
12
- }
13
-
14
- .settings-panel {
15
- background: var(--chat-bg, #1e1e1e);
16
- border: 1px solid var(--chat-border, #333);
17
- border-radius: 12px;
18
- width: 90%;
19
- max-width: 900px;
20
- height: 80vh;
21
- max-height: 700px;
22
- display: flex;
23
- flex-direction: row;
24
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
25
- overflow: hidden;
26
- }
27
-
28
- /* 左侧导航 - shadcn 风格 */
29
- .settings-sidebar {
30
- width: 240px;
31
- background: var(--chat-muted, #2a2a2a);
32
- border-right: 1px solid var(--chat-border, #333);
33
- display: flex;
34
- flex-direction: column;
35
- flex-shrink: 0;
36
- }
37
-
38
- .sidebar-header {
39
- display: flex;
40
- align-items: center;
41
- height: 50px;
42
- padding: 0 16px;
43
- border-bottom: 1px solid var(--chat-border, #333);
44
- }
45
-
46
- .sidebar-title {
47
- margin: 0;
48
- font-size: 15px;
49
- font-weight: 600;
50
- color: var(--chat-text, #fff);
51
- letter-spacing: 0.2px;
52
- line-height: 1;
53
- }
54
-
55
- .sidebar-content {
56
- flex: 1;
57
- overflow-y: auto;
58
- padding: 8px;
59
- }
60
-
61
- .sidebar-item {
62
- display: flex;
63
- align-items: center;
64
- gap: 12px;
65
- width: 100%;
66
- padding: 10px 12px;
67
- margin-bottom: 2px;
68
- background: transparent;
69
- border: none;
70
- border-radius: 8px;
71
- font-size: 14px;
72
- font-weight: 500;
73
- color: var(--chat-text-muted, #888);
74
- cursor: pointer;
75
- transition: all 0.15s ease;
76
- text-align: left;
77
- position: relative;
78
- }
79
-
80
- .sidebar-item:hover {
81
- background: rgba(255, 255, 255, 0.05);
82
- color: var(--chat-text, #ccc);
83
- }
84
-
85
- .sidebar-item.active {
86
- background: rgba(255, 255, 255, 0.1);
87
- color: var(--chat-text, #fff);
88
- }
89
-
90
- /* 右侧内容 */
91
- .settings-content {
92
- flex: 1;
93
- display: flex;
94
- flex-direction: column;
95
- overflow: hidden;
96
- }
97
-
98
- .content-header {
99
- display: flex;
100
- align-items: center;
101
- justify-content: space-between;
102
- height: 50px;
103
- padding: 0 16px;
104
- border-bottom: 1px solid var(--chat-border, #333);
105
- }
106
-
107
- .content-title {
108
- margin: 0;
109
- font-size: 15px;
110
- font-weight: 600;
111
- color: var(--chat-text, #fff);
112
- letter-spacing: 0.2px;
113
- line-height: 1;
114
- }
115
-
116
- .close-btn {
117
- display: flex;
118
- align-items: center;
119
- justify-content: center;
120
- width: 32px;
121
- height: 32px;
122
- padding: 0;
123
- background: transparent;
124
- border: none;
125
- border-radius: 6px;
126
- color: var(--chat-text-muted, #888);
127
- cursor: pointer;
128
- transition: all 0.15s;
129
- }
130
-
131
- .close-btn:hover {
132
- background: var(--chat-muted, #2a2a2a);
133
- color: var(--chat-text, #fff);
134
- }
135
-
136
- .content-body {
137
- flex: 1;
138
- overflow-y: auto;
139
- display: flex;
140
- flex-direction: column;
141
- gap: 24px;
142
- padding: 0 16px;
143
- }
144
-
145
- .setting-section {
146
- }
147
-
148
- .section-title {
149
- font-size: 16px;
150
- font-weight: 500;
151
- color: var(--chat-text, #fff);
152
- margin-bottom: 4px;
153
- }
154
-
155
- .section-description {
156
- font-size: 13px;
157
- color: var(--chat-text-muted, #888);
158
- margin-bottom: 16px;
159
- }
160
-
161
- .setting-item {
162
- display: flex;
163
- align-items: center;
164
- justify-content: space-between;
165
- padding: 16px 0;
166
- border-bottom: 1px solid var(--chat-border, #333);
167
- }
168
-
169
- .setting-item:last-child {
170
- border-bottom: none;
171
- }
172
-
173
- .setting-info {
174
- flex: 1;
175
- margin-right: 24px;
176
- }
177
-
178
- .setting-label {
179
- font-size: 14px;
180
- font-weight: 500;
181
- color: var(--chat-text, #fff);
182
- margin-bottom: 4px;
183
- }
184
-
185
- .setting-description {
186
- font-size: 13px;
187
- color: var(--chat-text-muted, #888);
188
- line-height: 1.5;
189
- }
190
-
191
- .setting-control {
192
- flex-shrink: 0;
193
- }
194
-
195
- .dropdown-wrapper {
196
- /* min-width: 280px; */
197
- }
198
-
199
- .dropdown-wrapper .dropdown-selector .selector-text {
200
- max-width: none;
201
- white-space: nowrap;
202
- }
203
-
204
- /* 开关样式 */
205
- .toggle-switch {
206
- position: relative;
207
- display: inline-block;
208
- width: 44px;
209
- height: 24px;
210
- cursor: pointer;
211
- }
212
-
213
- .toggle-switch input {
214
- opacity: 0;
215
- width: 0;
216
- height: 0;
217
- }
218
-
219
- .toggle-slider {
220
- position: absolute;
221
- top: 0;
222
- left: 0;
223
- right: 0;
224
- bottom: 0;
225
- background-color: var(--chat-muted, #3c3c3c);
226
- border-radius: 24px;
227
- transition: all 0.2s;
228
- border: 1px solid rgba(255, 255, 255, 0.1);
229
- }
230
-
231
- .toggle-slider:before {
232
- position: absolute;
233
- content: "";
234
- height: 18px;
235
- width: 18px;
236
- left: 3px;
237
- top: 50%;
238
- transform: translateY(-50%);
239
- background-color: #fff;
240
- border-radius: 50%;
241
- transition: all 0.2s;
242
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
243
- }
244
-
245
- .toggle-switch input:checked + .toggle-slider {
246
- background-color: rgb(153, 207, 140);
247
- border-color: rgba(255, 255, 255, 0.2);
248
- }
249
-
250
- .toggle-switch input:checked + .toggle-slider:before {
251
- transform: translate(20px, -50%);
252
- }
253
-
254
- .toggle-switch input:focus + .toggle-slider {
255
- box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.2);
256
- }
257
-
258
- /* 工具管理 */
259
- .setting-section-header {
260
- display: flex;
261
- align-items: flex-start;
262
- justify-content: space-between;
263
- gap: 16px;
264
- padding: 16px 0 8px;
265
- border-bottom: 1px solid var(--chat-border, #333);
266
- }
267
-
268
- .disable-all-btn {
269
- flex-shrink: 0;
270
- height: 32px;
271
- padding: 0 12px;
272
- border-radius: 8px;
273
- border: 1px solid var(--chat-border, #333);
274
- background: rgba(255, 255, 255, 0.06);
275
- color: var(--chat-text, #fff);
276
- font-size: 13px;
277
- cursor: pointer;
278
- transition: all 0.15s ease;
279
- }
280
-
281
- .disable-all-btn:hover {
282
- background: rgba(255, 255, 255, 0.1);
283
- }
284
-
285
- .disable-all-btn:disabled {
286
- opacity: 0.5;
287
- cursor: not-allowed;
288
- }
289
-
290
- .tools-list {
291
- display: flex;
292
- flex-direction: column;
293
- }
294
-
295
- .tool-item {
296
- display: flex;
297
- align-items: center;
298
- justify-content: space-between;
299
- gap: 16px;
300
- padding: 14px 0;
301
- border-bottom: 1px solid var(--chat-border, #333);
302
- }
303
-
304
- .tool-item:last-child {
305
- border-bottom: none;
306
- }
307
-
308
- .tool-info {
309
- flex: 1;
310
- min-width: 0;
311
- }
312
-
313
- .tool-name {
314
- font-size: 14px;
315
- font-weight: 500;
316
- color: var(--chat-text, #fff);
317
- margin-bottom: 4px;
318
- word-break: break-all;
319
- }
320
-
321
- .tool-description {
322
- font-size: 13px;
323
- color: var(--chat-text-muted, #888);
324
- line-height: 1.5;
325
- word-break: break-word;
326
- }
327
-
328
- .no-tools {
329
- padding: 16px 0;
330
- color: var(--chat-text, #fff);
331
- }
332
-
333
- .no-tools-hint {
334
- font-size: 12px;
335
- color: var(--chat-text-muted, #888);
336
- margin-top: 8px;
337
- }
@@ -1,215 +0,0 @@
1
- /**
2
- * 设置面板组件
3
- * 用于配置 AutoRunConfig
4
- */
5
-
6
- import { useState, useCallback, useMemo } from 'react'
7
- import { Icon } from '@iconify/react'
8
- import type { AutoRunConfig, AutoRunMode } from '@huyooo/ai-chat-bridge-electron/renderer'
9
- import { DropdownSelector, type DropdownOption } from '../input/DropdownSelector'
10
- import { ToggleSwitch } from './ToggleSwitch'
11
- import { IndexingSettings } from './IndexingSettings'
12
- import './SettingsPanel.css'
13
-
14
- interface SettingsPanelProps {
15
- visible: boolean
16
- config: AutoRunConfig
17
- /** 所有可用工具列表(用于工具管理) */
18
- allTools?: Array<{ name: string; description: string }>
19
- /** 启用的工具名称列表(undefined 表示全部启用) */
20
- enabledTools?: string[] | undefined
21
- /** 更新工具开关(持久化由上层处理) */
22
- onUpdateEnabledTools?: (tools: string[] | undefined) => void
23
- onClose: () => void
24
- onChange: (config: AutoRunConfig) => void
25
- }
26
-
27
- const sections = [
28
- { id: 'agent', label: 'Agent', icon: 'solar:link-round-angle-line-duotone' },
29
- { id: 'indexing', label: '索引与文档', icon: 'lucide:database' },
30
- ] as const
31
-
32
- export function SettingsPanel({ visible, config, allTools, enabledTools, onUpdateEnabledTools, onClose, onChange }: SettingsPanelProps) {
33
- const [currentSection, setCurrentSection] = useState<string>('agent')
34
-
35
- const currentSectionLabel = useMemo(
36
- () => sections.find(s => s.id === currentSection)?.label ?? '',
37
- [currentSection]
38
- )
39
-
40
- // 模式选项
41
- const modeOptions: DropdownOption[] = [
42
- { value: 'run-everything', label: '运行所有内容(自动执行)' },
43
- { value: 'manual', label: '手动批准(每次执行前询问)' },
44
- ]
45
-
46
- /** 更新配置项 */
47
- const updateConfig = useCallback(<K extends keyof AutoRunConfig>(key: K, value: AutoRunConfig[K]) => {
48
- onChange({ ...config, [key]: value })
49
- }, [config, onChange])
50
-
51
- /** 处理模式变化 */
52
- const handleModeChange = useCallback((value: string) => {
53
- updateConfig('mode', value as AutoRunMode)
54
- }, [updateConfig])
55
-
56
- /** 处理遮罩点击 */
57
- const handleOverlayClick = useCallback((e: React.MouseEvent) => {
58
- if (e.target === e.currentTarget) {
59
- onClose()
60
- }
61
- }, [onClose])
62
-
63
- const allToolNames = useMemo(() => (allTools ?? []).map((t) => t.name), [allTools])
64
-
65
- /** 判断工具是否启用 */
66
- const isToolEnabled = useCallback((toolName: string): boolean => {
67
- // undefined 表示全部启用
68
- if (enabledTools === undefined) return true
69
- return enabledTools.includes(toolName)
70
- }, [enabledTools])
71
-
72
- /** 处理工具开关切换 */
73
- const handleToolToggle = useCallback((toolName: string, checked: boolean) => {
74
- if (!onUpdateEnabledTools) return
75
-
76
- // undefined = 全部启用
77
- if (enabledTools === undefined) {
78
- if (checked) return
79
- onUpdateEnabledTools(allToolNames.filter((n) => n !== toolName))
80
- return
81
- }
82
-
83
- const set = new Set(enabledTools)
84
- if (checked) set.add(toolName)
85
- else set.delete(toolName)
86
-
87
- // 如果全部启用,回到 undefined(更符合默认语义)
88
- if (allToolNames.length > 0 && set.size === allToolNames.length) {
89
- onUpdateEnabledTools(undefined)
90
- return
91
- }
92
-
93
- onUpdateEnabledTools(Array.from(set))
94
- }, [enabledTools, allToolNames, onUpdateEnabledTools])
95
-
96
- /** 一键禁用所有工具 */
97
- const handleDisableAllTools = useCallback(() => {
98
- onUpdateEnabledTools?.([])
99
- }, [onUpdateEnabledTools])
100
-
101
- const isAllToolsDisabled = useMemo(
102
- () => enabledTools !== undefined && enabledTools.length === 0,
103
- [enabledTools]
104
- )
105
-
106
- const handleEnableAllTools = useCallback(() => {
107
- // 恢复默认:undefined 表示全部启用
108
- onUpdateEnabledTools?.(undefined)
109
- }, [onUpdateEnabledTools])
110
-
111
- if (!visible) return null
112
-
113
- return (
114
- <div className="settings-panel-overlay" onClick={handleOverlayClick}>
115
- <div className="settings-panel">
116
- {/* 左侧导航 */}
117
- <div className="settings-sidebar">
118
- <div className="sidebar-header">
119
- <h3 className="sidebar-title">设置</h3>
120
- </div>
121
- <div className="sidebar-content">
122
- {sections.map((section) => (
123
- <button
124
- key={section.id}
125
- className={`sidebar-item${currentSection === section.id ? ' active' : ''}`}
126
- onClick={() => setCurrentSection(section.id)}
127
- >
128
- <Icon icon={section.icon} width={18} />
129
- <span>{section.label}</span>
130
- </button>
131
- ))}
132
- </div>
133
- </div>
134
-
135
- {/* 右侧内容 */}
136
- <div className="settings-content">
137
- <div className="content-header">
138
- <h2 className="content-title">{currentSectionLabel}</h2>
139
- <button className="close-btn" onClick={onClose}>
140
- <Icon icon="lucide:x" width={20} />
141
- </button>
142
- </div>
143
- <div className="content-body">
144
- {/* Agent 设置 */}
145
- {currentSection === 'agent' && (
146
- <>
147
- {/* 自动运行模式 */}
148
- <div className="setting-item">
149
- <div className="setting-info">
150
- <div className="setting-label">自动运行模式</div>
151
- <div className="setting-description">控制工具执行的自动运行行为</div>
152
- </div>
153
- <div className="setting-control">
154
- <DropdownSelector
155
- value={config.mode ?? 'run-everything'}
156
- options={modeOptions}
157
- align="right"
158
- onSelect={handleModeChange}
159
- />
160
- </div>
161
- </div>
162
-
163
- {/* 工具管理 */}
164
- <div className="setting-section">
165
- <div className="setting-section-header">
166
- <div className="setting-info">
167
- <div className="setting-label">工具管理</div>
168
- <div className="setting-description">控制哪些工具可以被 AI 使用</div>
169
- </div>
170
- <button
171
- className="disable-all-btn"
172
- onClick={isAllToolsDisabled ? handleEnableAllTools : handleDisableAllTools}
173
- disabled={!allTools?.length}
174
- >
175
- {isAllToolsDisabled ? '启用所有工具' : '禁用所有工具'}
176
- </button>
177
- </div>
178
-
179
- <div className="tools-list">
180
- {allTools && allTools.length > 0 ? (
181
- allTools.map((tool) => (
182
- <div key={tool.name} className="tool-item">
183
- <div className="tool-info">
184
- <div className="tool-name">{tool.name}</div>
185
- <div className="tool-description">{tool.description}</div>
186
- </div>
187
- <ToggleSwitch
188
- checked={isToolEnabled(tool.name)}
189
- onChange={(checked) => handleToolToggle(tool.name, checked)}
190
- />
191
- </div>
192
- ))
193
- ) : (
194
- <div className="no-tools">
195
- <div>暂无可用工具</div>
196
- <div className="no-tools-hint">
197
- 工具需要在创建 Electron Bridge 时通过 <code>tools</code> 参数注入
198
- </div>
199
- </div>
200
- )}
201
- </div>
202
- </div>
203
- </>
204
- )}
205
-
206
- {/* 索引与文档设置 */}
207
- {currentSection === 'indexing' && (
208
- <IndexingSettings />
209
- )}
210
- </div>
211
- </div>
212
- </div>
213
- </div>
214
- )
215
- }
@@ -1,50 +0,0 @@
1
- .toast-container {
2
- position: fixed;
3
- top: 60px;
4
- left: 50%;
5
- transform: translateX(-50%);
6
- z-index: 10000;
7
- pointer-events: none;
8
- animation: toast-in 0.2s ease;
9
- }
10
-
11
- .toast {
12
- padding: 8px 16px;
13
- border-radius: 6px;
14
- font-size: 13px;
15
- border: 1px solid;
16
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
17
- }
18
-
19
- /* 正常/成功 - 灰色 */
20
- .toast-info,
21
- .toast-success {
22
- background: var(--chat-bg-secondary, #2a2a2a);
23
- color: var(--chat-text, #fff);
24
- border-color: var(--chat-border, #444);
25
- }
26
-
27
- /* 警告 - 黄色 */
28
- .toast-warning {
29
- background: rgba(245, 158, 11, 0.15);
30
- color: #f59e0b;
31
- border-color: rgba(245, 158, 11, 0.3);
32
- }
33
-
34
- /* 错误 - 红色 */
35
- .toast-error {
36
- background: rgba(239, 68, 68, 0.15);
37
- color: #ef4444;
38
- border-color: rgba(239, 68, 68, 0.3);
39
- }
40
-
41
- @keyframes toast-in {
42
- from {
43
- opacity: 0;
44
- transform: translateX(-50%) translateY(-10px);
45
- }
46
- to {
47
- opacity: 1;
48
- transform: translateX(-50%) translateY(0);
49
- }
50
- }
@@ -1,38 +0,0 @@
1
- import { FC, useEffect } from 'react'
2
- import { createPortal } from 'react-dom'
3
- import './Toast.css'
4
-
5
- interface ToastProps {
6
- visible: boolean
7
- message: string
8
- type?: 'info' | 'success' | 'warning' | 'error'
9
- duration?: number
10
- onClose: () => void
11
- }
12
-
13
- export const Toast: FC<ToastProps> = ({
14
- visible,
15
- message,
16
- type = 'info',
17
- duration = 2000,
18
- onClose,
19
- }) => {
20
- // 自动关闭
21
- useEffect(() => {
22
- if (visible && duration > 0) {
23
- const timer = setTimeout(() => {
24
- onClose()
25
- }, duration)
26
- return () => clearTimeout(timer)
27
- }
28
- }, [visible, duration, onClose])
29
-
30
- if (!visible) return null
31
-
32
- return createPortal(
33
- <div className="toast-container">
34
- <div className={`toast toast-${type}`}>{message}</div>
35
- </div>,
36
- document.body
37
- )
38
- }
@@ -1,52 +0,0 @@
1
- .toggle-switch {
2
- position: relative;
3
- display: inline-block;
4
- width: 44px;
5
- height: 24px;
6
- cursor: pointer;
7
- }
8
-
9
- .toggle-switch input {
10
- opacity: 0;
11
- width: 0;
12
- height: 0;
13
- }
14
-
15
- .toggle-slider {
16
- position: absolute;
17
- top: 0;
18
- left: 0;
19
- right: 0;
20
- bottom: 0;
21
- background-color: var(--chat-muted, #3c3c3c);
22
- border-radius: 24px;
23
- transition: all 0.2s;
24
- border: 1px solid rgba(255, 255, 255, 0.1);
25
- }
26
-
27
- .toggle-slider::before {
28
- position: absolute;
29
- content: "";
30
- height: 18px;
31
- width: 18px;
32
- left: 3px;
33
- top: 50%;
34
- transform: translateY(-50%);
35
- background-color: #fff;
36
- border-radius: 50%;
37
- transition: all 0.2s;
38
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
39
- }
40
-
41
- .toggle-switch input:checked + .toggle-slider {
42
- background-color: rgb(153, 207, 140);
43
- border-color: rgba(255, 255, 255, 0.2);
44
- }
45
-
46
- .toggle-switch input:checked + .toggle-slider::before {
47
- transform: translate(20px, -50%);
48
- }
49
-
50
- .toggle-switch input:focus + .toggle-slider {
51
- box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.2);
52
- }
@@ -1,20 +0,0 @@
1
- import type { FC } from 'react'
2
- import './ToggleSwitch.css'
3
-
4
- interface ToggleSwitchProps {
5
- checked?: boolean
6
- onChange: (value: boolean) => void
7
- }
8
-
9
- export const ToggleSwitch: FC<ToggleSwitchProps> = ({ checked = false, onChange }) => {
10
- return (
11
- <label className="toggle-switch">
12
- <input
13
- type="checkbox"
14
- checked={checked}
15
- onChange={(e) => onChange(e.target.checked)}
16
- />
17
- <span className="toggle-slider"></span>
18
- </label>
19
- )
20
- }