@h-ai/ui 0.1.0-alpha.56 → 0.1.0-alpha.58
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.
- package/README.en.md +742 -0
- package/README.ja.md +742 -0
- package/README.md +21 -4
- package/auto-import.js +9 -66
- package/dist/components/compounds/Modal.svelte +53 -6
- package/dist/components/primitives/BareInput.svelte +4 -2
- package/dist/components/scenes/app/LanguageSwitch.svelte +1 -1
- package/dist/components/scenes/app/LanguageSwitch.svelte.d.ts +1 -1
- package/dist/components/scenes/iam/UserProfile.svelte +9 -5
- package/dist/components/scenes/types.d.ts +4 -0
- package/dist/messages/en-US.json +2 -0
- package/dist/messages/zh-CN.json +2 -0
- package/dist/theme-config.d.ts +4 -2
- package/dist/theme-config.js +5 -3
- package/dist/types.d.ts +1 -1
- package/package.json +25 -21
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# @h-ai/ui
|
|
2
2
|
|
|
3
|
+
[简体中文](./README.md) | [English](./README.en.md) | [日本語](./README.ja.md)
|
|
4
|
+
|
|
5
|
+
[框架首页](../../README.md) · 相关模块:[@h-ai/core](../core/README.md)
|
|
6
|
+
|
|
3
7
|
> 基于 Svelte 5 Runes 的多端 UI 组件库,采用 DaisyUI v5 + Tailwind CSS v4 样式 + Bits UI v2 headless 交互,内置 i18n(zh-CN / en-US),内置 15 个精选 DaisyUI 主题。
|
|
4
8
|
|
|
5
9
|
## 安装
|
|
@@ -94,6 +98,16 @@ export default defineConfig({
|
|
|
94
98
|
|
|
95
99
|
> 除 `toast`、类型导入与 `Range`(会和 DOM `Range` 构造器冲突)外,其余公开 Svelte 组件都可通过 `@h-ai/ui/auto-import` 自动注入。
|
|
96
100
|
|
|
101
|
+
管理后台等只使用部分组件的页面,可直接使用公开组件目录入口;主题工具通过轻量 `@h-ai/ui/theme` 导入,避免主题消费加载根入口中的 AI、图表等组件。组件路径以已安装版本的 exports 为准,不导入包内 `src/` 或 `dist/`。
|
|
102
|
+
|
|
103
|
+
自动导入器识别无分号、跨行及带别名的显式导入,已绑定的组件名称不会重复注入;仅类型导入不会替代运行时组件。
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
import { Card } from '@h-ai/ui/components/compounds/index'
|
|
107
|
+
import { Button } from '@h-ai/ui/components/primitives/index'
|
|
108
|
+
import { getThemeInfo, THEMES } from '@h-ai/ui/theme'
|
|
109
|
+
```
|
|
110
|
+
|
|
97
111
|
### data-* 属性透传
|
|
98
112
|
|
|
99
113
|
公开 Svelte 组件支持把调用方传入的 `data-*` 属性透传到组件根节点或主交互节点,便于测试选择器、埋点和自动化标记。普通未知属性不会被透传。
|
|
@@ -150,6 +164,8 @@ components/
|
|
|
150
164
|
| `Spinner` | 加载动画 | `size`, `variant` |
|
|
151
165
|
| `Progress` | 进度条 | `value`, `max`, `variant`, `striped`, `animated` |
|
|
152
166
|
|
|
167
|
+
`BareInput` 的 `file` 类型通过 `onchange` 读取 `event.currentTarget.files`,不绑定文件路径 `value`;其他输入类型可使用 `bind:value`。
|
|
168
|
+
|
|
153
169
|
### 组合组件 Compounds(36 个)
|
|
154
170
|
|
|
155
171
|
#### 表单
|
|
@@ -171,7 +187,7 @@ components/
|
|
|
171
187
|
|
|
172
188
|
| 组件 | 描述 | 主要属性 |
|
|
173
189
|
| --------- | ------ | ------------------------------------------------------- |
|
|
174
|
-
| `Modal` |
|
|
190
|
+
| `Modal` | 模态框(语义标签、焦点约束与恢复) | `open`, `title`, `size`, `radius`, `bodyOverflow`, `showClose` |
|
|
175
191
|
| `Drawer` | 抽屉 | `open`, `position`, `title`, `size`, `width`, `resizable`, `widthStorageKey` |
|
|
176
192
|
| `Confirm` | 确认框 | `open`, `title`, `message`, `variant`, `onconfirm` |
|
|
177
193
|
| `Popover` | 弹出层 | `open`, `position`, `trigger`, `offset`, `portal` |
|
|
@@ -258,7 +274,7 @@ components/
|
|
|
258
274
|
| `ChangePasswordForm` | 修改密码 | `loading`, `errors`, `requireOldPassword`, `onsubmit` |
|
|
259
275
|
| `PasswordInput` | 密码输入框 | `value`, `showToggle`, `showStrength`, `minLength` |
|
|
260
276
|
| `PermGuard` | 权限守卫 | `permissions`, `mode`, `fallback` |
|
|
261
|
-
| `UserProfile` | 用户资料 | `user`, `editable`, `fields`, `onsubmit`
|
|
277
|
+
| `UserProfile` | 用户资料 | `user`, `editable`, `fields`, `avatarAccept`, `avatarHint`, `onsubmit` |
|
|
262
278
|
|
|
263
279
|
#### Storage 存储(4 个)
|
|
264
280
|
|
|
@@ -623,13 +639,14 @@ import {
|
|
|
623
639
|
applyTheme, // 应用主题(自动持久化到 localStorage)
|
|
624
640
|
createThemeBootstrapScript, // 生成可注入 app.html 的首屏主题恢复脚本
|
|
625
641
|
getCurrentTheme, // 获取当前主题
|
|
642
|
+
getSavedTheme, // 读取安全缓存,可传入应用配置的默认主题
|
|
626
643
|
getThemeInitScript, // 返回可注入到 HTML shell 的防闪烁脚本文本
|
|
627
644
|
isDarkTheme, // 检查是否暗色主题
|
|
628
645
|
normalizeHexColor, // 归一化 #RGB / #RRGGBB 主题色
|
|
629
646
|
normalizeThemeId, // 校验主题 ID 并自动回退
|
|
630
647
|
THEME_GROUPS, // 按亮色/暗色分组
|
|
631
648
|
THEMES, // ThemeInfo[] — 15 个精选主题元数据
|
|
632
|
-
} from '@h-ai/ui'
|
|
649
|
+
} from '@h-ai/ui/theme'
|
|
633
650
|
```
|
|
634
651
|
|
|
635
652
|
简单场景可以直接使用 `getThemeInitScript()`;如果应用有自定义存储 key、主题色 CSS 变量或语言偏好,推荐在服务端通过 `createThemeBootstrapScript()` 生成脚本后再注入 `app.html`。
|
|
@@ -645,7 +662,7 @@ import {
|
|
|
645
662
|
然后在 `hooks.server.ts` 里注入:
|
|
646
663
|
|
|
647
664
|
```ts
|
|
648
|
-
import { createThemeBootstrapScript, DEFAULT_THEME_COLOR_CSS_VAR } from '@h-ai/ui'
|
|
665
|
+
import { createThemeBootstrapScript, DEFAULT_THEME_COLOR_CSS_VAR } from '@h-ai/ui/theme'
|
|
649
666
|
|
|
650
667
|
const themeBootstrapScript = createThemeBootstrapScript({
|
|
651
668
|
storageKey: 'hai-demo-preferences',
|
package/auto-import.js
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
* =============================================================================
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import { parse } from 'svelte/compiler'
|
|
11
|
+
|
|
10
12
|
const UI_COMPONENTS = new Set([
|
|
11
13
|
// primitives
|
|
12
14
|
'Avatar',
|
|
@@ -112,10 +114,8 @@ const UI_COMPONENTS = new Set([
|
|
|
112
114
|
const TAG_REGEX = /<([A-Z][A-Za-z0-9]*)\b/g
|
|
113
115
|
const SCRIPT_REGEX = /<script\b([^>]*)>([\s\S]*?)<\/script>/g
|
|
114
116
|
const UI_IMPORT_REGEX = /import\s*\{([^}]+)\}\s*from\s*['"]@h-ai\/ui['"];?/
|
|
115
|
-
const IMPORT_ALIAS_SPLIT_REGEX = /\s+as\s+/i
|
|
116
117
|
const WINDOWS_PATH_SEPARATOR_REGEX = /\\/g
|
|
117
118
|
const MODULE_CONTEXT_ATTR_REGEX = /context\s*=\s*['"]module['"]/i
|
|
118
|
-
const TYPE_IMPORT_PREFIX_REGEX = /^type\s+/
|
|
119
119
|
|
|
120
120
|
function collectUsedComponents(content) {
|
|
121
121
|
const used = new Set()
|
|
@@ -130,73 +130,16 @@ function collectUsedComponents(content) {
|
|
|
130
130
|
|
|
131
131
|
function collectImportedNames(scriptContent) {
|
|
132
132
|
const imported = new Set()
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const fromIndex = statement.lastIndexOf(' from ')
|
|
138
|
-
if (fromIndex === -1)
|
|
139
|
-
return
|
|
140
|
-
|
|
141
|
-
let clause = statement.slice('import '.length, fromIndex).trim()
|
|
142
|
-
clause = clause.replace(TYPE_IMPORT_PREFIX_REGEX, '')
|
|
143
|
-
if (!clause)
|
|
144
|
-
return
|
|
145
|
-
|
|
146
|
-
// default + named: Button, { Input as UiInput }
|
|
147
|
-
if (clause.includes('{')) {
|
|
148
|
-
const [defaultPart, namedPart] = clause.split('{')
|
|
149
|
-
const defaultName = defaultPart.replace(',', '').trim()
|
|
150
|
-
if (defaultName)
|
|
151
|
-
imported.add(defaultName)
|
|
152
|
-
|
|
153
|
-
const raw = namedPart.replace('}', '')
|
|
154
|
-
for (const segment of raw.split(',')) {
|
|
155
|
-
const cleaned = segment.trim().replace(TYPE_IMPORT_PREFIX_REGEX, '')
|
|
156
|
-
if (!cleaned)
|
|
157
|
-
continue
|
|
158
|
-
const [original] = cleaned.split(IMPORT_ALIAS_SPLIT_REGEX)
|
|
159
|
-
if (original)
|
|
160
|
-
imported.add(original.trim())
|
|
161
|
-
}
|
|
162
|
-
return
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// namespace import: * as name
|
|
166
|
-
if (clause.startsWith('* as ')) {
|
|
167
|
-
const name = clause.replace('* as ', '').trim()
|
|
168
|
-
if (name)
|
|
169
|
-
imported.add(name)
|
|
170
|
-
return
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// default import
|
|
174
|
-
imported.add(clause)
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
for (const line of lines) {
|
|
178
|
-
const trimmed = line.trim()
|
|
179
|
-
if (!buffer) {
|
|
180
|
-
if (!trimmed.startsWith('import '))
|
|
181
|
-
continue
|
|
182
|
-
buffer = trimmed
|
|
183
|
-
if (trimmed.endsWith(';')) {
|
|
184
|
-
flushImport(buffer)
|
|
185
|
-
buffer = ''
|
|
186
|
-
}
|
|
133
|
+
// 复用 Svelte 的 TS 解析器识别绑定,兼容无分号、跨行、注释及别名。
|
|
134
|
+
const ast = parse(`<script lang="ts">${scriptContent}</script>`, { modern: true })
|
|
135
|
+
for (const statement of ast.instance?.content.body ?? []) {
|
|
136
|
+
if (statement.type !== 'ImportDeclaration' || statement.importKind === 'type')
|
|
187
137
|
continue
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
if (trimmed.endsWith(';')) {
|
|
192
|
-
flushImport(buffer)
|
|
193
|
-
buffer = ''
|
|
138
|
+
for (const specifier of statement.specifiers) {
|
|
139
|
+
if (specifier.importKind !== 'type')
|
|
140
|
+
imported.add(specifier.local.name)
|
|
194
141
|
}
|
|
195
142
|
}
|
|
196
|
-
|
|
197
|
-
if (buffer)
|
|
198
|
-
flushImport(buffer)
|
|
199
|
-
|
|
200
143
|
return imported
|
|
201
144
|
}
|
|
202
145
|
|
|
@@ -28,6 +28,9 @@
|
|
|
28
28
|
}: ModalProps & DataAttributes = $props()
|
|
29
29
|
|
|
30
30
|
const dataAttributes = $derived(getDataAttributes(restProps))
|
|
31
|
+
const modalId = $props.id()
|
|
32
|
+
const titleId = `${modalId}-title`
|
|
33
|
+
let panel = $state<HTMLDivElement>()
|
|
31
34
|
|
|
32
35
|
// 标题区仅在显式提供标题或自定义 header 时出现;
|
|
33
36
|
// 仅需关闭按钮的场景改为右上角悬浮按钮,避免业务侧额外占一整行头部。
|
|
@@ -91,16 +94,60 @@
|
|
|
91
94
|
onclose?.()
|
|
92
95
|
}
|
|
93
96
|
|
|
97
|
+
function focusableElements(): HTMLElement[] {
|
|
98
|
+
return [...(panel?.querySelectorAll<HTMLElement>('button, a[href], input:not([type="hidden"]), select, textarea, [tabindex]') ?? [])]
|
|
99
|
+
.filter(element => element.tabIndex >= 0 && !element.hasAttribute('disabled') && element.getClientRects().length > 0)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 打开时把焦点移入弹窗,关闭后恢复原操作位置。
|
|
103
|
+
$effect(() => {
|
|
104
|
+
if (!open || !panel)
|
|
105
|
+
return
|
|
106
|
+
const previous = document.activeElement instanceof HTMLElement ? document.activeElement : null
|
|
107
|
+
const elements = focusableElements()
|
|
108
|
+
const input = elements.find(element => element.matches('input:not([readonly]), textarea, select'))
|
|
109
|
+
const target = input ?? elements[0] ?? panel
|
|
110
|
+
target.focus()
|
|
111
|
+
return () => {
|
|
112
|
+
if (previous?.isConnected)
|
|
113
|
+
previous.focus()
|
|
114
|
+
}
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
function handleKeydown(event: KeyboardEvent) {
|
|
118
|
+
if (!open)
|
|
119
|
+
return
|
|
120
|
+
if (event.key === 'Escape' && closeOnEscape) {
|
|
121
|
+
event.preventDefault()
|
|
122
|
+
event.stopPropagation()
|
|
123
|
+
handleClose()
|
|
124
|
+
}
|
|
125
|
+
else if (event.key === 'Tab') {
|
|
126
|
+
const elements = focusableElements()
|
|
127
|
+
const first = elements[0]
|
|
128
|
+
const last = elements.at(-1)
|
|
129
|
+
if (!first) {
|
|
130
|
+
event.preventDefault()
|
|
131
|
+
panel?.focus()
|
|
132
|
+
}
|
|
133
|
+
else if (event.shiftKey && (document.activeElement === first || document.activeElement === panel)) {
|
|
134
|
+
event.preventDefault()
|
|
135
|
+
last?.focus()
|
|
136
|
+
}
|
|
137
|
+
else if (!event.shiftKey && document.activeElement === last) {
|
|
138
|
+
event.preventDefault()
|
|
139
|
+
first.focus()
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
94
144
|
</script>
|
|
95
145
|
|
|
96
146
|
<div {...dataAttributes}
|
|
97
147
|
class='hai-modal'
|
|
98
148
|
style:display={open ? '' : 'none'}
|
|
99
149
|
aria-hidden={open ? undefined : 'true'}
|
|
100
|
-
onkeydown={
|
|
101
|
-
if (closeOnEscape && e.key === 'Escape')
|
|
102
|
-
handleClose()
|
|
103
|
-
}}
|
|
150
|
+
onkeydown={handleKeydown}
|
|
104
151
|
>
|
|
105
152
|
<div
|
|
106
153
|
class='hai-modal__viewport'
|
|
@@ -117,7 +164,7 @@
|
|
|
117
164
|
<div class='hai-modal__backdrop' aria-hidden='true'></div>
|
|
118
165
|
{/if}
|
|
119
166
|
|
|
120
|
-
<div class={modalBoxClass} style={panelStyle}>
|
|
167
|
+
<div bind:this={panel} class={modalBoxClass} style={panelStyle} role='dialog' aria-modal='true' aria-labelledby={title && !header ? titleId : undefined} aria-label={title && !header ? undefined : title || uiM('common_dialog')} tabindex='-1'>
|
|
121
168
|
{#if hasFloatingClose}
|
|
122
169
|
<button
|
|
123
170
|
type='button'
|
|
@@ -145,7 +192,7 @@
|
|
|
145
192
|
{@render header()}
|
|
146
193
|
</div>
|
|
147
194
|
{:else if title}
|
|
148
|
-
<h3 class='flex-1 font-semibold text-[1.05rem] tracking-tight text-base-content/92'>
|
|
195
|
+
<h3 id={titleId} class='flex-1 font-semibold text-[1.05rem] tracking-tight text-base-content/92'>
|
|
149
196
|
{title}
|
|
150
197
|
</h3>
|
|
151
198
|
{:else}
|
|
@@ -48,7 +48,9 @@
|
|
|
48
48
|
|
|
49
49
|
const dataAttributes = $derived(getDataAttributes(restProps))
|
|
50
50
|
function handleInput(e: Event & { currentTarget: HTMLInputElement }) {
|
|
51
|
-
value
|
|
51
|
+
// 文件输入的 value 由浏览器管理,不能把 fakepath 写回原生控件。
|
|
52
|
+
if (type !== 'file')
|
|
53
|
+
value = e.currentTarget.value
|
|
52
54
|
oninput?.(e)
|
|
53
55
|
}
|
|
54
56
|
|
|
@@ -94,7 +96,7 @@
|
|
|
94
96
|
aria-controls={ariaControls}
|
|
95
97
|
aria-autocomplete={ariaAutocomplete}
|
|
96
98
|
class={className}
|
|
97
|
-
|
|
99
|
+
value={type === 'file' ? undefined : value}
|
|
98
100
|
oninput={handleInput}
|
|
99
101
|
onchange={handleChange}
|
|
100
102
|
onkeydown={handleKeydown}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
```svelte
|
|
11
11
|
<script>
|
|
12
12
|
import { LanguageSwitch, createLocaleStore, setGlobalLocale } from '@h-ai/ui'
|
|
13
|
-
import { setLocale, getLocale } from '
|
|
13
|
+
import { setLocale, getLocale } from '$lib/paraglide/runtime.js'
|
|
14
14
|
|
|
15
15
|
const localeStore = createLocaleStore()
|
|
16
16
|
</script>
|
|
@@ -31,7 +31,7 @@ type $$ComponentProps = Props & DataAttributes;
|
|
|
31
31
|
* ```svelte
|
|
32
32
|
* <script>
|
|
33
33
|
* import { LanguageSwitch, createLocaleStore, setGlobalLocale } from '@h-ai/ui'
|
|
34
|
-
* import { setLocale, getLocale } from '
|
|
34
|
+
* import { setLocale, getLocale } from '$lib/paraglide/runtime.js'
|
|
35
35
|
*
|
|
36
36
|
* const localeStore = createLocaleStore()
|
|
37
37
|
* </script>
|
|
@@ -26,6 +26,8 @@
|
|
|
26
26
|
alwaysEditable = false,
|
|
27
27
|
loading = false,
|
|
28
28
|
fields = ['avatar', 'username', 'email', 'displayName', 'phone'],
|
|
29
|
+
avatarAccept = 'image/*',
|
|
30
|
+
avatarHint,
|
|
29
31
|
class: className = '',
|
|
30
32
|
errors = {},
|
|
31
33
|
onsubmit,
|
|
@@ -34,6 +36,7 @@
|
|
|
34
36
|
}: UserProfileProps & DataAttributes = $props()
|
|
35
37
|
|
|
36
38
|
const dataAttributes = $derived(getDataAttributes(restProps))
|
|
39
|
+
const avatarInputId = $props.id()
|
|
37
40
|
let editMode = $state(false)
|
|
38
41
|
let formData = $state<UserProfileSubmitData>({
|
|
39
42
|
username: '',
|
|
@@ -163,24 +166,25 @@
|
|
|
163
166
|
<Avatar
|
|
164
167
|
src={user?.avatarUrl ?? user?.avatar}
|
|
165
168
|
alt={uiM('user_profile_avatar')}
|
|
169
|
+
name={user?.displayName || user?.nickname || user?.username}
|
|
166
170
|
size='lg'
|
|
167
|
-
ring
|
|
168
171
|
class='shadow-sm'
|
|
169
|
-
placeholder={user?.username?.charAt(0).toUpperCase() || 'U'}
|
|
170
172
|
/>
|
|
171
173
|
{#if editable && (editMode || alwaysEditable)}
|
|
172
174
|
<div>
|
|
173
175
|
<BareInput
|
|
176
|
+
id={avatarInputId}
|
|
174
177
|
type='file'
|
|
175
|
-
class='
|
|
176
|
-
accept=
|
|
178
|
+
class='hidden'
|
|
179
|
+
accept={avatarAccept}
|
|
177
180
|
disabled={loading}
|
|
178
181
|
onchange={handleAvatarChange}
|
|
179
182
|
/>
|
|
183
|
+
<Button type='button' variant='outline' size='sm' disabled={loading} onclick={() => document.getElementById(avatarInputId)?.click()}>{uiM('user_profile_upload_avatar')}</Button>
|
|
180
184
|
{#if errors.avatar}
|
|
181
185
|
<p class='text-xs text-error mt-1' role='alert'>{errors.avatar}</p>
|
|
182
186
|
{:else}
|
|
183
|
-
<p class='text-xs text-base-content/60 mt-1'>{uiM('user_profile_avatar_hint')}</p>
|
|
187
|
+
<p class='text-xs text-base-content/60 mt-1'>{avatarHint ?? uiM('user_profile_avatar_hint')}</p>
|
|
184
188
|
{/if}
|
|
185
189
|
</div>
|
|
186
190
|
{/if}
|
|
@@ -299,6 +299,10 @@ export interface UserProfileProps extends DataAttributes {
|
|
|
299
299
|
fields?: UserProfileField[];
|
|
300
300
|
/** 头像上传地址 */
|
|
301
301
|
avatarUploadUrl?: string;
|
|
302
|
+
/** 文件选择器允许的头像类型;最终校验由业务上传服务负责。 */
|
|
303
|
+
avatarAccept?: string;
|
|
304
|
+
/** 与业务上传限制一致的提示文案。 */
|
|
305
|
+
avatarHint?: string;
|
|
302
306
|
/** 自定义类名 */
|
|
303
307
|
class?: string;
|
|
304
308
|
/** 错误信息 */
|
package/dist/messages/en-US.json
CHANGED
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"empty_title": "No data",
|
|
36
36
|
"tag_input_placeholder": "Press Enter to add",
|
|
37
37
|
"common_close": "Close",
|
|
38
|
+
"common_dialog": "Dialog",
|
|
38
39
|
"modal_close": "Close modal",
|
|
39
40
|
"drawer_close": "Close sidebar",
|
|
40
41
|
"drawer_resize": "Resize drawer",
|
|
@@ -81,6 +82,7 @@
|
|
|
81
82
|
"avatar_upload_invalid_type": "Please select an image file",
|
|
82
83
|
"avatar_upload_failed": "Upload failed",
|
|
83
84
|
"user_profile_avatar": "Avatar",
|
|
85
|
+
"user_profile_upload_avatar": "Upload avatar",
|
|
84
86
|
"user_profile_username": "Username",
|
|
85
87
|
"user_profile_email": "Email",
|
|
86
88
|
"user_profile_display_name": "Display Name",
|
package/dist/messages/zh-CN.json
CHANGED
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"empty_title": "暂无数据",
|
|
36
36
|
"tag_input_placeholder": "按 Enter 添加",
|
|
37
37
|
"common_close": "关闭",
|
|
38
|
+
"common_dialog": "对话框",
|
|
38
39
|
"modal_close": "关闭模态框",
|
|
39
40
|
"drawer_close": "关闭侧边栏",
|
|
40
41
|
"drawer_resize": "调整抽屉宽度",
|
|
@@ -81,6 +82,7 @@
|
|
|
81
82
|
"avatar_upload_invalid_type": "请选择图片文件",
|
|
82
83
|
"avatar_upload_failed": "上传失败",
|
|
83
84
|
"user_profile_avatar": "头像",
|
|
85
|
+
"user_profile_upload_avatar": "上传头像",
|
|
84
86
|
"user_profile_username": "用户名",
|
|
85
87
|
"user_profile_email": "邮箱",
|
|
86
88
|
"user_profile_display_name": "显示名称",
|
package/dist/theme-config.d.ts
CHANGED
|
@@ -154,9 +154,11 @@ export declare function applyTheme(theme: string, persist?: boolean): void;
|
|
|
154
154
|
*/
|
|
155
155
|
export declare function getCurrentTheme(): string;
|
|
156
156
|
/**
|
|
157
|
-
*
|
|
157
|
+
* 获取保存的主题;存储为空或不可用时使用宿主默认主题。
|
|
158
|
+
*
|
|
159
|
+
* @param fallbackTheme - 未保存主题时的默认值,默认 light
|
|
158
160
|
*/
|
|
159
|
-
export declare function getSavedTheme(): string;
|
|
161
|
+
export declare function getSavedTheme(fallbackTheme?: string): string;
|
|
160
162
|
/**
|
|
161
163
|
* DaisyUI 主题配置字符串(用于 app.css)
|
|
162
164
|
*
|
package/dist/theme-config.js
CHANGED
|
@@ -185,10 +185,12 @@ export function getCurrentTheme() {
|
|
|
185
185
|
return document.documentElement.getAttribute('data-theme') ?? DEFAULT_THEME;
|
|
186
186
|
}
|
|
187
187
|
/**
|
|
188
|
-
*
|
|
188
|
+
* 获取保存的主题;存储为空或不可用时使用宿主默认主题。
|
|
189
|
+
*
|
|
190
|
+
* @param fallbackTheme - 未保存主题时的默认值,默认 light
|
|
189
191
|
*/
|
|
190
|
-
export function getSavedTheme() {
|
|
191
|
-
return readStoredValue(THEME_STORAGE_KEY) ??
|
|
192
|
+
export function getSavedTheme(fallbackTheme = DEFAULT_THEME) {
|
|
193
|
+
return readStoredValue(THEME_STORAGE_KEY) ?? fallbackTheme;
|
|
192
194
|
}
|
|
193
195
|
/**
|
|
194
196
|
* DaisyUI 主题配置字符串(用于 app.css)
|
package/dist/types.d.ts
CHANGED
|
@@ -160,7 +160,7 @@ export interface InputProps extends DataAttributes {
|
|
|
160
160
|
* 无样式输入框属性
|
|
161
161
|
*/
|
|
162
162
|
export interface BareInputProps extends DataAttributes {
|
|
163
|
-
/**
|
|
163
|
+
/** 文本值;file 类型通过事件读取 files,不绑定文件路径。 */
|
|
164
164
|
value?: string;
|
|
165
165
|
/** 占位符 */
|
|
166
166
|
placeholder?: string;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@h-ai/ui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.0-alpha.
|
|
4
|
+
"version": "0.1.0-alpha.58",
|
|
5
5
|
"description": "Hai Framework UI component library for Svelte 5 Runes.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"svelte": "./dist/index.js",
|
|
@@ -19,6 +19,10 @@
|
|
|
19
19
|
"types": "./vite.d.ts",
|
|
20
20
|
"import": "./vite.js"
|
|
21
21
|
},
|
|
22
|
+
"./theme": {
|
|
23
|
+
"types": "./dist/theme-config.d.ts",
|
|
24
|
+
"import": "./dist/theme-config.js"
|
|
25
|
+
},
|
|
22
26
|
"./components/*": {
|
|
23
27
|
"types": "./dist/components/*.d.ts",
|
|
24
28
|
"svelte": "./dist/components/*.js",
|
|
@@ -39,35 +43,35 @@
|
|
|
39
43
|
"access": "public"
|
|
40
44
|
},
|
|
41
45
|
"engines": {
|
|
42
|
-
"node": "
|
|
46
|
+
"node": "^22.22.2 || ^24.15.0 || >=26.0.0"
|
|
43
47
|
},
|
|
44
48
|
"peerDependencies": {
|
|
45
|
-
"svelte": "^5.
|
|
49
|
+
"svelte": "^5.57.2"
|
|
46
50
|
},
|
|
47
51
|
"dependencies": {
|
|
48
|
-
"@
|
|
49
|
-
"@
|
|
50
|
-
"
|
|
52
|
+
"@h-ai/core": "0.1.0-alpha.58",
|
|
53
|
+
"@internationalized/date": "^3.12.4",
|
|
54
|
+
"@shikijs/langs": "^4.5.0",
|
|
55
|
+
"bits-ui": "^2.19.5",
|
|
51
56
|
"html2canvas": "^1.4.1",
|
|
52
57
|
"jspdf": "^4.2.1",
|
|
53
|
-
"layerchart": "2.
|
|
54
|
-
"marked": "^18.
|
|
55
|
-
"mermaid": "^
|
|
56
|
-
"shiki": "^4.0
|
|
57
|
-
"tailwind-merge": "^3.
|
|
58
|
-
"@h-ai/core": "0.1.0-alpha.56"
|
|
58
|
+
"layerchart": "2.6.1",
|
|
59
|
+
"marked": "^18.1.0",
|
|
60
|
+
"mermaid": "^12.1.0",
|
|
61
|
+
"shiki": "^4.5.0",
|
|
62
|
+
"tailwind-merge": "^3.7.0"
|
|
59
63
|
},
|
|
60
64
|
"devDependencies": {
|
|
61
|
-
"@sveltejs/package": "^
|
|
62
|
-
"@sveltejs/vite-plugin-svelte": "^7.1
|
|
63
|
-
"@types/node": "^
|
|
64
|
-
"jsdom": "^
|
|
65
|
+
"@sveltejs/package": "^3.0.0",
|
|
66
|
+
"@sveltejs/vite-plugin-svelte": "^7.3.1",
|
|
67
|
+
"@types/node": "^26.6.5",
|
|
68
|
+
"jsdom": "^30.1.2",
|
|
65
69
|
"rimraf": "^6.1.3",
|
|
66
|
-
"svelte": "^5.
|
|
67
|
-
"svelte-check": "^4.
|
|
70
|
+
"svelte": "^5.57.2",
|
|
71
|
+
"svelte-check": "^4.7.6",
|
|
68
72
|
"typescript": "^6.0.3",
|
|
69
|
-
"vite": "^8.
|
|
70
|
-
"vitest": "^4.1.
|
|
73
|
+
"vite": "^8.3.4",
|
|
74
|
+
"vitest": "^4.1.11"
|
|
71
75
|
},
|
|
72
76
|
"scripts": {
|
|
73
77
|
"build": "svelte-package -i src/lib",
|
|
@@ -75,7 +79,7 @@
|
|
|
75
79
|
"check": "svelte-check --tsconfig ./tsconfig.json",
|
|
76
80
|
"test": "vitest run",
|
|
77
81
|
"test:watch": "vitest",
|
|
78
|
-
"typecheck": "
|
|
82
|
+
"typecheck": "node ../../scripts/typecheck.mjs --noEmit",
|
|
79
83
|
"lint": "eslint .",
|
|
80
84
|
"lint:fix": "eslint . --fix",
|
|
81
85
|
"clean": "rimraf dist .turbo"
|