@huyooo/ai-chat-frontend-vue 0.1.6 → 0.1.7

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 (159) hide show
  1. package/README.md +367 -0
  2. package/dist/adapter.d.ts +7 -7
  3. package/dist/adapter.d.ts.map +1 -1
  4. package/dist/components/ChatPanel.vue.d.ts +120 -9
  5. package/dist/components/ChatPanel.vue.d.ts.map +1 -1
  6. package/dist/components/common/ConfirmDialog.vue.d.ts +30 -0
  7. package/dist/components/common/ConfirmDialog.vue.d.ts.map +1 -0
  8. package/dist/components/common/CopyButton.vue.d.ts +18 -0
  9. package/dist/components/common/CopyButton.vue.d.ts.map +1 -0
  10. package/dist/components/common/IndexingSettings.vue.d.ts +3 -0
  11. package/dist/components/common/IndexingSettings.vue.d.ts.map +1 -0
  12. package/dist/components/common/SettingsPanel.vue.d.ts +16 -0
  13. package/dist/components/common/SettingsPanel.vue.d.ts.map +1 -0
  14. package/dist/components/common/Toast.vue.d.ts +18 -0
  15. package/dist/components/common/Toast.vue.d.ts.map +1 -0
  16. package/dist/components/common/ToggleSwitch.vue.d.ts +10 -0
  17. package/dist/components/common/ToggleSwitch.vue.d.ts.map +1 -0
  18. package/dist/components/{chat/ui → header}/ChatHeader.vue.d.ts +5 -3
  19. package/dist/components/header/ChatHeader.vue.d.ts.map +1 -0
  20. package/dist/components/input/AtFilePicker.vue.d.ts +21 -0
  21. package/dist/components/input/AtFilePicker.vue.d.ts.map +1 -0
  22. package/dist/components/{ChatInput.vue.d.ts → input/ChatInput.vue.d.ts} +16 -14
  23. package/dist/components/input/ChatInput.vue.d.ts.map +1 -0
  24. package/dist/components/input/DropdownSelector.vue.d.ts +42 -0
  25. package/dist/components/input/DropdownSelector.vue.d.ts.map +1 -0
  26. package/dist/components/input/ImagePreviewModal.vue.d.ts +17 -0
  27. package/dist/components/input/ImagePreviewModal.vue.d.ts.map +1 -0
  28. package/dist/components/input/at-views/AtBranchView.vue.d.ts +18 -0
  29. package/dist/components/input/at-views/AtBranchView.vue.d.ts.map +1 -0
  30. package/dist/components/input/at-views/AtBrowserView.vue.d.ts +18 -0
  31. package/dist/components/input/at-views/AtBrowserView.vue.d.ts.map +1 -0
  32. package/dist/components/input/at-views/AtChatsView.vue.d.ts +18 -0
  33. package/dist/components/input/at-views/AtChatsView.vue.d.ts.map +1 -0
  34. package/dist/components/input/at-views/AtDocsView.vue.d.ts +18 -0
  35. package/dist/components/input/at-views/AtDocsView.vue.d.ts.map +1 -0
  36. package/dist/components/input/at-views/AtFilesView.vue.d.ts +23 -0
  37. package/dist/components/input/at-views/AtFilesView.vue.d.ts.map +1 -0
  38. package/dist/components/input/at-views/AtTerminalsView.vue.d.ts +18 -0
  39. package/dist/components/input/at-views/AtTerminalsView.vue.d.ts.map +1 -0
  40. package/dist/components/message/MessageBubble.vue.d.ts +45 -0
  41. package/dist/components/message/MessageBubble.vue.d.ts.map +1 -0
  42. package/dist/components/message/PartsRenderer.vue.d.ts +15 -0
  43. package/dist/components/message/PartsRenderer.vue.d.ts.map +1 -0
  44. package/dist/components/message/WelcomeMessage.vue.d.ts +14 -0
  45. package/dist/components/message/WelcomeMessage.vue.d.ts.map +1 -0
  46. package/dist/components/message/blocks/CodeBlock.vue.d.ts +11 -0
  47. package/dist/components/message/blocks/CodeBlock.vue.d.ts.map +1 -0
  48. package/dist/components/{chat/SearchResultBlock.vue.d.ts → message/blocks/TextBlock.vue.d.ts} +3 -4
  49. package/dist/components/message/blocks/TextBlock.vue.d.ts.map +1 -0
  50. package/dist/components/message/blocks/index.d.ts +6 -0
  51. package/dist/components/message/blocks/index.d.ts.map +1 -0
  52. package/dist/components/message/parts/CollapsibleCard.vue.d.ts +45 -0
  53. package/dist/components/message/parts/CollapsibleCard.vue.d.ts.map +1 -0
  54. package/dist/components/{chat/ToolCallBlock.vue.d.ts → message/parts/ErrorPart.vue.d.ts} +4 -5
  55. package/dist/components/message/parts/ErrorPart.vue.d.ts.map +1 -0
  56. package/dist/components/{chat/ThinkingBlock.vue.d.ts → message/parts/ImagePart.vue.d.ts} +3 -3
  57. package/dist/components/message/parts/ImagePart.vue.d.ts.map +1 -0
  58. package/dist/components/message/parts/SearchPart.vue.d.ts +12 -0
  59. package/dist/components/message/parts/SearchPart.vue.d.ts.map +1 -0
  60. package/dist/components/{chat/messages/ExecutionSteps.vue.d.ts → message/parts/TextPart.vue.d.ts} +2 -9
  61. package/dist/components/message/parts/TextPart.vue.d.ts.map +1 -0
  62. package/dist/components/message/parts/ThinkingPart.vue.d.ts +12 -0
  63. package/dist/components/message/parts/ThinkingPart.vue.d.ts.map +1 -0
  64. package/dist/components/message/parts/ToolCallPart.vue.d.ts +19 -0
  65. package/dist/components/message/parts/ToolCallPart.vue.d.ts.map +1 -0
  66. package/dist/components/message/parts/ToolResultPart.vue.d.ts +14 -0
  67. package/dist/components/message/parts/ToolResultPart.vue.d.ts.map +1 -0
  68. package/dist/components/message/parts/index.d.ts +12 -0
  69. package/dist/components/message/parts/index.d.ts.map +1 -0
  70. package/dist/components/message/tool-results/DefaultToolResult.vue.d.ts +4 -0
  71. package/dist/components/message/tool-results/DefaultToolResult.vue.d.ts.map +1 -0
  72. package/dist/components/message/tool-results/SearchResults.vue.d.ts +4 -0
  73. package/dist/components/message/tool-results/SearchResults.vue.d.ts.map +1 -0
  74. package/dist/components/message/tool-results/WeatherCard.vue.d.ts +4 -0
  75. package/dist/components/message/tool-results/WeatherCard.vue.d.ts.map +1 -0
  76. package/dist/components/message/tool-results/index.d.ts +7 -0
  77. package/dist/components/message/tool-results/index.d.ts.map +1 -0
  78. package/dist/components/message/welcome-types.d.ts +28 -0
  79. package/dist/components/message/welcome-types.d.ts.map +1 -0
  80. package/dist/composables/useChat.d.ts +99 -44
  81. package/dist/composables/useChat.d.ts.map +1 -1
  82. package/dist/composables/useImageUpload.d.ts +55 -0
  83. package/dist/composables/useImageUpload.d.ts.map +1 -0
  84. package/dist/index.d.ts +25 -26
  85. package/dist/index.d.ts.map +1 -1
  86. package/dist/index.js +55871 -1252
  87. package/dist/style.css +1 -1
  88. package/dist/types/index.d.ts +113 -53
  89. package/dist/types/index.d.ts.map +1 -1
  90. package/dist/utils/fileIcon.d.ts +13 -0
  91. package/dist/utils/fileIcon.d.ts.map +1 -0
  92. package/package.json +12 -6
  93. package/src/adapter.ts +12 -70
  94. package/src/components/ChatPanel.vue +329 -110
  95. package/src/components/common/ConfirmDialog.vue +208 -0
  96. package/src/components/common/CopyButton.vue +71 -0
  97. package/src/components/common/IndexingSettings.vue +580 -0
  98. package/src/components/common/SettingsPanel.vue +293 -0
  99. package/src/components/common/Toast.vue +90 -0
  100. package/src/components/common/ToggleSwitch.vue +75 -0
  101. package/src/components/{chat/ui → header}/ChatHeader.vue +170 -93
  102. package/src/components/input/AtFilePicker.vue +657 -0
  103. package/src/components/input/ChatInput.vue +653 -0
  104. package/src/components/input/DropdownSelector.vue +322 -0
  105. package/src/components/input/ImagePreviewModal.vue +238 -0
  106. package/src/components/input/at-views/AtBranchView.vue +63 -0
  107. package/src/components/input/at-views/AtBrowserView.vue +63 -0
  108. package/src/components/input/at-views/AtChatsView.vue +63 -0
  109. package/src/components/input/at-views/AtDocsView.vue +63 -0
  110. package/src/components/input/at-views/AtFilesView.vue +255 -0
  111. package/src/components/input/at-views/AtTerminalsView.vue +63 -0
  112. package/src/components/message/ContentRenderer.vue +61 -0
  113. package/src/components/message/MessageBubble.vue +411 -0
  114. package/src/components/message/PartsRenderer.vue +101 -0
  115. package/src/components/message/ToolResultRenderer.vue +27 -0
  116. package/src/components/message/WelcomeMessage.vue +308 -0
  117. package/src/components/message/blocks/CodeBlock.vue +113 -0
  118. package/src/components/message/blocks/TextBlock.vue +21 -0
  119. package/src/components/message/blocks/index.ts +6 -0
  120. package/src/components/message/parts/CollapsibleCard.vue +135 -0
  121. package/src/components/message/parts/ErrorPart.vue +51 -0
  122. package/src/components/message/parts/ImagePart.vue +98 -0
  123. package/src/components/message/parts/SearchPart.vue +101 -0
  124. package/src/components/message/parts/TextPart.vue +28 -0
  125. package/src/components/message/parts/ThinkingPart.vue +54 -0
  126. package/src/components/message/parts/ToolCallPart.vue +460 -0
  127. package/src/components/message/parts/ToolResultPart.vue +78 -0
  128. package/src/components/message/parts/index.ts +13 -0
  129. package/src/components/message/tool-results/DefaultToolResult.vue +43 -0
  130. package/src/components/message/tool-results/SearchResults.vue +133 -0
  131. package/src/components/message/tool-results/WeatherCard.vue +139 -0
  132. package/src/components/message/tool-results/index.ts +7 -0
  133. package/src/components/message/welcome-types.ts +47 -0
  134. package/src/composables/useChat.ts +807 -155
  135. package/src/composables/useImageUpload.ts +228 -0
  136. package/src/index.ts +93 -46
  137. package/src/styles.css +47 -0
  138. package/src/types/index.ts +146 -98
  139. package/src/utils/fileIcon.ts +49 -0
  140. package/dist/components/ChatInput.vue.d.ts.map +0 -1
  141. package/dist/components/chat/SearchResultBlock.vue.d.ts.map +0 -1
  142. package/dist/components/chat/ThinkingBlock.vue.d.ts.map +0 -1
  143. package/dist/components/chat/ToolCallBlock.vue.d.ts.map +0 -1
  144. package/dist/components/chat/messages/ExecutionSteps.vue.d.ts.map +0 -1
  145. package/dist/components/chat/messages/MessageBubble.vue.d.ts +0 -28
  146. package/dist/components/chat/messages/MessageBubble.vue.d.ts.map +0 -1
  147. package/dist/components/chat/ui/ChatHeader.vue.d.ts.map +0 -1
  148. package/dist/components/chat/ui/WelcomeMessage.vue.d.ts +0 -7
  149. package/dist/components/chat/ui/WelcomeMessage.vue.d.ts.map +0 -1
  150. package/dist/preload/preload.d.ts +0 -6
  151. package/dist/preload/preload.d.ts.map +0 -1
  152. package/src/components/ChatInput.vue +0 -649
  153. package/src/components/chat/SearchResultBlock.vue +0 -155
  154. package/src/components/chat/ThinkingBlock.vue +0 -109
  155. package/src/components/chat/ToolCallBlock.vue +0 -213
  156. package/src/components/chat/messages/ExecutionSteps.vue +0 -281
  157. package/src/components/chat/messages/MessageBubble.vue +0 -272
  158. package/src/components/chat/ui/WelcomeMessage.vue +0 -135
  159. package/src/preload/preload.ts +0 -79
@@ -0,0 +1,293 @@
1
+ <template>
2
+ <div v-if="visible" class="settings-panel-overlay" @click.self="visible = false">
3
+ <div class="settings-panel">
4
+ <!-- 左侧导航 -->
5
+ <div class="settings-sidebar">
6
+ <div class="sidebar-header">
7
+ <h3 class="sidebar-title">设置</h3>
8
+ </div>
9
+ <div class="sidebar-content">
10
+ <button
11
+ v-for="section in sections"
12
+ :key="section.id"
13
+ :class="['sidebar-item', { active: currentSection === section.id }]"
14
+ @click="currentSection = section.id"
15
+ >
16
+ <Icon :icon="section.icon" width="18" />
17
+ <span>{{ section.label }}</span>
18
+ </button>
19
+ </div>
20
+ </div>
21
+
22
+ <!-- 右侧内容 -->
23
+ <div class="settings-content">
24
+ <div class="content-header">
25
+ <h2 class="content-title">{{ currentSectionLabel }}</h2>
26
+ <button class="close-btn" @click="visible = false">
27
+ <Icon icon="lucide:x" width="20" />
28
+ </button>
29
+ </div>
30
+ <div class="content-body">
31
+ <!-- Agent 设置 -->
32
+ <template v-if="currentSection === 'agent'">
33
+ <!-- 自动运行模式 -->
34
+ <div class="setting-item">
35
+ <div class="setting-info">
36
+ <div class="setting-label">自动运行模式</div>
37
+ <div class="setting-description">控制工具执行的自动运行行为</div>
38
+ </div>
39
+ <div class="setting-control">
40
+ <DropdownSelector
41
+ :value="config.mode ?? 'run-everything'"
42
+ :options="modeOptions"
43
+ align="right"
44
+ @select="handleModeChange"
45
+ />
46
+ </div>
47
+ </div>
48
+ </template>
49
+
50
+ <!-- 索引与文档设置 -->
51
+ <template v-if="currentSection === 'indexing'">
52
+ <IndexingSettings />
53
+ </template>
54
+ </div>
55
+ </div>
56
+ </div>
57
+ </div>
58
+ </template>
59
+
60
+ <script setup lang="ts">
61
+ import { ref, computed } from 'vue'
62
+ import { Icon } from '@iconify/vue'
63
+ import type { AutoRunConfig, AutoRunMode } from '@huyooo/ai-chat-bridge-electron/renderer'
64
+ import DropdownSelector from '../input/DropdownSelector.vue'
65
+ import ToggleSwitch from './ToggleSwitch.vue'
66
+ import IndexingSettings from './IndexingSettings.vue'
67
+
68
+ // 双向绑定:visible 和 config
69
+ const visible = defineModel<boolean>('visible', { required: true })
70
+ const config = defineModel<AutoRunConfig>('config', { required: true })
71
+
72
+ const emit = defineEmits<{
73
+ save: [config: AutoRunConfig]
74
+ }>()
75
+
76
+ // 设置分类
77
+ const sections = [
78
+ { id: 'agent', label: 'Agent', icon: 'solar:link-round-angle-line-duotone' },
79
+ { id: 'indexing', label: '索引与文档', icon: 'lucide:database' },
80
+ ] as const
81
+
82
+ const currentSection = ref<string>('agent')
83
+
84
+ const currentSectionLabel = computed(() =>
85
+ sections.find(s => s.id === currentSection.value)?.label ?? ''
86
+ )
87
+
88
+ // 模式选项
89
+ const modeOptions = [
90
+ { value: 'run-everything', label: '运行所有内容(自动执行)' },
91
+ { value: 'manual', label: '手动批准(每次执行前询问)' },
92
+ ]
93
+
94
+ /** 更新配置项并触发保存 */
95
+ function updateConfig<K extends keyof AutoRunConfig>(key: K, value: AutoRunConfig[K]) {
96
+ // 直接修改引用类型的属性,v-model 会自动同步
97
+ config.value[key] = value
98
+ emit('save', config.value)
99
+ }
100
+
101
+ /** 处理模式变化 */
102
+ function handleModeChange(value: string) {
103
+ updateConfig('mode', value as AutoRunMode)
104
+ }
105
+ </script>
106
+
107
+ <style scoped>
108
+ .settings-panel-overlay {
109
+ position: fixed;
110
+ top: 0;
111
+ left: 0;
112
+ right: 0;
113
+ bottom: 0;
114
+ background: rgba(0, 0, 0, 0.7);
115
+ display: flex;
116
+ align-items: center;
117
+ justify-content: center;
118
+ z-index: 1000;
119
+ }
120
+
121
+ .settings-panel {
122
+ background: var(--chat-bg, #1e1e1e);
123
+ border: 1px solid var(--chat-border, #333);
124
+ border-radius: 12px;
125
+ width: 90%;
126
+ max-width: 900px;
127
+ height: 80vh;
128
+ max-height: 700px;
129
+ display: flex;
130
+ flex-direction: row;
131
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
132
+ overflow: hidden;
133
+ }
134
+
135
+ /* 左侧导航 */
136
+ .settings-sidebar {
137
+ width: 240px;
138
+ background: var(--chat-muted, #2a2a2a);
139
+ border-right: 1px solid var(--chat-border, #333);
140
+ display: flex;
141
+ flex-direction: column;
142
+ flex-shrink: 0;
143
+ }
144
+
145
+ .sidebar-header {
146
+ display: flex;
147
+ align-items: center;
148
+ height: 50px;
149
+ padding: 0 16px;
150
+ border-bottom: 1px solid var(--chat-border, #333);
151
+ }
152
+
153
+ .sidebar-title {
154
+ margin: 0;
155
+ font-size: 15px;
156
+ font-weight: 600;
157
+ color: var(--chat-text, #fff);
158
+ }
159
+
160
+ .sidebar-content {
161
+ flex: 1;
162
+ overflow-y: auto;
163
+ padding: 8px;
164
+ }
165
+
166
+ .sidebar-item {
167
+ display: flex;
168
+ align-items: center;
169
+ gap: 12px;
170
+ width: 100%;
171
+ padding: 10px 12px;
172
+ margin-bottom: 2px;
173
+ background: transparent;
174
+ border: none;
175
+ border-radius: 8px;
176
+ font-size: 14px;
177
+ font-weight: 500;
178
+ color: var(--chat-text-muted, #888);
179
+ cursor: pointer;
180
+ transition: all 0.15s ease;
181
+ text-align: left;
182
+ }
183
+
184
+ .sidebar-item:hover {
185
+ background: rgba(255, 255, 255, 0.05);
186
+ color: var(--chat-text, #ccc);
187
+ }
188
+
189
+ .sidebar-item.active {
190
+ background: rgba(255, 255, 255, 0.1);
191
+ color: var(--chat-text, #fff);
192
+ }
193
+
194
+ /* 右侧内容 */
195
+ .settings-content {
196
+ flex: 1;
197
+ display: flex;
198
+ flex-direction: column;
199
+ overflow: hidden;
200
+ }
201
+
202
+ .content-header {
203
+ display: flex;
204
+ align-items: center;
205
+ justify-content: space-between;
206
+ height: 50px;
207
+ padding: 0 16px;
208
+ border-bottom: 1px solid var(--chat-border, #333);
209
+ }
210
+
211
+ .content-title {
212
+ margin: 0;
213
+ font-size: 15px;
214
+ font-weight: 600;
215
+ color: var(--chat-text, #fff);
216
+ }
217
+
218
+ .close-btn {
219
+ display: flex;
220
+ align-items: center;
221
+ justify-content: center;
222
+ width: 32px;
223
+ height: 32px;
224
+ padding: 0;
225
+ background: transparent;
226
+ border: none;
227
+ border-radius: 6px;
228
+ color: var(--chat-text-muted, #888);
229
+ cursor: pointer;
230
+ transition: all 0.15s;
231
+ }
232
+
233
+ .close-btn:hover {
234
+ background: var(--chat-muted, #2a2a2a);
235
+ color: var(--chat-text, #fff);
236
+ }
237
+
238
+ .content-body {
239
+ flex: 1;
240
+ overflow-y: auto;
241
+ display: flex;
242
+ flex-direction: column;
243
+ gap: 24px;
244
+ padding: 16px;
245
+ }
246
+
247
+ .section-title {
248
+ font-size: 16px;
249
+ font-weight: 500;
250
+ color: var(--chat-text, #fff);
251
+ margin-bottom: 4px;
252
+ }
253
+
254
+ .section-description {
255
+ font-size: 13px;
256
+ color: var(--chat-text-muted, #888);
257
+ margin-bottom: 16px;
258
+ }
259
+
260
+ .setting-item {
261
+ display: flex;
262
+ align-items: center;
263
+ justify-content: space-between;
264
+ padding: 16px 0;
265
+ border-bottom: 1px solid var(--chat-border, #333);
266
+ }
267
+
268
+ .setting-item:last-child {
269
+ border-bottom: none;
270
+ }
271
+
272
+ .setting-info {
273
+ flex: 1;
274
+ margin-right: 24px;
275
+ }
276
+
277
+ .setting-label {
278
+ font-size: 14px;
279
+ font-weight: 500;
280
+ color: var(--chat-text, #fff);
281
+ margin-bottom: 4px;
282
+ }
283
+
284
+ .setting-description {
285
+ font-size: 13px;
286
+ color: var(--chat-text-muted, #888);
287
+ line-height: 1.5;
288
+ }
289
+
290
+ .setting-control {
291
+ flex-shrink: 0;
292
+ }
293
+ </style>
@@ -0,0 +1,90 @@
1
+ <script setup lang="ts">
2
+ import { ref, watch } from 'vue';
3
+
4
+ const props = withDefaults(
5
+ defineProps<{
6
+ message: string;
7
+ type?: 'info' | 'success' | 'warning' | 'error';
8
+ duration?: number;
9
+ }>(),
10
+ {
11
+ type: 'info',
12
+ duration: 2000,
13
+ }
14
+ );
15
+
16
+ const visible = defineModel<boolean>('visible', { default: false });
17
+
18
+ // 自动关闭
19
+ watch(visible, (val) => {
20
+ if (val && props.duration > 0) {
21
+ setTimeout(() => {
22
+ visible.value = false;
23
+ }, props.duration);
24
+ }
25
+ });
26
+ </script>
27
+
28
+ <template>
29
+ <Teleport to="body">
30
+ <Transition name="toast">
31
+ <div v-if="visible" class="toast-container">
32
+ <div :class="['toast', `toast-${type}`]">
33
+ {{ message }}
34
+ </div>
35
+ </div>
36
+ </Transition>
37
+ </Teleport>
38
+ </template>
39
+
40
+ <style scoped>
41
+ .toast-container {
42
+ position: fixed;
43
+ top: 60px;
44
+ left: 50%;
45
+ transform: translateX(-50%);
46
+ z-index: 10000;
47
+ pointer-events: none;
48
+ }
49
+
50
+ .toast {
51
+ padding: 8px 16px;
52
+ border-radius: 6px;
53
+ font-size: 13px;
54
+ border: 1px solid;
55
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
56
+ }
57
+
58
+ /* 正常/成功 - 灰色 */
59
+ .toast-info,
60
+ .toast-success {
61
+ background: var(--chat-bg-secondary, #2a2a2a);
62
+ color: var(--chat-text, #fff);
63
+ border-color: var(--chat-border, #444);
64
+ }
65
+
66
+ /* 警告 - 黄色 */
67
+ .toast-warning {
68
+ background: rgba(245, 158, 11, 0.15);
69
+ color: #f59e0b;
70
+ border-color: rgba(245, 158, 11, 0.3);
71
+ }
72
+
73
+ /* 错误 - 红色 */
74
+ .toast-error {
75
+ background: rgba(239, 68, 68, 0.15);
76
+ color: #ef4444;
77
+ border-color: rgba(239, 68, 68, 0.3);
78
+ }
79
+
80
+ .toast-enter-active,
81
+ .toast-leave-active {
82
+ transition: all 0.2s ease;
83
+ }
84
+
85
+ .toast-enter-from,
86
+ .toast-leave-to {
87
+ opacity: 0;
88
+ transform: translateX(-50%) translateY(-10px);
89
+ }
90
+ </style>
@@ -0,0 +1,75 @@
1
+ <template>
2
+ <label class="toggle-switch">
3
+ <input
4
+ type="checkbox"
5
+ :checked="checked"
6
+ @change="emit('change', ($event.target as HTMLInputElement).checked)"
7
+ />
8
+ <span class="toggle-slider"></span>
9
+ </label>
10
+ </template>
11
+
12
+ <script setup lang="ts">
13
+ defineProps<{
14
+ checked?: boolean
15
+ }>()
16
+
17
+ const emit = defineEmits<{
18
+ change: [value: boolean]
19
+ }>()
20
+ </script>
21
+
22
+ <style scoped>
23
+ .toggle-switch {
24
+ position: relative;
25
+ display: inline-block;
26
+ width: 44px;
27
+ height: 24px;
28
+ cursor: pointer;
29
+ }
30
+
31
+ .toggle-switch input {
32
+ opacity: 0;
33
+ width: 0;
34
+ height: 0;
35
+ }
36
+
37
+ .toggle-slider {
38
+ position: absolute;
39
+ top: 0;
40
+ left: 0;
41
+ right: 0;
42
+ bottom: 0;
43
+ background-color: var(--chat-muted, #3c3c3c);
44
+ border-radius: 24px;
45
+ transition: all 0.2s;
46
+ border: 1px solid rgba(255, 255, 255, 0.1);
47
+ }
48
+
49
+ .toggle-slider::before {
50
+ position: absolute;
51
+ content: "";
52
+ height: 18px;
53
+ width: 18px;
54
+ left: 3px;
55
+ top: 50%;
56
+ transform: translateY(-50%);
57
+ background-color: #fff;
58
+ border-radius: 50%;
59
+ transition: all 0.2s;
60
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
61
+ }
62
+
63
+ .toggle-switch input:checked + .toggle-slider {
64
+ background-color: rgb(153, 207, 140);
65
+ border-color: rgba(255, 255, 255, 0.2);
66
+ }
67
+
68
+ .toggle-switch input:checked + .toggle-slider::before {
69
+ transform: translate(20px, -50%);
70
+ }
71
+
72
+ .toggle-switch input:focus + .toggle-slider {
73
+ box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.2);
74
+ }
75
+ </style>