@h-ai/ui 0.1.0-alpha.24 → 0.1.0-alpha.25

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
@@ -506,24 +506,42 @@ npm install -D @iconify/tailwind4 @iconify-json/tabler
506
506
  ```ts
507
507
  import {
508
508
  applyTheme, // 应用主题(自动持久化到 localStorage)
509
+ createThemeBootstrapScript, // 生成可注入 app.html 的首屏主题恢复脚本
509
510
  getCurrentTheme, // 获取当前主题
510
511
  getThemeInitScript, // 返回可注入到 HTML shell 的防闪烁脚本文本
511
512
  isDarkTheme, // 检查是否暗色主题
513
+ normalizeHexColor, // 归一化 #RGB / #RRGGBB 主题色
514
+ normalizeThemeId, // 校验主题 ID 并自动回退
512
515
  THEME_GROUPS, // 按亮色/暗色分组
513
516
  THEMES, // ThemeInfo[] — 15 个精选主题元数据
514
517
  } from '@h-ai/ui'
515
518
  ```
516
519
 
517
- 在 SvelteKit 的 `app.html` 中,请直接写入下面这段脚本内容(与 `getThemeInitScript()` 返回值一致):
520
+ 简单场景可以直接使用 `getThemeInitScript()`;如果应用有自定义存储 key、主题色 CSS 变量或语言偏好,推荐在服务端通过 `createThemeBootstrapScript()` 生成脚本后再注入 `app.html`。
521
+
522
+ 在 SvelteKit 的 `app.html` 中,可以先放一个占位符:
518
523
 
519
524
  ```html
520
525
  <head>
521
- <script>
522
- (function(){var t='light';try{var s=localStorage.getItem('theme');if(s)t=s}catch{}document.documentElement.setAttribute('data-theme',t)})()
523
- </script>
526
+ <script>%theme_bootstrap%</script>
524
527
  </head>
525
528
  ```
526
529
 
530
+ 然后在 `hooks.server.ts` 里注入:
531
+
532
+ ```ts
533
+ import { createThemeBootstrapScript, DEFAULT_THEME_COLOR_CSS_VAR } from '@h-ai/ui'
534
+
535
+ const themeBootstrapScript = createThemeBootstrapScript({
536
+ storageKey: 'hai-demo-preferences',
537
+ legacyThemeStorageKey: 'hai-demo-theme',
538
+ defaultThemeColor: '#5765f0',
539
+ colorCssVar: DEFAULT_THEME_COLOR_CSS_VAR,
540
+ })
541
+
542
+ transformPageChunk: ({ html }) => html.replace('%theme_bootstrap%', themeBootstrapScript)
543
+ ```
544
+
527
545
  ## 国际化 (i18n)
528
546
 
529
547
  @h-ai/ui 采用**组件内置翻译**模式:
@@ -86,6 +86,17 @@
86
86
  }
87
87
  }
88
88
 
89
+ function handleViewportKeydown(event: KeyboardEvent) {
90
+ if (
91
+ event.target === event.currentTarget
92
+ && closeOnBackdrop
93
+ && (event.key === 'Enter' || event.key === ' ')
94
+ ) {
95
+ event.preventDefault()
96
+ handleClose()
97
+ }
98
+ }
99
+
89
100
  function handleDialogCancel(event: Event) {
90
101
  // 原生 dialog 在按下 Escape 时会先触发 cancel;
91
102
  // 这里统一接管关闭逻辑,避免浏览器直接 close 后跳过组件自己的 onclose 回调。
@@ -117,74 +128,76 @@
117
128
  bind:this={modalElement}
118
129
  class='hai-modal'
119
130
  oncancel={handleDialogCancel}
120
- onclick={handleDialogClick}
121
131
  >
122
- <div class={modalBoxClass} style={panelStyle}>
123
- {#if hasHeader}
124
- <!-- 顶部栏单独固定,保证滚动时标题和关闭操作始终可见。 -->
125
- <div class='hai-modal__header flex flex-none items-start justify-between gap-4 px-6 py-5 sm:px-7'>
126
- {#if header}
127
- <div class='flex-1 font-semibold text-[1.05rem] text-base-content/92'>
128
- {@render header()}
129
- </div>
130
- {:else if title}
131
- <h3 class='flex-1 font-semibold text-[1.05rem] tracking-tight text-base-content/92'>
132
- {title}
133
- </h3>
134
- {:else}
135
- <div class='flex-1'></div>
136
- {/if}
132
+ <div
133
+ class='hai-modal__viewport'
134
+ role='button'
135
+ tabindex='-1'
136
+ aria-label={uiM('common_close')}
137
+ onclick={handleDialogClick}
138
+ onkeydown={handleViewportKeydown}
139
+ >
140
+ <div class={modalBoxClass} style={panelStyle}>
141
+ {#if hasHeader}
142
+ <!-- 顶部栏单独固定,保证滚动时标题和关闭操作始终可见。 -->
143
+ <div class='hai-modal__header flex flex-none items-start justify-between gap-4 px-6 py-5 sm:px-7'>
144
+ {#if header}
145
+ <div class='flex-1 font-semibold text-[1.05rem] text-base-content/92'>
146
+ {@render header()}
147
+ </div>
148
+ {:else if title}
149
+ <h3 class='flex-1 font-semibold text-[1.05rem] tracking-tight text-base-content/92'>
150
+ {title}
151
+ </h3>
152
+ {:else}
153
+ <div class='flex-1'></div>
154
+ {/if}
155
+
156
+ {#if showClose}
157
+ <button
158
+ type='button'
159
+ class='hai-modal__close'
160
+ aria-label={uiM('common_close')}
161
+ onclick={handleClose}
162
+ >
163
+ <svg viewBox='0 0 24 24' class='hai-modal__close-icon' aria-hidden='true'>
164
+ <path
165
+ d='M6 6l12 12M18 6L6 18'
166
+ fill='none'
167
+ stroke='currentColor'
168
+ stroke-width='1.9'
169
+ stroke-linecap='round'
170
+ ></path>
171
+ </svg>
172
+ </button>
173
+ {/if}
174
+ </div>
175
+ {/if}
137
176
 
138
- {#if showClose}
139
- <button
140
- type='button'
141
- class='hai-modal__close'
142
- aria-label={uiM('common_close')}
143
- onclick={handleClose}
144
- >
145
- <svg viewBox='0 0 24 24' class='hai-modal__close-icon' aria-hidden='true'>
146
- <path
147
- d='M6 6l12 12M18 6L6 18'
148
- fill='none'
149
- stroke='currentColor'
150
- stroke-width='1.9'
151
- stroke-linecap='round'
152
- ></path>
153
- </svg>
154
- </button>
177
+ <!-- 主体区域独立滚动,避免长内容把头部和底部一起顶走。 -->
178
+ <div class={cn('min-h-0 flex-1 px-6 py-5 sm:px-7', bodyOverflowClass, bodyClass)}>
179
+ {#if children}
180
+ {@render children()}
155
181
  {/if}
156
182
  </div>
157
- {/if}
158
183
 
159
- <!-- 主体区域独立滚动,避免长内容把头部和底部一起顶走。 -->
160
- <div class={cn('min-h-0 flex-1 px-6 py-5 sm:px-7', bodyOverflowClass, bodyClass)}>
161
- {#if children}
162
- {@render children()}
184
+ {#if footer}
185
+ <!-- 底栏固定在面板底部,用于承接确认/取消或状态摘要。 -->
186
+ <div class='hai-modal__footer flex flex-none items-center px-6 py-4 sm:px-7'>
187
+ <div class='flex w-full items-center justify-end gap-3'>
188
+ {@render footer()}
189
+ </div>
190
+ </div>
163
191
  {/if}
164
192
  </div>
165
-
166
- {#if footer}
167
- <!-- 底栏固定在面板底部,用于承接确认/取消或状态摘要。 -->
168
- <div class='hai-modal__footer flex flex-none items-center bg-base-100 px-6 py-4 sm:px-7'>
169
- <div class='flex w-full items-center justify-end gap-3'>
170
- {@render footer()}
171
- </div>
172
- </div>
173
- {/if}
174
193
  </div>
175
194
  </dialog>
176
195
 
177
196
  <style>
178
- dialog.hai-modal {
179
- inset: 0;
180
- width: 100vw;
181
- min-width: 0;
182
- max-width: none;
183
- height: 100vh;
184
- height: 100dvh;
185
- max-height: none;
197
+ dialog.hai-modal,
198
+ dialog.hai-modal:modal {
186
199
  margin: 0;
187
- padding: clamp(1rem, 2.6vw, 2rem);
200
+ padding: 0;
188
201
  border: 0;
189
202
  background: transparent;
190
203
  outline: none;
@@ -192,9 +205,12 @@
192
205
  box-sizing: border-box;
193
206
  }
194
207
 
195
- dialog.hai-modal[open] {
208
+ .hai-modal__viewport {
209
+ position: fixed;
210
+ inset: 0;
196
211
  display: grid;
197
212
  place-items: center;
213
+ padding: clamp(1rem, 2.6vw, 2rem);
198
214
  }
199
215
 
200
216
  dialog.hai-modal::backdrop {
@@ -220,10 +236,16 @@
220
236
  }
221
237
 
222
238
  .hai-modal__header {
239
+ /* 头部单独承接面板圆角,避免滚动主体或主题背景把顶部边缘切成直角。 */
240
+ border-top-left-radius: inherit;
241
+ border-top-right-radius: inherit;
223
242
  box-shadow: inset 0 -1px 0 color-mix(in srgb, var(--color-base-content) 6%, transparent);
224
243
  }
225
244
 
226
245
  .hai-modal__footer {
246
+ /* 底栏去掉独立背景后,继续继承圆角,保证底部阴影和面板外轮廓保持一致。 */
247
+ border-bottom-right-radius: inherit;
248
+ border-bottom-left-radius: inherit;
227
249
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--color-base-content) 6%, transparent);
228
250
  border-radius: 0 0 1.4rem 1.4rem;
229
251
  }
@@ -24,6 +24,16 @@ function escapeHtml(text) {
24
24
  .replace(/"/g, '&quot;')
25
25
  .replace(/'/g, '&#39;');
26
26
  }
27
+ function createTableCellAttrs(cell) {
28
+ const attrs = [];
29
+ if (cell.align) {
30
+ attrs.push(`style="text-align:${cell.align}"`);
31
+ }
32
+ if (cell.text.trim()) {
33
+ attrs.push(`title="${escapeHtml(cell.text)}"`);
34
+ }
35
+ return attrs.length > 0 ? ` ${attrs.join(' ')}` : '';
36
+ }
27
37
  /**
28
38
  * 创建自定义渲染器对象
29
39
  *
@@ -91,15 +101,13 @@ function createRendererObject(options) {
91
101
  table(token) {
92
102
  let headerHtml = '';
93
103
  for (const cell of token.header) {
94
- const align = cell.align ? ` style="text-align:${cell.align}"` : '';
95
- headerHtml += `<th${align}>${this.parser.parseInline(cell.tokens)}</th>`;
104
+ headerHtml += `<th${createTableCellAttrs(cell)}>${this.parser.parseInline(cell.tokens)}</th>`;
96
105
  }
97
106
  let bodyHtml = '';
98
107
  for (const row of token.rows) {
99
108
  let rowHtml = '';
100
109
  for (const cell of row) {
101
- const align = cell.align ? ` style="text-align:${cell.align}"` : '';
102
- rowHtml += `<td${align}>${this.parser.parseInline(cell.tokens)}</td>`;
110
+ rowHtml += `<td${createTableCellAttrs(cell)}>${this.parser.parseInline(cell.tokens)}</td>`;
103
111
  }
104
112
  bodyHtml += `<tr>${rowHtml}</tr>`;
105
113
  }
@@ -1097,7 +1097,7 @@
1097
1097
  <thead>
1098
1098
  <tr>
1099
1099
  {#each tableColumns as column (column.key)}
1100
- <th>{column.label}</th>
1100
+ <th title={column.label}>{column.label}</th>
1101
1101
  {/each}
1102
1102
  {#if editable}
1103
1103
  <th class='hai-ai-table-action-head'>{uiM('data_table_actions')}</th>
@@ -1122,11 +1122,13 @@
1122
1122
  ondrop={event => handleRowDrop(row.row_id, event)}
1123
1123
  >
1124
1124
  {#each tableColumns as column (column.key)}
1125
+ {@const cellInputValue = getCellInputValue(row, column.key)}
1125
1126
  <td>
1126
1127
  <input
1127
1128
  class={resolveCellInputClass(column.type)}
1128
1129
  type={column.type === 'number' ? 'number' : 'text'}
1129
- value={getCellInputValue(row, column.key)}
1130
+ value={cellInputValue}
1131
+ title={cellInputValue}
1130
1132
  disabled={!editable}
1131
1133
  oninput={event => handleCellInput(row.row_id, column, event)}
1132
1134
  />
@@ -52,6 +52,10 @@ export declare const THEME_GROUPS: ThemeGroup[];
52
52
  * 暗色主题列表
53
53
  */
54
54
  export declare const DARK_THEMES: string[];
55
+ /**
56
+ * 支持的主题 ID 列表
57
+ */
58
+ export declare const SUPPORTED_THEME_IDS: string[];
55
59
  /**
56
60
  * 默认主题色
57
61
  */
@@ -64,6 +68,14 @@ export declare const DEFAULT_THEME_COLOR_CSS_VAR = "--hai-theme-color-default";
64
68
  * 内置主题色预设
65
69
  */
66
70
  export declare const THEME_COLOR_PRESETS: ThemeColorPreset[];
71
+ /**
72
+ * 默认主题
73
+ */
74
+ export declare const DEFAULT_THEME = "light";
75
+ /**
76
+ * 主题存储键名
77
+ */
78
+ export declare const THEME_STORAGE_KEY = "theme";
67
79
  /**
68
80
  * 获取主题信息
69
81
  */
@@ -73,13 +85,57 @@ export declare function getThemeInfo(themeId: string): ThemeInfo | undefined;
73
85
  */
74
86
  export declare function isDarkTheme(themeId: string): boolean;
75
87
  /**
76
- * 默认主题
88
+ * 归一化主题 ID,不支持的值回退到默认主题。
77
89
  */
78
- export declare const DEFAULT_THEME = "light";
90
+ export declare function normalizeThemeId(themeId: string | null | undefined, fallbackThemeId?: string): string;
79
91
  /**
80
- * 主题存储键名
92
+ * 归一化 Hex 颜色;无效值返回 null。
81
93
  */
82
- export declare const THEME_STORAGE_KEY = "theme";
94
+ export declare function normalizeHexColor(value: string | null | undefined): string | null;
95
+ /**
96
+ * 解析主题对应的浏览器色调。
97
+ */
98
+ export declare function resolveThemeTone(themeId: string): 'light' | 'dark';
99
+ /**
100
+ * 主题首屏恢复脚本里可选的语言字段配置。
101
+ *
102
+ * 业务侧如果把语言和主题一起落到同一个偏好对象里,可用这组配置在首屏阶段同步恢复语言值,
103
+ * 避免 HTML shell 和 hydrate 后的语言状态短暂不一致。
104
+ */
105
+ export interface ThemeBootstrapLocaleConfig {
106
+ /** 偏好对象中的语言字段名。 */
107
+ key: string;
108
+ /** 默认语言。 */
109
+ defaultValue: string;
110
+ /** 允许的语言列表。 */
111
+ supportedValues: string[];
112
+ }
113
+ /**
114
+ * 生成首屏主题恢复脚本时使用的宿主侧配置。
115
+ *
116
+ * 这组配置刻意只保留“HTML shell 首屏恢复”所需的信息,避免把运行时 helper 或 UI 组件状态
117
+ * 泄漏到脚本字符串里,方便不同应用在 `app.html` / 服务端模板中复用同一套恢复逻辑。
118
+ */
119
+ export interface ThemeBootstrapScriptOptions {
120
+ /** 偏好存储 key。 */
121
+ storageKey: string;
122
+ /** 历史主题 key;传 null 表示不做迁移。 */
123
+ legacyThemeStorageKey?: string | null;
124
+ /** 默认主题 ID。 */
125
+ defaultThemeId?: string;
126
+ /** 默认主题色;传 null 表示不处理主题色。 */
127
+ defaultThemeColor?: string | null;
128
+ /** 主题色写入的 CSS 变量;传 null 表示不写。 */
129
+ colorCssVar?: string | null;
130
+ /** 主题 tone 写入的 dataset key;传 null 表示不写。 */
131
+ toneDatasetKey?: string | null;
132
+ /** 可选的语言字段配置。 */
133
+ locale?: ThemeBootstrapLocaleConfig | null;
134
+ }
135
+ /**
136
+ * 生成首屏主题恢复脚本,适合注入到 SvelteKit `app.html` 的 `<script>` 中。
137
+ */
138
+ export declare function createThemeBootstrapScript(options: ThemeBootstrapScriptOptions): string;
83
139
  /**
84
140
  * 获取主题初始化脚本(用于 HTML shell 防闪烁)
85
141
  *
@@ -46,6 +46,10 @@ export const THEME_GROUPS = [
46
46
  * 暗色主题列表
47
47
  */
48
48
  export const DARK_THEMES = THEMES.filter(t => t.dark).map(t => t.id);
49
+ /**
50
+ * 支持的主题 ID 列表
51
+ */
52
+ export const SUPPORTED_THEME_IDS = THEMES.map(theme => theme.id);
49
53
  /**
50
54
  * 默认主题色
51
55
  */
@@ -62,6 +66,17 @@ export const THEME_COLOR_PRESETS = [
62
66
  { value: '#13b981', labelKey: 'theme_color_grid_green' },
63
67
  { value: '#1f5eff', labelKey: 'theme_color_ocean' },
64
68
  ];
69
+ const HEX_COLOR_REGEX = /^#[0-9a-f]{6}$/i;
70
+ const SHORT_HEX_COLOR_REGEX = /^#[0-9a-f]{3}$/i;
71
+ const SUPPORTED_THEME_ID_SET = new Set(SUPPORTED_THEME_IDS);
72
+ /**
73
+ * 默认主题
74
+ */
75
+ export const DEFAULT_THEME = 'light';
76
+ /**
77
+ * 主题存储键名
78
+ */
79
+ export const THEME_STORAGE_KEY = 'theme';
65
80
  /**
66
81
  * 获取主题信息
67
82
  */
@@ -74,15 +89,68 @@ export function getThemeInfo(themeId) {
74
89
  export function isDarkTheme(themeId) {
75
90
  return DARK_THEMES.includes(themeId);
76
91
  }
77
- // ─── 主题初始化工具 ───
78
92
  /**
79
- * 默认主题
93
+ * 归一化主题 ID,不支持的值回退到默认主题。
80
94
  */
81
- export const DEFAULT_THEME = 'light';
95
+ export function normalizeThemeId(themeId, fallbackThemeId = DEFAULT_THEME) {
96
+ const normalized = `${themeId ?? ''}`;
97
+ if (SUPPORTED_THEME_ID_SET.has(normalized)) {
98
+ return normalized;
99
+ }
100
+ return SUPPORTED_THEME_ID_SET.has(fallbackThemeId) ? fallbackThemeId : DEFAULT_THEME;
101
+ }
82
102
  /**
83
- * 主题存储键名
103
+ * 归一化 Hex 颜色;无效值返回 null。
84
104
  */
85
- export const THEME_STORAGE_KEY = 'theme';
105
+ export function normalizeHexColor(value) {
106
+ const normalized = value?.trim().toLowerCase() ?? '';
107
+ if (HEX_COLOR_REGEX.test(normalized)) {
108
+ return normalized;
109
+ }
110
+ if (SHORT_HEX_COLOR_REGEX.test(normalized)) {
111
+ return `#${normalized[1]}${normalized[1]}${normalized[2]}${normalized[2]}${normalized[3]}${normalized[3]}`;
112
+ }
113
+ return null;
114
+ }
115
+ /**
116
+ * 解析主题对应的浏览器色调。
117
+ */
118
+ export function resolveThemeTone(themeId) {
119
+ return isDarkTheme(themeId) ? 'dark' : 'light';
120
+ }
121
+ function escapeJsonForScript(value) {
122
+ return JSON.stringify(value)
123
+ .replace(/</g, '\\u003C')
124
+ .replace(/>/g, '\\u003E')
125
+ .replace(/\u2028/g, '\\u2028')
126
+ .replace(/\u2029/g, '\\u2029');
127
+ }
128
+ /**
129
+ * 生成首屏主题恢复脚本,适合注入到 SvelteKit `app.html` 的 `<script>` 中。
130
+ */
131
+ export function createThemeBootstrapScript(options) {
132
+ const defaultThemeId = normalizeThemeId(options.defaultThemeId ?? DEFAULT_THEME);
133
+ const defaultThemeColor = options.defaultThemeColor == null
134
+ ? null
135
+ : normalizeHexColor(options.defaultThemeColor) ?? DEFAULT_THEME_COLOR;
136
+ const locale = options.locale
137
+ ? {
138
+ key: options.locale.key,
139
+ defaultValue: options.locale.defaultValue,
140
+ supportedValues: [...options.locale.supportedValues],
141
+ }
142
+ : null;
143
+ const defaultPreferences = {
144
+ themeId: defaultThemeId,
145
+ };
146
+ if (defaultThemeColor) {
147
+ defaultPreferences.themeColor = defaultThemeColor;
148
+ }
149
+ if (locale) {
150
+ defaultPreferences[locale.key] = locale.defaultValue;
151
+ }
152
+ return `(function(){var d=${escapeJsonForScript(defaultPreferences)};var s=${escapeJsonForScript(options.storageKey)};var l=${escapeJsonForScript(options.legacyThemeStorageKey ?? null)};var u=new Set(${escapeJsonForScript(SUPPORTED_THEME_IDS)});var k=new Set(${escapeJsonForScript(DARK_THEMES)});var c=${escapeJsonForScript(options.colorCssVar ?? null)};var y=${escapeJsonForScript(options.toneDatasetKey ?? null)};var i=${escapeJsonForScript(locale)};function n(e){var r=''+(e??'');return u.has(r)?r:d.themeId}function h(e){var r=(''+(e??'')).trim().toLowerCase();if(/^#[0-9a-f]{6}$/i.test(r))return r;if(/^#[0-9a-f]{3}$/i.test(r))return '#'+r[1]+r[1]+r[2]+r[2]+r[3]+r[3];return Object.prototype.hasOwnProperty.call(d,'themeColor')?d.themeColor:null}function g(e){var r={...d,themeId:n(e?.themeId)};if(Object.prototype.hasOwnProperty.call(d,'themeColor'))r.themeColor=h(e?.themeColor);if(i)r[i.key]=i.supportedValues.includes(e?.[i.key])?e[i.key]:d[i.key];return r}function a(e){var r=document.documentElement;var o=k.has(e.themeId)?'dark':'light';r.setAttribute('data-theme',e.themeId);if(y)r.dataset[y]=o;if(c){if(e.themeColor){r.style.setProperty(c,e.themeColor)}else{r.style.removeProperty(c)}}r.style.setProperty('color-scheme',o)}var p=d;try{var f=window.localStorage.getItem(s);if(f){p=g(JSON.parse(f))}else if(l){p=g({themeId:window.localStorage.getItem(l)})}else{p=g(d)}a(p);window.localStorage.setItem(s,JSON.stringify(p));if(l)window.localStorage.removeItem(l)}catch{a(d)}})()`;
153
+ }
86
154
  /**
87
155
  * 获取主题初始化脚本(用于 HTML shell 防闪烁)
88
156
  *
@@ -90,7 +158,10 @@ export const THEME_STORAGE_KEY = 'theme';
90
158
  * 对于 SvelteKit `app.html`,请直接粘贴这段脚本字符串的内容,不能写 `{@html getThemeInitScript()}`。
91
159
  */
92
160
  export function getThemeInitScript() {
93
- return `(function(){var t='${DEFAULT_THEME}';try{var s=localStorage.getItem('${THEME_STORAGE_KEY}');if(s)t=s}catch{}document.documentElement.setAttribute('data-theme',t)})()`;
161
+ return createThemeBootstrapScript({
162
+ storageKey: THEME_STORAGE_KEY,
163
+ defaultThemeId: DEFAULT_THEME,
164
+ });
94
165
  }
95
166
  /**
96
167
  * 应用主题
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.24",
4
+ "version": "0.1.0-alpha.25",
5
5
  "description": "Hai Framework UI component library for Svelte 5 Runes.",
6
6
  "license": "Apache-2.0",
7
7
  "svelte": "./dist/index.js",
@@ -45,7 +45,7 @@
45
45
  "mermaid": "^11.12.0",
46
46
  "shiki": "^4.0.2",
47
47
  "tailwind-merge": "^3.5.0",
48
- "@h-ai/core": "0.1.0-alpha.24"
48
+ "@h-ai/core": "0.1.0-alpha.25"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@sveltejs/package": "^2.5.7",