@h-ai/ui 0.1.0-alpha.10

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 (237) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +500 -0
  3. package/auto-import.d.ts +96 -0
  4. package/auto-import.js +263 -0
  5. package/dist/components/compounds/Accordion.svelte +142 -0
  6. package/dist/components/compounds/Accordion.svelte.d.ts +25 -0
  7. package/dist/components/compounds/ActionSheet.svelte +116 -0
  8. package/dist/components/compounds/ActionSheet.svelte.d.ts +22 -0
  9. package/dist/components/compounds/Alert.svelte +67 -0
  10. package/dist/components/compounds/Alert.svelte.d.ts +8 -0
  11. package/dist/components/compounds/AppBar.svelte +77 -0
  12. package/dist/components/compounds/AppBar.svelte.d.ts +20 -0
  13. package/dist/components/compounds/BottomNav.svelte +75 -0
  14. package/dist/components/compounds/BottomNav.svelte.d.ts +18 -0
  15. package/dist/components/compounds/Breadcrumb.svelte +49 -0
  16. package/dist/components/compounds/Breadcrumb.svelte.d.ts +4 -0
  17. package/dist/components/compounds/Calendar.svelte +116 -0
  18. package/dist/components/compounds/Calendar.svelte.d.ts +47 -0
  19. package/dist/components/compounds/Card.svelte +77 -0
  20. package/dist/components/compounds/Card.svelte.d.ts +8 -0
  21. package/dist/components/compounds/Combobox.svelte +327 -0
  22. package/dist/components/compounds/Combobox.svelte.d.ts +69 -0
  23. package/dist/components/compounds/Confirm.svelte +90 -0
  24. package/dist/components/compounds/Confirm.svelte.d.ts +4 -0
  25. package/dist/components/compounds/DataTable.svelte +133 -0
  26. package/dist/components/compounds/DataTable.svelte.d.ts +69 -0
  27. package/dist/components/compounds/DatePicker.svelte +135 -0
  28. package/dist/components/compounds/DatePicker.svelte.d.ts +46 -0
  29. package/dist/components/compounds/Drawer.svelte +113 -0
  30. package/dist/components/compounds/Drawer.svelte.d.ts +4 -0
  31. package/dist/components/compounds/Dropdown.svelte +91 -0
  32. package/dist/components/compounds/Dropdown.svelte.d.ts +8 -0
  33. package/dist/components/compounds/Empty.svelte +83 -0
  34. package/dist/components/compounds/Empty.svelte.d.ts +4 -0
  35. package/dist/components/compounds/Form.svelte +96 -0
  36. package/dist/components/compounds/Form.svelte.d.ts +7 -0
  37. package/dist/components/compounds/FormField.svelte +56 -0
  38. package/dist/components/compounds/FormField.svelte.d.ts +4 -0
  39. package/dist/components/compounds/InfiniteScroll.svelte +82 -0
  40. package/dist/components/compounds/InfiniteScroll.svelte.d.ts +22 -0
  41. package/dist/components/compounds/Modal.svelte +123 -0
  42. package/dist/components/compounds/Modal.svelte.d.ts +8 -0
  43. package/dist/components/compounds/PageHeader.svelte +44 -0
  44. package/dist/components/compounds/PageHeader.svelte.d.ts +28 -0
  45. package/dist/components/compounds/Pagination.svelte +157 -0
  46. package/dist/components/compounds/Pagination.svelte.d.ts +4 -0
  47. package/dist/components/compounds/Popover.svelte +137 -0
  48. package/dist/components/compounds/Popover.svelte.d.ts +4 -0
  49. package/dist/components/compounds/PullRefresh.svelte +98 -0
  50. package/dist/components/compounds/PullRefresh.svelte.d.ts +16 -0
  51. package/dist/components/compounds/Result.svelte +84 -0
  52. package/dist/components/compounds/Result.svelte.d.ts +4 -0
  53. package/dist/components/compounds/SafeArea.svelte +37 -0
  54. package/dist/components/compounds/SafeArea.svelte.d.ts +12 -0
  55. package/dist/components/compounds/Skeleton.svelte +75 -0
  56. package/dist/components/compounds/Skeleton.svelte.d.ts +8 -0
  57. package/dist/components/compounds/Steps.svelte +111 -0
  58. package/dist/components/compounds/Steps.svelte.d.ts +4 -0
  59. package/dist/components/compounds/SwipeCell.svelte +115 -0
  60. package/dist/components/compounds/SwipeCell.svelte.d.ts +19 -0
  61. package/dist/components/compounds/Tabs.svelte +79 -0
  62. package/dist/components/compounds/Tabs.svelte.d.ts +8 -0
  63. package/dist/components/compounds/TagInput.svelte +108 -0
  64. package/dist/components/compounds/TagInput.svelte.d.ts +4 -0
  65. package/dist/components/compounds/Timeline.svelte +167 -0
  66. package/dist/components/compounds/Timeline.svelte.d.ts +25 -0
  67. package/dist/components/compounds/ToastContainer.svelte +59 -0
  68. package/dist/components/compounds/ToastContainer.svelte.d.ts +3 -0
  69. package/dist/components/compounds/Tooltip.svelte +41 -0
  70. package/dist/components/compounds/Tooltip.svelte.d.ts +4 -0
  71. package/dist/components/compounds/accordion-types.d.ts +16 -0
  72. package/dist/components/compounds/accordion-types.js +5 -0
  73. package/dist/components/compounds/action-sheet-types.d.ts +14 -0
  74. package/dist/components/compounds/action-sheet-types.js +5 -0
  75. package/dist/components/compounds/bottom-nav-types.d.ts +17 -0
  76. package/dist/components/compounds/bottom-nav-types.js +5 -0
  77. package/dist/components/compounds/index.d.ts +44 -0
  78. package/dist/components/compounds/index.js +54 -0
  79. package/dist/components/compounds/swipe-cell-types.d.ts +14 -0
  80. package/dist/components/compounds/swipe-cell-types.js +5 -0
  81. package/dist/components/compounds/timeline-types.d.ts +20 -0
  82. package/dist/components/compounds/timeline-types.js +5 -0
  83. package/dist/components/index.d.ts +9 -0
  84. package/dist/components/index.js +12 -0
  85. package/dist/components/primitives/Avatar.svelte +104 -0
  86. package/dist/components/primitives/Avatar.svelte.d.ts +8 -0
  87. package/dist/components/primitives/Badge.svelte +33 -0
  88. package/dist/components/primitives/Badge.svelte.d.ts +8 -0
  89. package/dist/components/primitives/BareButton.svelte +45 -0
  90. package/dist/components/primitives/BareButton.svelte.d.ts +4 -0
  91. package/dist/components/primitives/BareInput.svelte +101 -0
  92. package/dist/components/primitives/BareInput.svelte.d.ts +4 -0
  93. package/dist/components/primitives/Button.svelte +60 -0
  94. package/dist/components/primitives/Button.svelte.d.ts +8 -0
  95. package/dist/components/primitives/Checkbox.svelte +58 -0
  96. package/dist/components/primitives/Checkbox.svelte.d.ts +4 -0
  97. package/dist/components/primitives/IconButton.svelte +105 -0
  98. package/dist/components/primitives/IconButton.svelte.d.ts +4 -0
  99. package/dist/components/primitives/Input.svelte +130 -0
  100. package/dist/components/primitives/Input.svelte.d.ts +8 -0
  101. package/dist/components/primitives/Progress.svelte +58 -0
  102. package/dist/components/primitives/Progress.svelte.d.ts +4 -0
  103. package/dist/components/primitives/Radio.svelte +60 -0
  104. package/dist/components/primitives/Radio.svelte.d.ts +25 -0
  105. package/dist/components/primitives/Range.svelte +117 -0
  106. package/dist/components/primitives/Range.svelte.d.ts +27 -0
  107. package/dist/components/primitives/Rating.svelte +148 -0
  108. package/dist/components/primitives/Rating.svelte.d.ts +27 -0
  109. package/dist/components/primitives/Select.svelte +95 -0
  110. package/dist/components/primitives/Select.svelte.d.ts +25 -0
  111. package/dist/components/primitives/Spinner.svelte +54 -0
  112. package/dist/components/primitives/Spinner.svelte.d.ts +4 -0
  113. package/dist/components/primitives/Switch.svelte +53 -0
  114. package/dist/components/primitives/Switch.svelte.d.ts +4 -0
  115. package/dist/components/primitives/Tag.svelte +64 -0
  116. package/dist/components/primitives/Tag.svelte.d.ts +4 -0
  117. package/dist/components/primitives/Textarea.svelte +92 -0
  118. package/dist/components/primitives/Textarea.svelte.d.ts +4 -0
  119. package/dist/components/primitives/ToggleCheckbox.svelte +36 -0
  120. package/dist/components/primitives/ToggleCheckbox.svelte.d.ts +4 -0
  121. package/dist/components/primitives/ToggleInput.svelte +36 -0
  122. package/dist/components/primitives/ToggleInput.svelte.d.ts +4 -0
  123. package/dist/components/primitives/ToggleRadio.svelte +36 -0
  124. package/dist/components/primitives/ToggleRadio.svelte.d.ts +4 -0
  125. package/dist/components/primitives/index.d.ts +26 -0
  126. package/dist/components/primitives/index.js +30 -0
  127. package/dist/components/scenes/ai/AiDocumentDownloadMenu.svelte +387 -0
  128. package/dist/components/scenes/ai/AiDocumentDownloadMenu.svelte.d.ts +38 -0
  129. package/dist/components/scenes/ai/AiDocumentEditor.svelte +2209 -0
  130. package/dist/components/scenes/ai/AiDocumentEditor.svelte.d.ts +4 -0
  131. package/dist/components/scenes/ai/MarkdownRenderer.svelte +412 -0
  132. package/dist/components/scenes/ai/MarkdownRenderer.svelte.d.ts +4 -0
  133. package/dist/components/scenes/ai/document-download.d.ts +40 -0
  134. package/dist/components/scenes/ai/document-download.js +300 -0
  135. package/dist/components/scenes/ai/document-parse.d.ts +28 -0
  136. package/dist/components/scenes/ai/document-parse.js +243 -0
  137. package/dist/components/scenes/ai/document-types.d.ts +164 -0
  138. package/dist/components/scenes/ai/document-types.js +1 -0
  139. package/dist/components/scenes/ai/highlight.d.ts +26 -0
  140. package/dist/components/scenes/ai/highlight.js +151 -0
  141. package/dist/components/scenes/ai/index.d.ts +17 -0
  142. package/dist/components/scenes/ai/index.js +13 -0
  143. package/dist/components/scenes/ai/markdown-parse.d.ts +32 -0
  144. package/dist/components/scenes/ai/markdown-parse.js +164 -0
  145. package/dist/components/scenes/app/FeedbackModal.svelte +160 -0
  146. package/dist/components/scenes/app/FeedbackModal.svelte.d.ts +47 -0
  147. package/dist/components/scenes/app/LanguageSwitch.svelte +136 -0
  148. package/dist/components/scenes/app/LanguageSwitch.svelte.d.ts +50 -0
  149. package/dist/components/scenes/app/SettingsModal.svelte +174 -0
  150. package/dist/components/scenes/app/SettingsModal.svelte.d.ts +31 -0
  151. package/dist/components/scenes/app/ThemeSelector.svelte +176 -0
  152. package/dist/components/scenes/app/ThemeSelector.svelte.d.ts +31 -0
  153. package/dist/components/scenes/app/ThemeToggle.svelte +76 -0
  154. package/dist/components/scenes/app/ThemeToggle.svelte.d.ts +29 -0
  155. package/dist/components/scenes/app/index.d.ts +11 -0
  156. package/dist/components/scenes/app/index.js +11 -0
  157. package/dist/components/scenes/crud/CrudDeleteConfirm.svelte +39 -0
  158. package/dist/components/scenes/crud/CrudDeleteConfirm.svelte.d.ts +18 -0
  159. package/dist/components/scenes/crud/CrudDetailDrawer.svelte +155 -0
  160. package/dist/components/scenes/crud/CrudDetailDrawer.svelte.d.ts +38 -0
  161. package/dist/components/scenes/crud/CrudEditDrawer.svelte +225 -0
  162. package/dist/components/scenes/crud/CrudEditDrawer.svelte.d.ts +48 -0
  163. package/dist/components/scenes/crud/CrudFilterBar.svelte +96 -0
  164. package/dist/components/scenes/crud/CrudFilterBar.svelte.d.ts +31 -0
  165. package/dist/components/scenes/crud/CrudPage.svelte +499 -0
  166. package/dist/components/scenes/crud/CrudPage.svelte.d.ts +114 -0
  167. package/dist/components/scenes/crud/crud-types.d.ts +135 -0
  168. package/dist/components/scenes/crud/crud-types.js +7 -0
  169. package/dist/components/scenes/crud/index.d.ts +12 -0
  170. package/dist/components/scenes/crud/index.js +11 -0
  171. package/dist/components/scenes/crypto/EncryptedInput.svelte +156 -0
  172. package/dist/components/scenes/crypto/EncryptedInput.svelte.d.ts +32 -0
  173. package/dist/components/scenes/crypto/HashDisplay.svelte +97 -0
  174. package/dist/components/scenes/crypto/HashDisplay.svelte.d.ts +4 -0
  175. package/dist/components/scenes/crypto/SignatureDisplay.svelte +161 -0
  176. package/dist/components/scenes/crypto/SignatureDisplay.svelte.d.ts +4 -0
  177. package/dist/components/scenes/crypto/index.d.ts +9 -0
  178. package/dist/components/scenes/crypto/index.js +11 -0
  179. package/dist/components/scenes/iam/ChangePasswordForm.svelte +134 -0
  180. package/dist/components/scenes/iam/ChangePasswordForm.svelte.d.ts +8 -0
  181. package/dist/components/scenes/iam/ForgotPasswordForm.svelte +147 -0
  182. package/dist/components/scenes/iam/ForgotPasswordForm.svelte.d.ts +4 -0
  183. package/dist/components/scenes/iam/LoginForm.svelte +191 -0
  184. package/dist/components/scenes/iam/LoginForm.svelte.d.ts +8 -0
  185. package/dist/components/scenes/iam/PasswordInput.svelte +208 -0
  186. package/dist/components/scenes/iam/PasswordInput.svelte.d.ts +49 -0
  187. package/dist/components/scenes/iam/PermGuard.svelte +69 -0
  188. package/dist/components/scenes/iam/PermGuard.svelte.d.ts +16 -0
  189. package/dist/components/scenes/iam/RegisterForm.svelte +269 -0
  190. package/dist/components/scenes/iam/RegisterForm.svelte.d.ts +8 -0
  191. package/dist/components/scenes/iam/ResetPasswordForm.svelte +153 -0
  192. package/dist/components/scenes/iam/ResetPasswordForm.svelte.d.ts +8 -0
  193. package/dist/components/scenes/iam/UserProfile.svelte +251 -0
  194. package/dist/components/scenes/iam/UserProfile.svelte.d.ts +4 -0
  195. package/dist/components/scenes/iam/index.d.ts +16 -0
  196. package/dist/components/scenes/iam/index.js +18 -0
  197. package/dist/components/scenes/iam/password-utils.d.ts +15 -0
  198. package/dist/components/scenes/iam/password-utils.js +19 -0
  199. package/dist/components/scenes/iam/permission-context.svelte.d.ts +90 -0
  200. package/dist/components/scenes/iam/permission-context.svelte.js +114 -0
  201. package/dist/components/scenes/index.d.ts +13 -0
  202. package/dist/components/scenes/index.js +20 -0
  203. package/dist/components/scenes/storage/AvatarUpload.svelte +218 -0
  204. package/dist/components/scenes/storage/AvatarUpload.svelte.d.ts +4 -0
  205. package/dist/components/scenes/storage/FileList.svelte +260 -0
  206. package/dist/components/scenes/storage/FileList.svelte.d.ts +4 -0
  207. package/dist/components/scenes/storage/FileUpload.svelte +402 -0
  208. package/dist/components/scenes/storage/FileUpload.svelte.d.ts +7 -0
  209. package/dist/components/scenes/storage/ImageUpload.svelte +287 -0
  210. package/dist/components/scenes/storage/ImageUpload.svelte.d.ts +4 -0
  211. package/dist/components/scenes/storage/index.d.ts +10 -0
  212. package/dist/components/scenes/storage/index.js +12 -0
  213. package/dist/components/scenes/types.d.ts +583 -0
  214. package/dist/components/scenes/types.js +7 -0
  215. package/dist/i18n.svelte.d.ts +44 -0
  216. package/dist/i18n.svelte.js +108 -0
  217. package/dist/index.d.ts +13 -0
  218. package/dist/index.js +19 -0
  219. package/dist/messages/en-US.json +242 -0
  220. package/dist/messages/zh-CN.json +242 -0
  221. package/dist/messages.d.ts +29 -0
  222. package/dist/messages.js +42 -0
  223. package/dist/styles/design-tokens.css +58 -0
  224. package/dist/styles/global.css +258 -0
  225. package/dist/styles/mobile.css +105 -0
  226. package/dist/styles/theme.css +31 -0
  227. package/dist/theme-config.d.ts +92 -0
  228. package/dist/theme-config.js +122 -0
  229. package/dist/toast.svelte.d.ts +45 -0
  230. package/dist/toast.svelte.js +77 -0
  231. package/dist/types.d.ts +944 -0
  232. package/dist/types.js +7 -0
  233. package/dist/utils/platform.svelte.d.ts +54 -0
  234. package/dist/utils/platform.svelte.js +89 -0
  235. package/dist/utils.d.ts +46 -0
  236. package/dist/utils.js +163 -0
  237. package/package.json +70 -0
@@ -0,0 +1,160 @@
1
+ <!--
2
+ @component FeedbackModal
3
+ 用户反馈模态框,支持 Bug 报告、功能建议等多种反馈类型。
4
+
5
+ @prop {boolean} open - 是否显示模态框(双向绑定)
6
+ @prop {function} onsubmit - 提交反馈的回调
7
+
8
+ @example
9
+ <FeedbackModal
10
+ bind:open={showFeedback}
11
+ onsubmit={async (data) => { await submitFeedback(data) }}
12
+ />
13
+ -->
14
+ <script lang='ts'>
15
+ import Modal from '../../compounds/Modal.svelte'
16
+ import Button from '../../primitives/Button.svelte'
17
+ import Input from '../../primitives/Input.svelte'
18
+ import Select from '../../primitives/Select.svelte'
19
+ import Textarea from '../../primitives/Textarea.svelte'
20
+
21
+ type FeedbackType = 'bug' | 'feature' | 'question' | 'other'
22
+
23
+ interface FeedbackData {
24
+ type: FeedbackType
25
+ description: string
26
+ contact?: string
27
+ }
28
+
29
+ /** i18n 文案配置 */
30
+ interface FeedbackLabels {
31
+ title?: string
32
+ description?: string
33
+ typeLabel?: string
34
+ contentLabel?: string
35
+ contentPlaceholder?: string
36
+ contactLabel?: string
37
+ contactPlaceholder?: string
38
+ cancel?: string
39
+ submit?: string
40
+ errorEmpty?: string
41
+ errorSubmit?: string
42
+ types?: {
43
+ bug?: string
44
+ feature?: string
45
+ question?: string
46
+ other?: string
47
+ }
48
+ }
49
+
50
+ import { uiM } from '../../../messages.js'
51
+
52
+ interface Props {
53
+ open?: boolean
54
+ labels?: FeedbackLabels
55
+ onsubmit?: (data: FeedbackData) => Promise<void>
56
+ }
57
+
58
+ let { open = $bindable(false), labels = {}, onsubmit }: Props = $props()
59
+
60
+ // 文案优先使用传入的 labels,缺省回退到内置消息 uiM(...)
61
+
62
+ let feedbackType = $state<FeedbackType>('bug')
63
+ let description = $state('')
64
+ let contact = $state('')
65
+ let loading = $state(false)
66
+ let error = $state('')
67
+
68
+ const typeOptions = $derived([
69
+ { value: 'bug', label: labels.types?.bug ?? uiM('feedback_type_bug') },
70
+ { value: 'feature', label: labels.types?.feature ?? uiM('feedback_type_feature') },
71
+ { value: 'question', label: labels.types?.question ?? uiM('feedback_type_question') },
72
+ { value: 'other', label: labels.types?.other ?? uiM('feedback_type_other') },
73
+ ])
74
+
75
+ async function handleSubmit() {
76
+ if (!description.trim()) {
77
+ error = labels.errorEmpty ?? uiM('feedback_error_empty')
78
+ return
79
+ }
80
+
81
+ loading = true
82
+ error = ''
83
+
84
+ try {
85
+ await onsubmit?.({
86
+ type: feedbackType,
87
+ description: description.trim(),
88
+ contact: contact.trim() || undefined,
89
+ })
90
+ open = false
91
+ resetForm()
92
+ }
93
+ catch (e) {
94
+ error = e instanceof Error ? e.message : (labels.errorSubmit ?? uiM('feedback_error_submit'))
95
+ }
96
+ finally {
97
+ loading = false
98
+ }
99
+ }
100
+
101
+ function resetForm() {
102
+ feedbackType = 'bug'
103
+ description = ''
104
+ contact = ''
105
+ error = ''
106
+ }
107
+ </script>
108
+
109
+ <Modal bind:open title={labels.title ?? uiM('feedback_title')}>
110
+ <p class='text-base-content/70 mb-4'>{labels.description ?? uiM('feedback_description')}</p>
111
+
112
+ {#if error}
113
+ <div class='alert alert-error mb-4'>
114
+ <span class='icon-[tabler--alert-circle] size-5'></span>
115
+ <span>{error}</span>
116
+ </div>
117
+ {/if}
118
+
119
+ <div class='space-y-4'>
120
+ <div class='fieldset'>
121
+ <legend class='fieldset-legend'>{labels.typeLabel ?? uiM('feedback_type_label')}</legend>
122
+ <Select
123
+ id='feedback-type'
124
+ options={typeOptions}
125
+ bind:value={feedbackType}
126
+ />
127
+ </div>
128
+
129
+ <div class='fieldset'>
130
+ <legend class='fieldset-legend'>{labels.contentLabel ?? uiM('feedback_content_label')} <span class='text-error'>*</span></legend>
131
+ <Textarea
132
+ id='feedback-desc'
133
+ class='h-32'
134
+ placeholder={labels.contentPlaceholder ?? uiM('feedback_content_placeholder')}
135
+ bind:value={description}
136
+ />
137
+ </div>
138
+
139
+ <div class='fieldset'>
140
+ <legend class='fieldset-legend'>{labels.contactLabel ?? uiM('feedback_contact_label')}</legend>
141
+ <Input
142
+ id='feedback-contact'
143
+ type='text'
144
+ class='w-full'
145
+ placeholder={labels.contactPlaceholder ?? uiM('feedback_contact_placeholder')}
146
+ bind:value={contact}
147
+ autocomplete='email'
148
+ />
149
+ </div>
150
+ </div>
151
+
152
+ {#snippet footer()}
153
+ <Button variant='ghost' onclick={() => open = false}>
154
+ {labels.cancel ?? uiM('feedback_cancel')}
155
+ </Button>
156
+ <Button variant='primary' {loading} onclick={handleSubmit}>
157
+ {labels.submit ?? uiM('feedback_submit')}
158
+ </Button>
159
+ {/snippet}
160
+ </Modal>
@@ -0,0 +1,47 @@
1
+ type FeedbackType = 'bug' | 'feature' | 'question' | 'other';
2
+ interface FeedbackData {
3
+ type: FeedbackType;
4
+ description: string;
5
+ contact?: string;
6
+ }
7
+ /** i18n 文案配置 */
8
+ interface FeedbackLabels {
9
+ title?: string;
10
+ description?: string;
11
+ typeLabel?: string;
12
+ contentLabel?: string;
13
+ contentPlaceholder?: string;
14
+ contactLabel?: string;
15
+ contactPlaceholder?: string;
16
+ cancel?: string;
17
+ submit?: string;
18
+ errorEmpty?: string;
19
+ errorSubmit?: string;
20
+ types?: {
21
+ bug?: string;
22
+ feature?: string;
23
+ question?: string;
24
+ other?: string;
25
+ };
26
+ }
27
+ interface Props {
28
+ open?: boolean;
29
+ labels?: FeedbackLabels;
30
+ onsubmit?: (data: FeedbackData) => Promise<void>;
31
+ }
32
+ /**
33
+ * FeedbackModal
34
+ * 用户反馈模态框,支持 Bug 报告、功能建议等多种反馈类型。
35
+ *
36
+ * @prop {boolean} open - 是否显示模态框(双向绑定)
37
+ * @prop {function} onsubmit - 提交反馈的回调
38
+ *
39
+ * @example
40
+ * <FeedbackModal
41
+ * bind:open={showFeedback}
42
+ * onsubmit={async (data) => { await submitFeedback(data) }}
43
+ * />
44
+ */
45
+ declare const FeedbackModal: import("svelte").Component<Props, {}, "open">;
46
+ type FeedbackModal = ReturnType<typeof FeedbackModal>;
47
+ export default FeedbackModal;
@@ -0,0 +1,136 @@
1
+ <!--
2
+ @component LanguageSwitch
3
+ 语言切换下拉组件,支持多语言切换。
4
+
5
+ @prop {string} currentLanguage - 当前语言代码(BCP 47 格式,如 'zh-CN', 'en-US')
6
+ @prop {Language[]} languages - 可选语言列表
7
+ @prop {function} onchange - 语言变更回调,传入选中的语言代码
8
+
9
+ @example
10
+ ```svelte
11
+ <script>
12
+ import { LanguageSwitch, createLocaleStore, setGlobalLocale } from '@h-ai/ui'
13
+ import { setLocale, getLocale } from '../../../paraglide/runtime.js'
14
+
15
+ const localeStore = createLocaleStore()
16
+ </script>
17
+
18
+ <LanguageSwitch
19
+ currentLanguage={localeStore.current}
20
+ languages={[
21
+ { value: 'zh-CN', label: '简体中文' },
22
+ { value: 'en-US', label: 'English' }
23
+ ]}
24
+ onchange={(lang) => {
25
+ localeStore.set(lang) // 同步到 @h-ai/core 全局 locale
26
+ setLocale(lang) // 同步到 Paraglide
27
+ }}
28
+ />
29
+ ```
30
+ -->
31
+ <script lang='ts'>
32
+ import { uiM } from '../../../messages.js'
33
+ import BareButton from '../../primitives/BareButton.svelte'
34
+
35
+ interface Language {
36
+ value: string
37
+ label: string
38
+ flag?: string
39
+ }
40
+
41
+ interface Props {
42
+ /** 当前语言代码(BCP 47 格式) */
43
+ currentLanguage?: string
44
+ /** 可选语言列表 */
45
+ languages?: Language[]
46
+ /** 语言变更回调 */
47
+ onchange?: (lang: string) => void
48
+ /** 紧凑模式:仅显示图标,适用于移动端头部 */
49
+ compact?: boolean
50
+ /** 自定义类名 */
51
+ class?: string
52
+ }
53
+
54
+ const {
55
+ currentLanguage = 'zh-CN',
56
+ languages = [
57
+ { value: 'zh-CN', label: '简体中文', flag: 'CN' },
58
+ { value: 'en-US', label: 'English', flag: 'US' },
59
+ ],
60
+ onchange,
61
+ compact = false,
62
+ class: className = '',
63
+ }: Props = $props()
64
+
65
+ let open = $state(false)
66
+ let containerRef = $state<HTMLDivElement | null>(null)
67
+
68
+ const currentLangInfo = $derived(
69
+ languages.find(l => l.value === currentLanguage),
70
+ )
71
+
72
+ function selectLanguage(lang: string) {
73
+ if (lang !== currentLanguage) {
74
+ onchange?.(lang)
75
+ }
76
+ open = false
77
+ }
78
+
79
+ function handleClickOutside(event: MouseEvent) {
80
+ if (containerRef && !containerRef.contains(event.target as Node)) {
81
+ open = false
82
+ }
83
+ }
84
+
85
+ $effect(() => {
86
+ if (open) {
87
+ document.addEventListener('click', handleClickOutside)
88
+ return () => document.removeEventListener('click', handleClickOutside)
89
+ }
90
+ return undefined
91
+ })
92
+ </script>
93
+
94
+ <div bind:this={containerRef} class='dropdown dropdown-end {open ? 'dropdown-open' : ''} {className}'>
95
+ <BareButton
96
+ type='button'
97
+ class='btn btn-ghost {compact ? 'btn-sm btn-square' : 'btn-sm gap-2 min-w-fit'}'
98
+ onclick={() => (open = !open)}
99
+ ariaLabel={uiM('language_switch_label')}
100
+ >
101
+ <svg xmlns='http://www.w3.org/2000/svg' class='h-4 w-4 shrink-0' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'>
102
+ <path d='M3 5h12M9 3v2m1.048 9.5A18.022 18.022 0 016.412 9m6.088 9h7M11 21l5-10 5 10M12.751 5C11.783 10.77 8.07 15.61 3 18.129' />
103
+ </svg>
104
+ {#if !compact}
105
+ {#if currentLangInfo?.flag}
106
+ <span class='shrink-0 text-xs'>{currentLangInfo.flag}</span>
107
+ {/if}
108
+ <span class='text-sm whitespace-nowrap'>{currentLangInfo?.label ?? currentLanguage}</span>
109
+ <svg xmlns='http://www.w3.org/2000/svg' class='h-4 w-4 shrink-0 opacity-50' viewBox='0 0 20 20' fill='currentColor'>
110
+ <path fill-rule='evenodd' d='M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z' clip-rule='evenodd' />
111
+ </svg>
112
+ {/if}
113
+ </BareButton>
114
+
115
+ {#if open}
116
+ <div class='dropdown-content bg-base-100 rounded-box shadow-lg border border-base-content/10 z-50 p-2'>
117
+ {#each languages as lang (lang.value)}
118
+ <BareButton
119
+ type='button'
120
+ class='flex items-center gap-2 w-full px-3 py-2 rounded-lg text-left transition-colors whitespace-nowrap {lang.value === currentLanguage ? 'bg-primary/10 text-primary' : 'hover:bg-base-200'}'
121
+ onclick={() => selectLanguage(lang.value)}
122
+ >
123
+ {#if lang.flag}
124
+ <span class='shrink-0 text-xs font-medium text-base-content/60'>{lang.flag}</span>
125
+ {/if}
126
+ <span class='whitespace-nowrap'>{lang.label}</span>
127
+ {#if lang.value === currentLanguage}
128
+ <svg xmlns='http://www.w3.org/2000/svg' class='h-4 w-4 ml-auto shrink-0' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'>
129
+ <path d='M5 13l4 4L19 7' />
130
+ </svg>
131
+ {/if}
132
+ </BareButton>
133
+ {/each}
134
+ </div>
135
+ {/if}
136
+ </div>
@@ -0,0 +1,50 @@
1
+ interface Language {
2
+ value: string;
3
+ label: string;
4
+ flag?: string;
5
+ }
6
+ interface Props {
7
+ /** 当前语言代码(BCP 47 格式) */
8
+ currentLanguage?: string;
9
+ /** 可选语言列表 */
10
+ languages?: Language[];
11
+ /** 语言变更回调 */
12
+ onchange?: (lang: string) => void;
13
+ /** 紧凑模式:仅显示图标,适用于移动端头部 */
14
+ compact?: boolean;
15
+ /** 自定义类名 */
16
+ class?: string;
17
+ }
18
+ /**
19
+ * LanguageSwitch
20
+ * 语言切换下拉组件,支持多语言切换。
21
+ *
22
+ * @prop {string} currentLanguage - 当前语言代码(BCP 47 格式,如 'zh-CN', 'en-US')
23
+ * @prop {Language[]} languages - 可选语言列表
24
+ * @prop {function} onchange - 语言变更回调,传入选中的语言代码
25
+ *
26
+ * @example
27
+ * ```svelte
28
+ * <script>
29
+ * import { LanguageSwitch, createLocaleStore, setGlobalLocale } from '@h-ai/ui'
30
+ * import { setLocale, getLocale } from '../../../paraglide/runtime.js'
31
+ *
32
+ * const localeStore = createLocaleStore()
33
+ * </script>
34
+ *
35
+ * <LanguageSwitch
36
+ * currentLanguage={localeStore.current}
37
+ * languages={[
38
+ * { value: 'zh-CN', label: '简体中文' },
39
+ * { value: 'en-US', label: 'English' }
40
+ * ]}
41
+ * onchange={(lang) => {
42
+ * localeStore.set(lang) // 同步到 @h-ai/core 全局 locale
43
+ * setLocale(lang) // 同步到 Paraglide
44
+ * }}
45
+ * />
46
+ * ```
47
+ */
48
+ declare const LanguageSwitch: import("svelte").Component<Props, {}, "">;
49
+ type LanguageSwitch = ReturnType<typeof LanguageSwitch>;
50
+ export default LanguageSwitch;
@@ -0,0 +1,174 @@
1
+ <!--
2
+ @component SettingsModal
3
+ 应用设置模态框,包含语言和主题切换功能。
4
+ 内置多语言支持,自动跟随全局 locale
5
+
6
+ @prop {boolean} open - 是否显示模态框(双向绑定)
7
+ @prop {string} currentLanguage - 当前语言
8
+ @prop {string} currentTheme - 当前主题
9
+ @prop {function} onlanguagechange - 语言变更回调
10
+ @prop {function} onthemechange - 主题变更回调
11
+ -->
12
+ <script lang='ts'>
13
+ import { uiM } from '../../../messages.js'
14
+ import BareButton from '../../primitives/BareButton.svelte'
15
+ import IconButton from '../../primitives/IconButton.svelte'
16
+
17
+ interface Props {
18
+ open?: boolean
19
+ currentLanguage?: string
20
+ currentTheme?: string
21
+ languages?: { value: string, label: string, icon?: string }[]
22
+ themes?: { value: string, label: string }[]
23
+ onlanguagechange?: (lang: string) => void
24
+ onthemechange?: (theme: string) => void
25
+ onclose?: () => void
26
+ }
27
+
28
+ let {
29
+ open = $bindable(false),
30
+ currentLanguage = 'zh-cn',
31
+ currentTheme = 'light',
32
+ languages = [
33
+ { value: 'zh-cn', label: '简体中文', icon: '🇨🇳' },
34
+ { value: 'en-us', label: 'English', icon: '🇺🇸' },
35
+ ],
36
+ themes = [
37
+ { value: 'light', label: 'Light' },
38
+ { value: 'dark', label: 'Dark' },
39
+ { value: 'corporate', label: 'Corporate' },
40
+ { value: 'luxury', label: 'Luxury' },
41
+ { value: 'pastel', label: 'Pastel' },
42
+ ],
43
+ onlanguagechange,
44
+ onthemechange,
45
+ onclose,
46
+ }: Props = $props()
47
+
48
+ function handleClose() {
49
+ open = false
50
+ onclose?.()
51
+ }
52
+
53
+ function handleBackdropClick(e: MouseEvent) {
54
+ if (e.target === e.currentTarget) {
55
+ handleClose()
56
+ }
57
+ }
58
+
59
+ function handleKeydown(e: KeyboardEvent) {
60
+ if (e.key === 'Escape') {
61
+ handleClose()
62
+ }
63
+ }
64
+ </script>
65
+
66
+ <svelte:window onkeydown={handleKeydown} />
67
+
68
+ {#if open}
69
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
70
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
71
+ <div
72
+ class='fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm transition-opacity'
73
+ onclick={handleBackdropClick}
74
+ >
75
+ <div
76
+ class='relative w-full max-w-lg mx-4 transform overflow-hidden rounded-2xl bg-base-100 shadow-xl transition-all'
77
+ role='dialog'
78
+ aria-modal='true'
79
+ >
80
+ <!-- 标题栏 -->
81
+ <div class='flex items-center justify-between border-b border-base-content/10 px-6 py-4'>
82
+ <h3 class='text-lg font-semibold text-base-content'>{uiM('settings_title')}</h3>
83
+ <IconButton
84
+ size='sm'
85
+ variant='ghost'
86
+ label={uiM('settings_close')}
87
+ onclick={handleClose}
88
+ class='hover:bg-base-content/10'
89
+ >
90
+ {#snippet children()}
91
+ <span class='icon-[tabler--x] size-5'></span>
92
+ {/snippet}
93
+ </IconButton>
94
+ </div>
95
+
96
+ <!-- 内容区 -->
97
+ <div class='overflow-y-auto p-6' style='max-height: calc(85vh - 4rem);'>
98
+ <!-- 语言设置 -->
99
+ <section class='mb-8'>
100
+ <h4 class='mb-4 flex items-center gap-2 text-sm font-semibold uppercase tracking-wider text-base-content/60'>
101
+ <span class='icon-[tabler--world] size-4'></span>
102
+ {uiM('settings_language')}
103
+ </h4>
104
+ <div class='flex gap-3'>
105
+ {#each languages as lang (lang.value)}
106
+ <BareButton
107
+ type='button'
108
+ class='group flex flex-1 items-center justify-center gap-3 rounded-xl border-2 px-6 py-4 text-base font-medium transition-all duration-200
109
+ hover:border-primary/60 hover:bg-primary/5 hover:shadow-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40
110
+ {currentLanguage === lang.value
111
+ ? 'border-primary bg-primary/10 text-primary shadow-sm'
112
+ : 'border-base-content/15 bg-base-100 text-base-content/80'}'
113
+ onclick={() => onlanguagechange?.(lang.value)}
114
+ role='option'
115
+ ariaSelected={currentLanguage === lang.value}
116
+ >
117
+ {#if lang.icon}
118
+ <span class='text-2xl'>{lang.icon}</span>
119
+ {/if}
120
+ <span>{lang.label}</span>
121
+ {#if currentLanguage === lang.value}
122
+ <span class='icon-[tabler--check] size-5 text-primary'></span>
123
+ {/if}
124
+ </BareButton>
125
+ {/each}
126
+ </div>
127
+ </section>
128
+
129
+ <!-- 主题设置 -->
130
+ <section>
131
+ <h4 class='mb-4 flex items-center gap-2 text-sm font-semibold uppercase tracking-wider text-base-content/60'>
132
+ <span class='icon-[tabler--sun] size-4'></span>
133
+ {uiM('settings_theme')}
134
+ </h4>
135
+ <div class='grid gap-2.5 grid-cols-2 sm:grid-cols-3'>
136
+ {#each themes as theme (theme.value)}
137
+ <BareButton
138
+ type='button'
139
+ class='group relative flex flex-col gap-2 rounded-xl border-2 p-3 text-left transition-all duration-200
140
+ hover:border-primary/50 hover:shadow-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40
141
+ {currentTheme === theme.value
142
+ ? 'border-primary bg-primary/5 shadow-sm'
143
+ : 'border-base-content/10 bg-base-100 hover:bg-base-50'}'
144
+ onclick={() => onthemechange?.(theme.value)}
145
+ role='option'
146
+ ariaSelected={currentTheme === theme.value}
147
+ >
148
+ {#if currentTheme === theme.value}
149
+ <div class='absolute -right-1 -top-1 rounded-full bg-primary p-0.5 text-primary-content shadow'>
150
+ <span class='icon-[tabler--check] size-3'></span>
151
+ </div>
152
+ {/if}
153
+
154
+ <!-- 主题预览 -->
155
+ <div data-theme={theme.value} class='flex items-center gap-2 rounded-lg bg-base-100 p-2 ring-1 ring-base-content/10' aria-hidden='true'>
156
+ <span class='text-sm font-bold text-base-content'>Aa</span>
157
+ <div class='flex flex-1 gap-1'>
158
+ <span class='h-3 flex-1 rounded bg-primary'></span>
159
+ <span class='h-3 flex-1 rounded bg-secondary'></span>
160
+ <span class='h-3 flex-1 rounded bg-accent'></span>
161
+ </div>
162
+ </div>
163
+
164
+ <span class='text-xs font-medium text-base-content/80 group-hover:text-base-content'>
165
+ {theme.label}
166
+ </span>
167
+ </BareButton>
168
+ {/each}
169
+ </div>
170
+ </section>
171
+ </div>
172
+ </div>
173
+ </div>
174
+ {/if}
@@ -0,0 +1,31 @@
1
+ interface Props {
2
+ open?: boolean;
3
+ currentLanguage?: string;
4
+ currentTheme?: string;
5
+ languages?: {
6
+ value: string;
7
+ label: string;
8
+ icon?: string;
9
+ }[];
10
+ themes?: {
11
+ value: string;
12
+ label: string;
13
+ }[];
14
+ onlanguagechange?: (lang: string) => void;
15
+ onthemechange?: (theme: string) => void;
16
+ onclose?: () => void;
17
+ }
18
+ /**
19
+ * SettingsModal
20
+ * 应用设置模态框,包含语言和主题切换功能。
21
+ * 内置多语言支持,自动跟随全局 locale
22
+ *
23
+ * @prop {boolean} open - 是否显示模态框(双向绑定)
24
+ * @prop {string} currentLanguage - 当前语言
25
+ * @prop {string} currentTheme - 当前主题
26
+ * @prop {function} onlanguagechange - 语言变更回调
27
+ * @prop {function} onthemechange - 主题变更回调
28
+ */
29
+ declare const SettingsModal: import("svelte").Component<Props, {}, "open">;
30
+ type SettingsModal = ReturnType<typeof SettingsModal>;
31
+ export default SettingsModal;