@aquiferre/theme-kit 0.1.3 → 0.2.0

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/index.d.ts CHANGED
@@ -1,3 +1,12 @@
1
1
  export { useThemeStore, themeList, type ThemeMode, type ThemeInfo } from './theme'
2
+ export {
3
+ useLocaleStore,
4
+ resolveInitialLocale,
5
+ SUPPORTED_LOCALES,
6
+ DEFAULT_LOCALE,
7
+ type LocaleCode,
8
+ type LocaleInfo,
9
+ } from './locale'
10
+ export { themeKitMessages, themeKitMessagesZhCN, themeKitMessagesEnUS } from './messages'
2
11
  export { ThemeKitPlugin, type SystemColors } from './plugin'
3
12
  export { useSystemColors, useSystemColor } from './composables'
package/index.ts CHANGED
@@ -1,3 +1,12 @@
1
1
  export { useThemeStore, themeList, type ThemeMode, type ThemeInfo } from './theme'
2
+ export {
3
+ useLocaleStore,
4
+ resolveInitialLocale,
5
+ SUPPORTED_LOCALES,
6
+ DEFAULT_LOCALE,
7
+ type LocaleCode,
8
+ type LocaleInfo,
9
+ } from './locale'
10
+ export { themeKitMessages, themeKitMessagesZhCN, themeKitMessagesEnUS } from './messages'
2
11
  export { ThemeKitPlugin, type SystemColors } from './plugin'
3
12
  export { useSystemColors, useSystemColor } from './composables'
package/locale.ts ADDED
@@ -0,0 +1,80 @@
1
+ import { defineStore } from 'pinia'
2
+ import { ref, watch } from 'vue'
3
+
4
+ export type LocaleCode = 'zh-CN' | 'en-US'
5
+
6
+ export interface LocaleInfo {
7
+ code: LocaleCode
8
+ /** 母语名称:语言切换器选项固定用母语展示,不随界面语言翻译 */
9
+ label: string
10
+ }
11
+
12
+ export const DEFAULT_LOCALE: LocaleCode = 'zh-CN'
13
+
14
+ /** 支持的语言列表,新增语言需同步 front-kit 词典与后端 users.language 白名单 */
15
+ export const SUPPORTED_LOCALES: readonly LocaleInfo[] = [
16
+ { code: 'zh-CN', label: '简体中文' },
17
+ { code: 'en-US', label: 'English' },
18
+ ]
19
+
20
+ const STORAGE_KEY = 'locale'
21
+
22
+ const supportedCodes = SUPPORTED_LOCALES.map((l) => l.code)
23
+
24
+ function isSupported(l: string | null): l is LocaleCode {
25
+ return !!l && (supportedCodes as string[]).includes(l)
26
+ }
27
+
28
+ function browserLocale(): LocaleCode | null {
29
+ if (typeof window === 'undefined' || !navigator.language) return null
30
+ const lang = navigator.language.toLowerCase()
31
+ if (lang.startsWith('zh')) return 'zh-CN'
32
+ if (lang.startsWith('en')) return 'en-US'
33
+ return null
34
+ }
35
+
36
+ /**
37
+ * 初始语言解析(非响应式纯函数,可在组件外/创建 i18n 实例前调用):
38
+ * localStorage > 浏览器语言白名单 > zh-CN。
39
+ */
40
+ export function resolveInitialLocale(): LocaleCode {
41
+ if (typeof window === 'undefined') return DEFAULT_LOCALE
42
+ const stored = localStorage.getItem(STORAGE_KEY)
43
+ if (isSupported(stored)) return stored
44
+ return browserLocale() ?? DEFAULT_LOCALE
45
+ }
46
+
47
+ /**
48
+ * 语言偏好 store:仿 useThemeStore —— localStorage 'locale' 持久化 +
49
+ * storage 事件跨 tab 同步 + 自动同步 <html lang>。
50
+ * 登录后以服务端 profile.language 为准覆盖(app 侧实现)。
51
+ */
52
+ export const useLocaleStore = defineStore('locale', () => {
53
+ const locale = ref<LocaleCode>(resolveInitialLocale())
54
+
55
+ function apply(l: LocaleCode) {
56
+ if (typeof document !== 'undefined') {
57
+ document.documentElement.lang = l
58
+ }
59
+ if (typeof window !== 'undefined') {
60
+ localStorage.setItem(STORAGE_KEY, l)
61
+ }
62
+ }
63
+
64
+ function setLocale(l: LocaleCode) {
65
+ if (!isSupported(l)) return
66
+ locale.value = l
67
+ }
68
+
69
+ watch(locale, apply, { immediate: true })
70
+
71
+ if (typeof window !== 'undefined') {
72
+ window.addEventListener('storage', (event) => {
73
+ if (event.key === STORAGE_KEY && isSupported(event.newValue)) {
74
+ locale.value = event.newValue
75
+ }
76
+ })
77
+ }
78
+
79
+ return { locale, setLocale }
80
+ })
@@ -0,0 +1,9 @@
1
+ // theme-kit 词典(en-US):扁平 key 结构,key 前缀 theme.,由 app 在 createI18n 时与自身词典合并
2
+ export const themeKitMessagesEnUS: Record<string, string> = {
3
+ 'theme.light.label': 'Light',
4
+ 'theme.light.description': 'Bright and clear interface',
5
+ 'theme.dark.label': 'Dark',
6
+ 'theme.dark.description': 'Eye-friendly dark theme',
7
+ 'theme.neutral.label': 'Neutral',
8
+ 'theme.neutral.description': 'Low-saturation soft tones',
9
+ }
@@ -0,0 +1,14 @@
1
+ import { themeKitMessagesZhCN } from './zh-CN'
2
+ import { themeKitMessagesEnUS } from './en-US'
3
+
4
+ export { themeKitMessagesZhCN } from './zh-CN'
5
+ export { themeKitMessagesEnUS } from './en-US'
6
+
7
+ /** 各语言词典聚合:app 侧 createI18n 时展开合并 */
8
+ export const themeKitMessages = {
9
+ 'zh-CN': themeKitMessagesZhCN,
10
+ 'en-US': themeKitMessagesEnUS,
11
+ }
12
+
13
+ export type ThemeKitMessagesZh = typeof themeKitMessagesZhCN
14
+ export type ThemeKitMessagesEn = typeof themeKitMessagesEnUS
@@ -0,0 +1,9 @@
1
+ // theme-kit 词典(zh-CN):扁平 key 结构,key 前缀 theme.,由 app 在 createI18n 时与自身词典合并
2
+ export const themeKitMessagesZhCN: Record<string, string> = {
3
+ 'theme.light.label': '浅色模式',
4
+ 'theme.light.description': '明亮清晰的界面',
5
+ 'theme.dark.label': '深色模式',
6
+ 'theme.dark.description': '护眼科普的暗色主题',
7
+ 'theme.neutral.label': '中性模式',
8
+ 'theme.neutral.description': '低饱和度的柔和色调',
9
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aquiferre/theme-kit",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -15,9 +15,15 @@
15
15
  "./components.css": "./components.css",
16
16
  "./tailwind.preset": "./tailwind.preset.js"
17
17
  },
18
- "files": ["*.css", "*.ts", "*.js", "*.d.ts"],
18
+ "files": [
19
+ "*.css",
20
+ "*.ts",
21
+ "*.js",
22
+ "*.d.ts",
23
+ "messages/*.ts"
24
+ ],
19
25
  "peerDependencies": {
20
- "pinia": "^2.0.0",
26
+ "pinia": ">=2.0.0",
21
27
  "vue": "^3.0.0"
22
28
  },
23
29
  "publishConfig": {
package/theme.ts CHANGED
@@ -5,32 +5,33 @@ export type ThemeMode = 'light' | 'dark' | 'neutral'
5
5
 
6
6
  export interface ThemeInfo {
7
7
  mode: ThemeMode
8
- label: string
8
+ labelKey: string
9
9
  icon: string
10
- description: string
10
+ descriptionKey: string
11
11
  }
12
12
 
13
13
  /**
14
- * 主题列表 (内置数据)
14
+ * 主题列表 (内置数据):文案经 labelKey/descriptionKey 由消费组件 t() 翻译,
15
+ * 词典来自 theme-kit/messages(key 前缀 theme.)。
15
16
  */
16
17
  export const themeList: ThemeInfo[] = [
17
18
  {
18
19
  mode: 'light',
19
- label: '浅色模式',
20
+ labelKey: 'theme.light.label',
20
21
  icon: '☀️',
21
- description: '明亮清晰的界面'
22
+ descriptionKey: 'theme.light.description'
22
23
  },
23
24
  {
24
25
  mode: 'dark',
25
- label: '深色模式',
26
+ labelKey: 'theme.dark.label',
26
27
  icon: '🌙',
27
- description: '护眼科普的暗色主题'
28
+ descriptionKey: 'theme.dark.description'
28
29
  },
29
30
  {
30
31
  mode: 'neutral',
31
- label: '中性模式',
32
+ labelKey: 'theme.neutral.label',
32
33
  icon: '✨',
33
- description: '低饱和度的柔和色调'
34
+ descriptionKey: 'theme.neutral.description'
34
35
  }
35
36
  ]
36
37
 
package/variables.css CHANGED
@@ -28,6 +28,10 @@
28
28
  --color-border-focus: #2563eb;
29
29
  --color-border-danger: #dc2626;
30
30
 
31
+ /* Toggle switch: per-theme thumb & idle ring */
32
+ --color-switch-thumb: #ffffff;
33
+ --color-switch-ring: #cbd5e1;
34
+
31
35
  --color-accent-primary: #2563eb;
32
36
  --color-accent-primary-hover: #1d4ed8;
33
37
  --color-accent-secondary: #4f46e5;
@@ -104,6 +108,10 @@
104
108
  --color-border-focus: #818cf8;
105
109
  --color-border-danger: #f87171;
106
110
 
111
+ /* Toggle switch */
112
+ --color-switch-thumb: #f1f5f9;
113
+ --color-switch-ring: transparent;
114
+
107
115
  --color-accent-primary: #818cf8;
108
116
  --color-accent-primary-hover: #6366f1;
109
117
  --color-accent-secondary: #a78bfa;
@@ -168,6 +176,10 @@
168
176
  --color-border-focus: #5a6a8a;
169
177
  --color-border-danger: #cc4444;
170
178
 
179
+ /* Toggle switch */
180
+ --color-switch-thumb: #ffffff;
181
+ --color-switch-ring: #b0b0b0;
182
+
171
183
  --color-accent-primary: #5a6a8a;
172
184
  --color-accent-primary-hover: #4a5a7a;
173
185
  --color-accent-secondary: #6a7a9a;