@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.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` | 模态框 | `open`, `title`, `size`, `radius`, `bodyOverflow`, `showClose` |
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
- const lines = scriptContent.split('\n')
134
- let buffer = ''
135
-
136
- const flushImport = (statement) => {
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
- buffer += ` ${trimmed}`
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={(e) => {
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 = e.currentTarget.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
- bind:value
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 '../../../paraglide/runtime.js'
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 '../../../paraglide/runtime.js'
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='file-input file-input-sm w-full max-w-xs'
176
- accept='image/*'
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
  /** 错误信息 */
@@ -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",
@@ -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": "显示名称",
@@ -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
- * 获取保存的主题(从 localStorage)
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
  *
@@ -185,10 +185,12 @@ export function getCurrentTheme() {
185
185
  return document.documentElement.getAttribute('data-theme') ?? DEFAULT_THEME;
186
186
  }
187
187
  /**
188
- * 获取保存的主题(从 localStorage)
188
+ * 获取保存的主题;存储为空或不可用时使用宿主默认主题。
189
+ *
190
+ * @param fallbackTheme - 未保存主题时的默认值,默认 light
189
191
  */
190
- export function getSavedTheme() {
191
- return readStoredValue(THEME_STORAGE_KEY) ?? DEFAULT_THEME;
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.56",
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": ">=22.12.0"
46
+ "node": "^22.22.2 || ^24.15.0 || >=26.0.0"
43
47
  },
44
48
  "peerDependencies": {
45
- "svelte": "^5.55.9"
49
+ "svelte": "^5.57.2"
46
50
  },
47
51
  "dependencies": {
48
- "@internationalized/date": "^3.8.0",
49
- "@shikijs/langs": "^4.0.2",
50
- "bits-ui": "^2.18.1",
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.0.1",
54
- "marked": "^18.0.0",
55
- "mermaid": "^11.12.0",
56
- "shiki": "^4.0.2",
57
- "tailwind-merge": "^3.5.0",
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": "^2.5.7",
62
- "@sveltejs/vite-plugin-svelte": "^7.1.2",
63
- "@types/node": "^25.9.1",
64
- "jsdom": "^29.0.2",
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.55.9",
67
- "svelte-check": "^4.4.8",
70
+ "svelte": "^5.57.2",
71
+ "svelte-check": "^4.7.6",
68
72
  "typescript": "^6.0.3",
69
- "vite": "^8.0.14",
70
- "vitest": "^4.1.7"
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": "tsc --noEmit",
82
+ "typecheck": "node ../../scripts/typecheck.mjs --noEmit",
79
83
  "lint": "eslint .",
80
84
  "lint:fix": "eslint . --fix",
81
85
  "clean": "rimraf dist .turbo"