@h-ai/ui 0.1.0-alpha.29 → 0.1.0-alpha.31

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 (194) hide show
  1. package/README.md +50 -3
  2. package/dist/components/compounds/Accordion.svelte +6 -3
  3. package/dist/components/compounds/Accordion.svelte.d.ts +3 -1
  4. package/dist/components/compounds/ActionSheet.svelte +11 -8
  5. package/dist/components/compounds/ActionSheet.svelte.d.ts +3 -1
  6. package/dist/components/compounds/Alert.svelte +6 -4
  7. package/dist/components/compounds/Alert.svelte.d.ts +3 -2
  8. package/dist/components/compounds/AppBar.svelte +13 -9
  9. package/dist/components/compounds/AppBar.svelte.d.ts +3 -1
  10. package/dist/components/compounds/BottomNav.svelte +13 -10
  11. package/dist/components/compounds/BottomNav.svelte.d.ts +3 -1
  12. package/dist/components/compounds/Breadcrumb.svelte +6 -4
  13. package/dist/components/compounds/Breadcrumb.svelte.d.ts +3 -2
  14. package/dist/components/compounds/Calendar.svelte +7 -2
  15. package/dist/components/compounds/Calendar.svelte.d.ts +3 -1
  16. package/dist/components/compounds/Card.svelte +6 -4
  17. package/dist/components/compounds/Card.svelte.d.ts +3 -2
  18. package/dist/components/compounds/Combobox.svelte +6 -2
  19. package/dist/components/compounds/Combobox.svelte.d.ts +3 -1
  20. package/dist/components/compounds/Confirm.svelte +9 -7
  21. package/dist/components/compounds/Confirm.svelte.d.ts +3 -2
  22. package/dist/components/compounds/DataTable.svelte +6 -2
  23. package/dist/components/compounds/DataTable.svelte.d.ts +2 -1
  24. package/dist/components/compounds/DatePicker.svelte +7 -2
  25. package/dist/components/compounds/DatePicker.svelte.d.ts +3 -1
  26. package/dist/components/compounds/Drawer.svelte +6 -4
  27. package/dist/components/compounds/Drawer.svelte.d.ts +3 -2
  28. package/dist/components/compounds/Dropdown.svelte +6 -4
  29. package/dist/components/compounds/Dropdown.svelte.d.ts +3 -2
  30. package/dist/components/compounds/Empty.svelte +6 -4
  31. package/dist/components/compounds/Empty.svelte.d.ts +3 -2
  32. package/dist/components/compounds/Form.svelte +11 -9
  33. package/dist/components/compounds/Form.svelte.d.ts +3 -2
  34. package/dist/components/compounds/FormField.svelte +6 -4
  35. package/dist/components/compounds/FormField.svelte.d.ts +3 -2
  36. package/dist/components/compounds/InfiniteScroll.svelte +10 -7
  37. package/dist/components/compounds/InfiniteScroll.svelte.d.ts +3 -1
  38. package/dist/components/compounds/MessageBoxContainer.svelte +6 -1
  39. package/dist/components/compounds/MessageBoxContainer.svelte.d.ts +3 -17
  40. package/dist/components/compounds/MessageBoxDialog.svelte +9 -7
  41. package/dist/components/compounds/MessageBoxDialog.svelte.d.ts +2 -1
  42. package/dist/components/compounds/MessageContainer.svelte +43 -66
  43. package/dist/components/compounds/MessageContainer.svelte.d.ts +2 -1
  44. package/dist/components/compounds/Modal.svelte +29 -43
  45. package/dist/components/compounds/Modal.svelte.d.ts +3 -2
  46. package/dist/components/compounds/PageHeader.svelte +6 -2
  47. package/dist/components/compounds/PageHeader.svelte.d.ts +3 -1
  48. package/dist/components/compounds/Pagination.svelte +6 -4
  49. package/dist/components/compounds/Pagination.svelte.d.ts +3 -2
  50. package/dist/components/compounds/Popover.svelte +10 -8
  51. package/dist/components/compounds/Popover.svelte.d.ts +3 -2
  52. package/dist/components/compounds/PullRefresh.svelte +11 -8
  53. package/dist/components/compounds/PullRefresh.svelte.d.ts +3 -1
  54. package/dist/components/compounds/Result.svelte +6 -4
  55. package/dist/components/compounds/Result.svelte.d.ts +3 -2
  56. package/dist/components/compounds/SafeArea.svelte +6 -3
  57. package/dist/components/compounds/SafeArea.svelte.d.ts +3 -1
  58. package/dist/components/compounds/Skeleton.svelte +6 -4
  59. package/dist/components/compounds/Skeleton.svelte.d.ts +3 -2
  60. package/dist/components/compounds/Steps.svelte +6 -4
  61. package/dist/components/compounds/Steps.svelte.d.ts +3 -2
  62. package/dist/components/compounds/SwipeCell.svelte +6 -3
  63. package/dist/components/compounds/SwipeCell.svelte.d.ts +3 -1
  64. package/dist/components/compounds/Tabs.svelte +6 -4
  65. package/dist/components/compounds/Tabs.svelte.d.ts +3 -2
  66. package/dist/components/compounds/TagInput.svelte +11 -9
  67. package/dist/components/compounds/TagInput.svelte.d.ts +3 -2
  68. package/dist/components/compounds/Timeline.svelte +6 -3
  69. package/dist/components/compounds/Timeline.svelte.d.ts +3 -1
  70. package/dist/components/compounds/ToastContainer.svelte +6 -2
  71. package/dist/components/compounds/ToastContainer.svelte.d.ts +2 -1
  72. package/dist/components/compounds/Tooltip.svelte +6 -4
  73. package/dist/components/compounds/Tooltip.svelte.d.ts +3 -2
  74. package/dist/components/primitives/Avatar.svelte +6 -4
  75. package/dist/components/primitives/Avatar.svelte.d.ts +3 -2
  76. package/dist/components/primitives/Badge.svelte +6 -4
  77. package/dist/components/primitives/Badge.svelte.d.ts +3 -2
  78. package/dist/components/primitives/BareButton.svelte +15 -12
  79. package/dist/components/primitives/BareButton.svelte.d.ts +3 -2
  80. package/dist/components/primitives/BareInput.svelte +37 -34
  81. package/dist/components/primitives/BareInput.svelte.d.ts +3 -2
  82. package/dist/components/primitives/Button.svelte +12 -10
  83. package/dist/components/primitives/Button.svelte.d.ts +3 -2
  84. package/dist/components/primitives/Checkbox.svelte +6 -4
  85. package/dist/components/primitives/Checkbox.svelte.d.ts +3 -2
  86. package/dist/components/primitives/IconButton.svelte +6 -4
  87. package/dist/components/primitives/IconButton.svelte.d.ts +3 -2
  88. package/dist/components/primitives/Input.svelte +6 -4
  89. package/dist/components/primitives/Input.svelte.d.ts +3 -2
  90. package/dist/components/primitives/Progress.svelte +6 -4
  91. package/dist/components/primitives/Progress.svelte.d.ts +3 -2
  92. package/dist/components/primitives/Radio.svelte +6 -4
  93. package/dist/components/primitives/Radio.svelte.d.ts +2 -2
  94. package/dist/components/primitives/Range.svelte +6 -3
  95. package/dist/components/primitives/Range.svelte.d.ts +3 -1
  96. package/dist/components/primitives/Rating.svelte +6 -3
  97. package/dist/components/primitives/Rating.svelte.d.ts +3 -1
  98. package/dist/components/primitives/Select.svelte +255 -48
  99. package/dist/components/primitives/Select.svelte.d.ts +2 -2
  100. package/dist/components/primitives/Spinner.svelte +6 -4
  101. package/dist/components/primitives/Spinner.svelte.d.ts +3 -2
  102. package/dist/components/primitives/Switch.svelte +6 -4
  103. package/dist/components/primitives/Switch.svelte.d.ts +3 -2
  104. package/dist/components/primitives/Tag.svelte +6 -4
  105. package/dist/components/primitives/Tag.svelte.d.ts +3 -2
  106. package/dist/components/primitives/Textarea.svelte +34 -19
  107. package/dist/components/primitives/Textarea.svelte.d.ts +3 -2
  108. package/dist/components/primitives/ToggleCheckbox.svelte +13 -10
  109. package/dist/components/primitives/ToggleCheckbox.svelte.d.ts +3 -2
  110. package/dist/components/primitives/ToggleInput.svelte +13 -10
  111. package/dist/components/primitives/ToggleInput.svelte.d.ts +3 -2
  112. package/dist/components/primitives/ToggleRadio.svelte +13 -10
  113. package/dist/components/primitives/ToggleRadio.svelte.d.ts +3 -2
  114. package/dist/components/scenes/ai/AiDocumentDownloadMenu.svelte +8 -5
  115. package/dist/components/scenes/ai/AiDocumentDownloadMenu.svelte.d.ts +3 -1
  116. package/dist/components/scenes/ai/AiDocumentEditor.svelte +40 -10
  117. package/dist/components/scenes/ai/AiDocumentEditor.svelte.d.ts +3 -1
  118. package/dist/components/scenes/ai/MarkdownRenderer.svelte +49 -7
  119. package/dist/components/scenes/ai/MarkdownRenderer.svelte.d.ts +3 -1
  120. package/dist/components/scenes/ai/document-parse.d.ts +2 -0
  121. package/dist/components/scenes/ai/document-parse.js +16 -17
  122. package/dist/components/scenes/ai/document-types.d.ts +6 -1
  123. package/dist/components/scenes/ai/markdown-parse.d.ts +2 -0
  124. package/dist/components/scenes/ai/markdown-parse.js +17 -20
  125. package/dist/components/scenes/ai/safe-html.d.ts +16 -0
  126. package/dist/components/scenes/ai/safe-html.js +284 -0
  127. package/dist/components/scenes/ai/table/AiTableEditor.svelte +6 -3
  128. package/dist/components/scenes/ai/table/AiTableEditor.svelte.d.ts +3 -1
  129. package/dist/components/scenes/ai/table/table-types.d.ts +2 -1
  130. package/dist/components/scenes/app/FeedbackModal.svelte +5 -2
  131. package/dist/components/scenes/app/FeedbackModal.svelte.d.ts +3 -1
  132. package/dist/components/scenes/app/LanguageSwitch.svelte +6 -2
  133. package/dist/components/scenes/app/LanguageSwitch.svelte.d.ts +3 -1
  134. package/dist/components/scenes/app/SettingsLayout.svelte +6 -3
  135. package/dist/components/scenes/app/SettingsLayout.svelte.d.ts +2 -1
  136. package/dist/components/scenes/app/SettingsModal.svelte +6 -3
  137. package/dist/components/scenes/app/SettingsModal.svelte.d.ts +3 -1
  138. package/dist/components/scenes/app/ThemeColorPicker.svelte +6 -3
  139. package/dist/components/scenes/app/ThemeColorPicker.svelte.d.ts +3 -1
  140. package/dist/components/scenes/app/ThemeSelector.svelte +6 -3
  141. package/dist/components/scenes/app/ThemeSelector.svelte.d.ts +3 -1
  142. package/dist/components/scenes/app/ThemeToggle.svelte +6 -2
  143. package/dist/components/scenes/app/ThemeToggle.svelte.d.ts +3 -1
  144. package/dist/components/scenes/crud/CrudDeleteConfirm.svelte +16 -11
  145. package/dist/components/scenes/crud/CrudDeleteConfirm.svelte.d.ts +2 -1
  146. package/dist/components/scenes/crud/CrudDetailDrawer.svelte +6 -3
  147. package/dist/components/scenes/crud/CrudDetailDrawer.svelte.d.ts +2 -2
  148. package/dist/components/scenes/crud/CrudDetailPanel.svelte +6 -3
  149. package/dist/components/scenes/crud/CrudDetailPanel.svelte.d.ts +2 -2
  150. package/dist/components/scenes/crud/CrudEditPanel.svelte +6 -3
  151. package/dist/components/scenes/crud/CrudEditPanel.svelte.d.ts +2 -2
  152. package/dist/components/scenes/crud/CrudFilterBar.svelte +6 -2
  153. package/dist/components/scenes/crud/CrudFilterBar.svelte.d.ts +2 -1
  154. package/dist/components/scenes/crud/CrudPage.svelte +6 -2
  155. package/dist/components/scenes/crud/CrudPage.svelte.d.ts +2 -1
  156. package/dist/components/scenes/crud/crud-types.d.ts +2 -2
  157. package/dist/components/scenes/crypto/EncryptedInput.svelte +6 -3
  158. package/dist/components/scenes/crypto/EncryptedInput.svelte.d.ts +3 -2
  159. package/dist/components/scenes/crypto/HashDisplay.svelte +6 -3
  160. package/dist/components/scenes/crypto/HashDisplay.svelte.d.ts +3 -1
  161. package/dist/components/scenes/crypto/SignatureDisplay.svelte +6 -3
  162. package/dist/components/scenes/crypto/SignatureDisplay.svelte.d.ts +3 -1
  163. package/dist/components/scenes/error/ErrorPage.svelte +6 -3
  164. package/dist/components/scenes/error/ErrorPage.svelte.d.ts +2 -1
  165. package/dist/components/scenes/iam/AuthShell.svelte +6 -3
  166. package/dist/components/scenes/iam/AuthShell.svelte.d.ts +2 -1
  167. package/dist/components/scenes/iam/ChangePasswordForm.svelte +6 -3
  168. package/dist/components/scenes/iam/ChangePasswordForm.svelte.d.ts +3 -1
  169. package/dist/components/scenes/iam/ForgotPasswordForm.svelte +6 -3
  170. package/dist/components/scenes/iam/ForgotPasswordForm.svelte.d.ts +3 -1
  171. package/dist/components/scenes/iam/LoginForm.svelte +6 -3
  172. package/dist/components/scenes/iam/LoginForm.svelte.d.ts +3 -1
  173. package/dist/components/scenes/iam/PasswordInput.svelte +6 -4
  174. package/dist/components/scenes/iam/PasswordInput.svelte.d.ts +3 -2
  175. package/dist/components/scenes/iam/RegisterForm.svelte +6 -4
  176. package/dist/components/scenes/iam/RegisterForm.svelte.d.ts +3 -1
  177. package/dist/components/scenes/iam/ResetPasswordForm.svelte +6 -3
  178. package/dist/components/scenes/iam/ResetPasswordForm.svelte.d.ts +3 -1
  179. package/dist/components/scenes/iam/UserProfile.svelte +6 -3
  180. package/dist/components/scenes/iam/UserProfile.svelte.d.ts +3 -1
  181. package/dist/components/scenes/storage/AvatarUpload.svelte +11 -8
  182. package/dist/components/scenes/storage/AvatarUpload.svelte.d.ts +3 -1
  183. package/dist/components/scenes/storage/FileList.svelte +6 -3
  184. package/dist/components/scenes/storage/FileList.svelte.d.ts +3 -1
  185. package/dist/components/scenes/storage/FileUpload.svelte +6 -3
  186. package/dist/components/scenes/storage/FileUpload.svelte.d.ts +3 -1
  187. package/dist/components/scenes/storage/ImageUpload.svelte +12 -9
  188. package/dist/components/scenes/storage/ImageUpload.svelte.d.ts +3 -1
  189. package/dist/components/scenes/types.d.ts +20 -16
  190. package/dist/styles/global.css +5 -0
  191. package/dist/types.d.ts +48 -41
  192. package/dist/utils.d.ts +6 -1
  193. package/dist/utils.js +16 -1
  194. package/package.json +2 -2
@@ -8,7 +8,8 @@
8
8
  =============================================================================
9
9
  -->
10
10
  <script lang='ts'>
11
- import type { ToggleCheckboxProps } from '../../types.js'
11
+ import type { DataAttributes, ToggleCheckboxProps } from '../../types.js'
12
+ import { getDataAttributes } from '../../utils.js'
12
13
 
13
14
  let {
14
15
  checked = $bindable(false),
@@ -17,20 +18,22 @@
17
18
  disabled = false,
18
19
  class: className = '',
19
20
  onchange,
20
- }: ToggleCheckboxProps = $props()
21
+ ...restProps
22
+ }: ToggleCheckboxProps & DataAttributes = $props()
21
23
 
24
+ const dataAttributes = $derived(getDataAttributes(restProps))
22
25
  function handleChange(e: Event & { currentTarget: HTMLInputElement }) {
23
26
  checked = e.currentTarget.checked
24
27
  onchange?.(checked)
25
28
  }
26
29
  </script>
27
30
 
28
- <input
29
- {id}
30
- {name}
31
- type='checkbox'
32
- class={className}
33
- {disabled}
34
- checked={checked}
35
- onchange={handleChange}
31
+ <input {...dataAttributes}
32
+ {id}
33
+ {name}
34
+ type='checkbox'
35
+ class={className}
36
+ {disabled}
37
+ checked={checked}
38
+ onchange={handleChange}
36
39
  />
@@ -1,4 +1,5 @@
1
- import type { ToggleCheckboxProps } from '../../types.js';
2
- declare const ToggleCheckbox: import("svelte").Component<ToggleCheckboxProps, {}, "checked">;
1
+ import type { DataAttributes, ToggleCheckboxProps } from '../../types.js';
2
+ type $$ComponentProps = ToggleCheckboxProps & DataAttributes;
3
+ declare const ToggleCheckbox: import("svelte").Component<$$ComponentProps, {}, "checked">;
3
4
  type ToggleCheckbox = ReturnType<typeof ToggleCheckbox>;
4
5
  export default ToggleCheckbox;
@@ -8,7 +8,8 @@
8
8
  =============================================================================
9
9
  -->
10
10
  <script lang='ts'>
11
- import type { ToggleCheckboxProps } from '../../types.js'
11
+ import type { DataAttributes, ToggleCheckboxProps } from '../../types.js'
12
+ import { getDataAttributes } from '../../utils.js'
12
13
 
13
14
  let {
14
15
  checked = $bindable(false),
@@ -17,20 +18,22 @@
17
18
  disabled = false,
18
19
  class: className = '',
19
20
  onchange,
20
- }: ToggleCheckboxProps = $props()
21
+ ...restProps
22
+ }: ToggleCheckboxProps & DataAttributes = $props()
21
23
 
24
+ const dataAttributes = $derived(getDataAttributes(restProps))
22
25
  function handleChange(e: Event & { currentTarget: HTMLInputElement }) {
23
26
  checked = e.currentTarget.checked
24
27
  onchange?.(checked)
25
28
  }
26
29
  </script>
27
30
 
28
- <input
29
- {id}
30
- {name}
31
- type='checkbox'
32
- class={className}
33
- {disabled}
34
- checked={checked}
35
- onchange={handleChange}
31
+ <input {...dataAttributes}
32
+ {id}
33
+ {name}
34
+ type='checkbox'
35
+ class={className}
36
+ {disabled}
37
+ checked={checked}
38
+ onchange={handleChange}
36
39
  />
@@ -1,4 +1,5 @@
1
- import type { ToggleCheckboxProps } from '../../types.js';
2
- declare const ToggleInput: import("svelte").Component<ToggleCheckboxProps, {}, "checked">;
1
+ import type { DataAttributes, ToggleCheckboxProps } from '../../types.js';
2
+ type $$ComponentProps = ToggleCheckboxProps & DataAttributes;
3
+ declare const ToggleInput: import("svelte").Component<$$ComponentProps, {}, "checked">;
3
4
  type ToggleInput = ReturnType<typeof ToggleInput>;
4
5
  export default ToggleInput;
@@ -8,7 +8,8 @@
8
8
  =============================================================================
9
9
  -->
10
10
  <script lang='ts'>
11
- import type { ToggleRadioProps } from '../../types.js'
11
+ import type { DataAttributes, ToggleRadioProps } from '../../types.js'
12
+ import { getDataAttributes } from '../../utils.js'
12
13
 
13
14
  let {
14
15
  checked = $bindable(false),
@@ -17,20 +18,22 @@
17
18
  disabled = false,
18
19
  class: className = '',
19
20
  onchange,
20
- }: ToggleRadioProps = $props()
21
+ ...restProps
22
+ }: ToggleRadioProps & DataAttributes = $props()
21
23
 
24
+ const dataAttributes = $derived(getDataAttributes(restProps))
22
25
  function handleChange(e: Event & { currentTarget: HTMLInputElement }) {
23
26
  checked = e.currentTarget.checked
24
27
  onchange?.(checked)
25
28
  }
26
29
  </script>
27
30
 
28
- <input
29
- {id}
30
- {name}
31
- type='radio'
32
- class={className}
33
- {disabled}
34
- checked={checked}
35
- onchange={handleChange}
31
+ <input {...dataAttributes}
32
+ {id}
33
+ {name}
34
+ type='radio'
35
+ class={className}
36
+ {disabled}
37
+ checked={checked}
38
+ onchange={handleChange}
36
39
  />
@@ -1,4 +1,5 @@
1
- import type { ToggleRadioProps } from '../../types.js';
2
- declare const ToggleRadio: import("svelte").Component<ToggleRadioProps, {}, "checked">;
1
+ import type { DataAttributes, ToggleRadioProps } from '../../types.js';
2
+ type $$ComponentProps = ToggleRadioProps & DataAttributes;
3
+ declare const ToggleRadio: import("svelte").Component<$$ComponentProps, {}, "checked">;
3
4
  type ToggleRadio = ReturnType<typeof ToggleRadio>;
4
5
  export default ToggleRadio;
@@ -1,10 +1,11 @@
1
1
  <script lang='ts'>
2
+ import type { DataAttributes } from '../../../types.js'
2
3
  import type {
3
4
  MarkdownSourceKind,
4
5
  MarkdownToolbarDownloadAction,
5
6
  } from './document-types.js'
6
7
  import { uiM } from '../../../messages.js'
7
- import { cn } from '../../../utils.js'
8
+ import { cn, getDataAttributes } from '../../../utils.js'
8
9
  import {
9
10
  downloadAiDocument,
10
11
  resolveDocumentDownloadActions,
@@ -62,8 +63,10 @@
62
63
  triggerClass = '',
63
64
  menuClass = '',
64
65
  itemClass = '',
65
- }: AiDocumentDownloadMenuProps = $props()
66
+ ...restProps
67
+ }: AiDocumentDownloadMenuProps & DataAttributes = $props()
66
68
 
69
+ const dataAttributes = $derived(getDataAttributes(restProps))
67
70
  // menuWrapEl 只负责识别外部点击,以便菜单展开后能自然收起。
68
71
  let menuWrapEl: HTMLDivElement | undefined = $state()
69
72
  // open 由组件内部维护,左右两侧可以直接复用,不需要额外再托管弹层状态。
@@ -118,9 +121,9 @@
118
121
  }
119
122
  </script>
120
123
 
121
- <div
122
- bind:this={menuWrapEl}
123
- class={cn('hai-ai-download-menu', className)}
124
+ <div {...dataAttributes}
125
+ bind:this={menuWrapEl}
126
+ class={cn('hai-ai-download-menu', className)}
124
127
  >
125
128
  <button
126
129
  type='button'
@@ -1,3 +1,4 @@
1
+ import type { DataAttributes } from '../../../types.js';
1
2
  import type { MarkdownSourceKind, MarkdownToolbarDownloadAction } from './document-types.js';
2
3
  interface AiDocumentDownloadMenuProps {
3
4
  /** 触发下载时导出的原始内容。 */
@@ -33,6 +34,7 @@ interface AiDocumentDownloadMenuProps {
33
34
  /** 菜单项自定义类名。 */
34
35
  itemClass?: string;
35
36
  }
36
- declare const AiDocumentDownloadMenu: import("svelte").Component<AiDocumentDownloadMenuProps, {}, "">;
37
+ type $$ComponentProps = AiDocumentDownloadMenuProps & DataAttributes;
38
+ declare const AiDocumentDownloadMenu: import("svelte").Component<$$ComponentProps, {}, "">;
37
39
  type AiDocumentDownloadMenu = ReturnType<typeof AiDocumentDownloadMenu>;
38
40
  export default AiDocumentDownloadMenu;
@@ -11,6 +11,7 @@
11
11
  =============================================================================
12
12
  -->
13
13
  <script lang='ts'>
14
+ import type { DataAttributes } from '../../../types.js'
14
15
  import type {
15
16
  AiDocumentEditorProps,
16
17
  MarkdownBlockFormatKind,
@@ -27,7 +28,7 @@
27
28
  import { tick } from 'svelte'
28
29
  import { writeTextToClipboard } from '../../../internal/browser-safety.js'
29
30
  import { uiM } from '../../../messages.js'
30
- import { cn } from '../../../utils.js'
31
+ import { cn, getDataAttributes } from '../../../utils.js'
31
32
  import AiDocumentDownloadMenu from './AiDocumentDownloadMenu.svelte'
32
33
  import {
33
34
  createBuiltInCodePreview,
@@ -239,6 +240,8 @@
239
240
  sourceKind = 'markdown',
240
241
  // sourceKind=code 时的默认语言提示。
241
242
  codeLanguage,
243
+ // 正文字号,number 会按 px 处理。
244
+ fontSize,
242
245
  // 外层自定义类名。
243
246
  class: className = '',
244
247
  // 是否显示代码块复制按钮。
@@ -249,6 +252,8 @@
249
252
  showCodePreviewToggle = false,
250
253
  // 是否显式允许内置 HTML / JS / CSS 预览执行高风险内容。
251
254
  allowUnsafeCodePreview = false,
255
+ // 是否按安全白名单解析原始 HTML 标签。
256
+ allowHtmlTags = false,
252
257
  // 代码预览切换模式下展示在语言标签旁的提示文案。
253
258
  codePreviewHint = '',
254
259
  // 是否启用语法高亮。
@@ -319,8 +324,10 @@
319
324
  oncopyselection,
320
325
  // 选区注释回调。
321
326
  onannotation,
322
- }: AiDocumentEditorProps = $props()
327
+ ...restProps
328
+ }: AiDocumentEditorProps & DataAttributes = $props()
323
329
 
330
+ const dataAttributes = $derived(getDataAttributes(restProps))
324
331
  // outlineCollapsedInitialized 用来把 `initialOutlineCollapsed` 只消费一次,避免用户手动展开后又被 props 回写覆盖。
325
332
  let outlineCollapsedInitialized = $state(false)
326
333
  // 当前目录是否折叠。
@@ -409,6 +416,7 @@
409
416
  codeViewPreviewLabel: uiM('file_list_preview'),
410
417
  codePreviewHint: normalizedCodePreviewHint,
411
418
  breaks,
419
+ allowHtmlTags,
412
420
  }),
413
421
  )
414
422
  // html 是最终注入正文的内容。
@@ -463,6 +471,25 @@
463
471
  editable ? 'hai-markdown-editable' : '',
464
472
  ),
465
473
  )
474
+ const normalizedFontSize = $derived(resolveFontSize(fontSize))
475
+ const documentStyle = $derived(
476
+ normalizedFontSize
477
+ ? `--hai-markdown-font-size:${normalizedFontSize};`
478
+ : undefined,
479
+ )
480
+
481
+ function resolveFontSize(value: AiDocumentEditorProps['fontSize']): string | undefined {
482
+ if (typeof value === 'number' && Number.isFinite(value)) {
483
+ return `${value}px`
484
+ }
485
+
486
+ if (typeof value === 'string') {
487
+ const normalized = value.trim()
488
+ return normalized || undefined
489
+ }
490
+
491
+ return undefined
492
+ }
466
493
 
467
494
  $effect(() => {
468
495
  void html
@@ -933,6 +960,7 @@
933
960
  enableHighlight,
934
961
  showCopyButton: false,
935
962
  breaks,
963
+ allowHtmlTags,
936
964
  })
937
965
 
938
966
  host.innerHTML = `<div class="hai-md-preview-card"><div class="hai-md-preview-head">${previewTitle}</div>${previewDesc}<div class="hai-md-preview-rendered">${markdownHtml}</div></div>`
@@ -2340,7 +2368,7 @@
2340
2368
  }
2341
2369
  </script>
2342
2370
 
2343
- <section class={cn('hai-ai-doc-pane', className)}>
2371
+ <section {...dataAttributes} class={cn('hai-ai-doc-pane', className)}>
2344
2372
  <div class='hai-ai-doc-shell'>
2345
2373
  {#if showToolbar}
2346
2374
  <header class='hai-ai-doc-topbar'>
@@ -3135,6 +3163,7 @@
3135
3163
  <article
3136
3164
  bind:this={previewHost}
3137
3165
  class={readerDocumentClass}
3166
+ style={documentStyle}
3138
3167
  contenteditable={editable}
3139
3168
  role='document'
3140
3169
  onmousemove={handlePreviewMouseMove}
@@ -3990,6 +4019,7 @@
3990
4019
  }
3991
4020
 
3992
4021
  .hai-markdown-document {
4022
+ font-size: var(--hai-markdown-font-size, 1rem);
3993
4023
  width: 100%;
3994
4024
  min-width: 0;
3995
4025
  max-width: 52.5rem;
@@ -4042,29 +4072,29 @@
4042
4072
  }
4043
4073
 
4044
4074
  .hai-markdown :global(h1) {
4045
- font-size: 2.2rem;
4075
+ font-size: 2.2em;
4046
4076
  padding-bottom: 0.45em;
4047
4077
  border-bottom: 1px solid oklch(var(--bc) / 0.12);
4048
4078
  }
4049
4079
 
4050
4080
  .hai-markdown :global(h2) {
4051
- font-size: 1.48rem;
4081
+ font-size: 1.48em;
4052
4082
  }
4053
4083
 
4054
4084
  .hai-markdown :global(h3) {
4055
- font-size: 1.18rem;
4085
+ font-size: 1.18em;
4056
4086
  }
4057
4087
 
4058
4088
  .hai-markdown :global(h4) {
4059
- font-size: 1.02rem;
4089
+ font-size: 1.02em;
4060
4090
  }
4061
4091
 
4062
4092
  .hai-markdown :global(h5) {
4063
- font-size: 0.95rem;
4093
+ font-size: 0.95em;
4064
4094
  }
4065
4095
 
4066
4096
  .hai-markdown :global(h6) {
4067
- font-size: 0.88rem;
4097
+ font-size: 0.88em;
4068
4098
  color: oklch(var(--bc) / 0.7);
4069
4099
  }
4070
4100
 
@@ -4261,7 +4291,7 @@
4261
4291
  font-family:
4262
4292
  ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas,
4263
4293
  'Liberation Mono', monospace;
4264
- font-size: 0.875rem;
4294
+ font-size: 0.875em;
4265
4295
  line-height: 1.45;
4266
4296
  tab-size: 2;
4267
4297
 
@@ -1,4 +1,6 @@
1
+ import type { DataAttributes } from '../../../types.js';
1
2
  import type { AiDocumentEditorProps } from './document-types.js';
2
- declare const AiDocumentEditor: import("svelte").Component<AiDocumentEditorProps, {}, "editorScrollHost" | "previewHost">;
3
+ type $$ComponentProps = AiDocumentEditorProps & DataAttributes;
4
+ declare const AiDocumentEditor: import("svelte").Component<$$ComponentProps, {}, "editorScrollHost" | "previewHost">;
3
5
  type AiDocumentEditor = ReturnType<typeof AiDocumentEditor>;
4
6
  export default AiDocumentEditor;
@@ -16,24 +16,53 @@
16
16
  =============================================================================
17
17
  -->
18
18
  <script lang='ts'>
19
+ import type { DataAttributes } from '../../../types.js'
19
20
  import type { MarkdownRendererProps } from '../types.js'
20
21
  import { writeTextToClipboard } from '../../../internal/browser-safety.js'
21
- import { cn } from '../../../utils.js'
22
+ import { cn, getDataAttributes } from '../../../utils.js'
22
23
  import { parseMarkdown } from './markdown-parse.js'
23
24
 
24
25
  const {
25
26
  content = '',
27
+ fontSize,
26
28
  class: className = '',
27
29
  showCopyButton = true,
28
30
  enableHighlight = true,
29
31
  breaks = true,
30
- }: MarkdownRendererProps = $props()
31
-
32
+ allowHtmlTags = false,
33
+ ...restProps
34
+ }: MarkdownRendererProps & DataAttributes = $props()
35
+
36
+ const dataAttributes = $derived(getDataAttributes(restProps))
37
+ const normalizedFontSize = $derived(resolveFontSize(fontSize))
38
+ const markdownStyle = $derived(
39
+ normalizedFontSize
40
+ ? `--hai-markdown-font-size:${normalizedFontSize};`
41
+ : undefined,
42
+ )
32
43
  /** 解析后的 HTML */
33
44
  const html = $derived(
34
- parseMarkdown(content, { enableHighlight, showCopyButton, breaks }),
45
+ parseMarkdown(content, {
46
+ enableHighlight,
47
+ showCopyButton,
48
+ breaks,
49
+ allowHtmlTags,
50
+ }),
35
51
  )
36
52
 
53
+ function resolveFontSize(value: MarkdownRendererProps['fontSize']): string | undefined {
54
+ if (typeof value === 'number' && Number.isFinite(value)) {
55
+ return `${value}px`
56
+ }
57
+
58
+ if (typeof value === 'string') {
59
+ const normalized = value.trim()
60
+ return normalized || undefined
61
+ }
62
+
63
+ return undefined
64
+ }
65
+
37
66
  /**
38
67
  * 事件代理:处理代码块复制按钮点击
39
68
  */
@@ -62,10 +91,10 @@
62
91
  }
63
92
  </script>
64
93
 
65
- <!-- svelte-ignore a11y_click_events_have_key_events -->
66
- <!-- svelte-ignore a11y_no_static_element_interactions -->
67
94
  <div
95
+ {...dataAttributes}
68
96
  class={cn('hai-markdown', className)}
97
+ style={markdownStyle}
69
98
  onclick={handleClick}
70
99
  >
71
100
  <!-- eslint-disable-next-line svelte/no-at-html-tags -- Markdown HTML 渲染 -->
@@ -80,6 +109,7 @@
80
109
  * ========================================================================= */
81
110
 
82
111
  .hai-markdown {
112
+ font-size: var(--hai-markdown-font-size, 1rem);
83
113
  line-height: 1.75;
84
114
  color: inherit;
85
115
  word-wrap: break-word;
@@ -170,6 +200,18 @@
170
200
  color: oklch(var(--bc) / 0.55);
171
201
  }
172
202
 
203
+ .hai-markdown :global(u) {
204
+ text-decoration-thickness: 0.08em;
205
+ text-underline-offset: 0.14em;
206
+ }
207
+
208
+ .hai-markdown :global(mark) {
209
+ padding: 0 0.12em;
210
+ border-radius: 0.25rem;
211
+ background: oklch(var(--wa, 0.9 0.14 90) / 0.34);
212
+ color: inherit;
213
+ }
214
+
173
215
  /* ─── 行内代码 ─── */
174
216
 
175
217
  .hai-markdown :global(:not(pre) > code) {
@@ -244,7 +286,7 @@
244
286
 
245
287
  .hai-markdown :global(.hai-md-code-block code) {
246
288
  font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace;
247
- font-size: 0.875rem;
289
+ font-size: 0.875em;
248
290
  line-height: 1.6;
249
291
  tab-size: 2;
250
292
  }
@@ -1,4 +1,6 @@
1
+ import type { DataAttributes } from '../../../types.js';
1
2
  import type { MarkdownRendererProps } from '../types.js';
2
- declare const MarkdownRenderer: import("svelte").Component<MarkdownRendererProps, {}, "">;
3
+ type $$ComponentProps = MarkdownRendererProps & DataAttributes;
4
+ declare const MarkdownRenderer: import("svelte").Component<$$ComponentProps, {}, "">;
3
5
  type MarkdownRenderer = ReturnType<typeof MarkdownRenderer>;
4
6
  export default MarkdownRenderer;
@@ -21,6 +21,8 @@ export interface MarkdownDocumentParseOptions {
21
21
  codePreviewHint?: string;
22
22
  /** Whether soft line breaks are converted to <br>. */
23
23
  breaks?: boolean;
24
+ /** Whether raw HTML tags are parsed through a safe allowlist. */
25
+ allowHtmlTags?: boolean;
24
26
  }
25
27
  export interface MarkdownRenderResult {
26
28
  /** Rendered HTML output. */
@@ -7,6 +7,7 @@ import { Marked } from 'marked';
7
7
  import { createEditorMarkdownExtensions, extractPlainTextFromTokens, } from './editor-markdown-extensions.js';
8
8
  import { highlightCode, isLanguageSupported } from './highlight.js';
9
9
  import { isMermaidLanguage } from './mermaid-render.js';
10
+ import { escapeHtml, isExternalLinkHref, sanitizeImageSrc, sanitizeLinkHref, sanitizeMarkdownHtml, } from './safe-html.js';
10
11
  /** Default parsing options for document mode. */
11
12
  const DEFAULT_OPTIONS = {
12
13
  enableHighlight: true,
@@ -17,19 +18,15 @@ const DEFAULT_OPTIONS = {
17
18
  codeViewPreviewLabel: 'Preview',
18
19
  codePreviewHint: '',
19
20
  breaks: true,
21
+ allowHtmlTags: false,
20
22
  };
21
- const SAFE_LINK_HREF_REGEX = /^(?:https?:\/\/|\/|#|mailto:)/i;
22
- const SAFE_IMAGE_SRC_REGEX = /^(?:https?:\/\/|\/|data:image\/)/i;
23
- /**
24
- * Escape HTML entities to prevent raw HTML injection.
25
- */
26
- function escapeHtml(text) {
27
- return text
28
- .replace(/&/g, '&amp;')
29
- .replace(/</g, '&lt;')
30
- .replace(/>/g, '&gt;')
31
- .replace(/"/g, '&quot;')
32
- .replace(/'/g, '&#39;');
23
+ function readHtmlTokenSource(token) {
24
+ if ('raw' in token && typeof token.raw === 'string') {
25
+ return token.raw;
26
+ }
27
+ return 'text' in token && typeof token.text === 'string'
28
+ ? token.text
29
+ : '';
33
30
  }
34
31
  /**
35
32
  * Create a URL-safe heading slug while keeping letters and digits.
@@ -166,16 +163,18 @@ function createRendererObject(options, state) {
166
163
  + `</div>`;
167
164
  },
168
165
  html(token) {
169
- return escapeHtml('text' in token ? token.text : '');
166
+ const source = readHtmlTokenSource(token);
167
+ return options.allowHtmlTags
168
+ ? sanitizeMarkdownHtml(source)
169
+ : escapeHtml(source);
170
170
  },
171
171
  link({ href, title, tokens }) {
172
172
  // text preserves inline markdown within the link label.
173
173
  const text = this.parser.parseInline(tokens);
174
174
  // safeHref strips potentially unsafe protocols.
175
- const safeHref = href && SAFE_LINK_HREF_REGEX.test(href) ? href : '';
175
+ const safeHref = sanitizeLinkHref(href);
176
176
  // isExternal controls whether target/_blank is added.
177
- const isExternal = safeHref
178
- && (safeHref.startsWith('http://') || safeHref.startsWith('https://'));
177
+ const isExternal = safeHref && isExternalLinkHref(safeHref);
179
178
  // attrs is the flattened HTML attribute string.
180
179
  const attrs = [
181
180
  `href="${escapeHtml(safeHref)}"`,
@@ -186,7 +185,7 @@ function createRendererObject(options, state) {
186
185
  },
187
186
  image({ href, title, text }) {
188
187
  // safeSrc prevents dangerous image protocols.
189
- const safeSrc = href && SAFE_IMAGE_SRC_REGEX.test(href) ? href : '';
188
+ const safeSrc = sanitizeImageSrc(href);
190
189
  // attrs is the flattened HTML attribute string.
191
190
  const attrs = [
192
191
  `src="${escapeHtml(safeSrc)}"`,
@@ -1,3 +1,4 @@
1
+ import type { DataAttributes } from '../../../types.js';
1
2
  /**
2
3
  * Markdown source type, used to distinguish plain markdown, document panel, or code-only rendering.
3
4
  */
@@ -126,7 +127,7 @@ export interface MarkdownToolbarDownloadAction {
126
127
  type BivariantCallback<Args extends unknown[], Return = void> = {
127
128
  bivarianceHack: (...args: Args) => Return;
128
129
  }['bivarianceHack'];
129
- export interface AiDocumentEditorProps {
130
+ export interface AiDocumentEditorProps extends DataAttributes {
130
131
  /** Markdown source content. */
131
132
  content?: string;
132
133
  /** Document title displayed in the header. */
@@ -135,6 +136,8 @@ export interface AiDocumentEditorProps {
135
136
  sourceKind?: MarkdownSourceKind;
136
137
  /** Default language when sourceKind is `code`. */
137
138
  codeLanguage?: string;
139
+ /** Content font size. Numbers are interpreted as px. */
140
+ fontSize?: string | number;
138
141
  /** Custom class for the outer container. */
139
142
  class?: string;
140
143
  /** Whether to show the copy button on code blocks. */
@@ -143,6 +146,8 @@ export interface AiDocumentEditorProps {
143
146
  showRunButton?: boolean;
144
147
  /** Whether to show the code/preview toggle on code blocks. */
145
148
  showCodePreviewToggle?: boolean;
149
+ /** Whether to parse raw HTML tags through a safe allowlist. */
150
+ allowHtmlTags?: boolean;
146
151
  /**
147
152
  * 是否启用内置的高风险代码预览。
148
153
  *
@@ -15,6 +15,8 @@ export interface MarkdownParseOptions {
15
15
  showCopyButton?: boolean;
16
16
  /** 是否将换行符转换为 <br>(默认 true,适合 AI 输出) */
17
17
  breaks?: boolean;
18
+ /** 是否按安全白名单解析原始 HTML 标签(默认 false) */
19
+ allowHtmlTags?: boolean;
18
20
  }
19
21
  /**
20
22
  * 将 Markdown 文本解析为 HTML