@h-ai/ui 0.1.0-alpha.18 → 0.1.0-alpha.20

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.
@@ -0,0 +1,58 @@
1
+ /**
2
+ * @h-ai/ui — 浏览器能力安全辅助
3
+ *
4
+ * 统一封装容易在受限 WebView、隐私模式或不安全上下文中失败的浏览器 API,
5
+ * 让组件在能力缺失时优雅降级而不是抛出运行时错误。
6
+ */
7
+ /**
8
+ * 安全读取 localStorage。
9
+ *
10
+ * 某些浏览器隐私模式或嵌入式 WebView 会在访问 localStorage 时直接抛出 SecurityError,
11
+ * 这里统一吞掉异常并回退为 null。
12
+ */
13
+ export function readStoredValue(key) {
14
+ if (typeof localStorage === 'undefined') {
15
+ return null;
16
+ }
17
+ try {
18
+ return localStorage.getItem(key);
19
+ }
20
+ catch {
21
+ return null;
22
+ }
23
+ }
24
+ /**
25
+ * 安全写入 localStorage。
26
+ *
27
+ * 返回值显式告诉调用方持久化是否成功,方便调用方在失败时继续使用内存态而不必中断交互。
28
+ */
29
+ export function writeStoredValue(key, value) {
30
+ if (typeof localStorage === 'undefined') {
31
+ return false;
32
+ }
33
+ try {
34
+ localStorage.setItem(key, value);
35
+ return true;
36
+ }
37
+ catch {
38
+ return false;
39
+ }
40
+ }
41
+ /**
42
+ * 安全写入剪贴板。
43
+ *
44
+ * 在不安全上下文、旧 WebView 或浏览器策略禁用时直接返回 false,
45
+ * 让上层决定是否展示成功反馈。
46
+ */
47
+ export async function writeTextToClipboard(text) {
48
+ if (typeof navigator === 'undefined' || !navigator.clipboard?.writeText) {
49
+ return false;
50
+ }
51
+ try {
52
+ await navigator.clipboard.writeText(text);
53
+ return true;
54
+ }
55
+ catch {
56
+ return false;
57
+ }
58
+ }
@@ -81,9 +81,10 @@ export declare const DEFAULT_THEME = "light";
81
81
  */
82
82
  export declare const THEME_STORAGE_KEY = "theme";
83
83
  /**
84
- * 获取主题初始化脚本(用于 app.html 防闪烁)
84
+ * 获取主题初始化脚本(用于 HTML shell 防闪烁)
85
85
  *
86
- * 使用方式:在 app.html 的 <head> 中添加 <script>{getThemeInitScript()}</script>
86
+ * 返回值是“脚本标签内的文本内容”,适合宿主在构建阶段或服务端模板中注入。
87
+ * 对于 SvelteKit `app.html`,请直接粘贴这段脚本字符串的内容,不能写 `{@html getThemeInitScript()}`。
87
88
  */
88
89
  export declare function getThemeInitScript(): string;
89
90
  /**
@@ -4,6 +4,7 @@
4
4
  * DaisyUI 主题元数据和配置
5
5
  * @module theme-config
6
6
  */
7
+ import { readStoredValue, writeStoredValue } from './internal/browser-safety.js';
7
8
  /**
8
9
  * DaisyUI 内置主题配置(精选)
9
10
  */
@@ -83,12 +84,13 @@ export const DEFAULT_THEME = 'light';
83
84
  */
84
85
  export const THEME_STORAGE_KEY = 'theme';
85
86
  /**
86
- * 获取主题初始化脚本(用于 app.html 防闪烁)
87
+ * 获取主题初始化脚本(用于 HTML shell 防闪烁)
87
88
  *
88
- * 使用方式:在 app.html 的 <head> 中添加 <script>{getThemeInitScript()}</script>
89
+ * 返回值是“脚本标签内的文本内容”,适合宿主在构建阶段或服务端模板中注入。
90
+ * 对于 SvelteKit `app.html`,请直接粘贴这段脚本字符串的内容,不能写 `{@html getThemeInitScript()}`。
89
91
  */
90
92
  export function getThemeInitScript() {
91
- return `(function(){var t=localStorage.getItem('${THEME_STORAGE_KEY}')||'${DEFAULT_THEME}';document.documentElement.setAttribute('data-theme',t)})()`;
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)})()`;
92
94
  }
93
95
  /**
94
96
  * 应用主题
@@ -99,8 +101,8 @@ export function applyTheme(theme, persist = true) {
99
101
  if (typeof document === 'undefined')
100
102
  return;
101
103
  document.documentElement.setAttribute('data-theme', theme);
102
- if (persist && typeof localStorage !== 'undefined') {
103
- localStorage.setItem(THEME_STORAGE_KEY, theme);
104
+ if (persist) {
105
+ writeStoredValue(THEME_STORAGE_KEY, theme);
104
106
  }
105
107
  }
106
108
  /**
@@ -115,9 +117,7 @@ export function getCurrentTheme() {
115
117
  * 获取保存的主题(从 localStorage)
116
118
  */
117
119
  export function getSavedTheme() {
118
- if (typeof localStorage === 'undefined')
119
- return DEFAULT_THEME;
120
- return localStorage.getItem(THEME_STORAGE_KEY) ?? DEFAULT_THEME;
120
+ return readStoredValue(THEME_STORAGE_KEY) ?? DEFAULT_THEME;
121
121
  }
122
122
  /**
123
123
  * DaisyUI 主题配置字符串(用于 app.css)
package/dist/types.d.ts CHANGED
@@ -908,7 +908,7 @@ export interface StepsProps {
908
908
  * 图标按钮属性
909
909
  */
910
910
  export interface IconButtonProps {
911
- /** 图标(HTML/SVG 字符串或 Snippet) */
911
+ /** 图标(受信任的内联 SVG 字符串或 Snippet) */
912
912
  icon?: string | Snippet;
913
913
  /** 标签(无障碍) */
914
914
  label?: 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.18",
4
+ "version": "0.1.0-alpha.20",
5
5
  "description": "Hai Framework UI component library for Svelte 5 Runes.",
6
6
  "license": "Apache-2.0",
7
7
  "svelte": "./dist/index.js",
@@ -44,7 +44,7 @@
44
44
  "marked": "^18.0.0",
45
45
  "shiki": "^4.0.2",
46
46
  "tailwind-merge": "^3.5.0",
47
- "@h-ai/core": "0.1.0-alpha.18"
47
+ "@h-ai/core": "0.1.0-alpha.20"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@sveltejs/package": "^2.5.7",