skinsuite 0.1.0 → 0.2.1

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.
Files changed (82) hide show
  1. package/README.md +44 -6
  2. package/dist/constants.d.ts +44 -7
  3. package/dist/constants.d.ts.map +1 -1
  4. package/dist/constants.js +44 -7
  5. package/dist/constants.js.map +1 -1
  6. package/dist/index.d.ts +32 -6
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +25 -4
  9. package/dist/index.js.map +1 -1
  10. package/dist/manifest.d.ts +83 -0
  11. package/dist/manifest.d.ts.map +1 -0
  12. package/dist/manifest.js +90 -0
  13. package/dist/manifest.js.map +1 -0
  14. package/dist/registry.d.ts +73 -0
  15. package/dist/registry.d.ts.map +1 -0
  16. package/dist/registry.js +299 -0
  17. package/dist/registry.js.map +1 -0
  18. package/dist/remote-config.d.ts +25 -0
  19. package/dist/remote-config.d.ts.map +1 -0
  20. package/dist/remote-config.js +43 -0
  21. package/dist/remote-config.js.map +1 -0
  22. package/dist/scheme.d.ts +36 -0
  23. package/dist/scheme.d.ts.map +1 -0
  24. package/dist/scheme.js +47 -0
  25. package/dist/scheme.js.map +1 -0
  26. package/dist/types.d.ts +17 -1
  27. package/dist/types.d.ts.map +1 -1
  28. package/dist/views/device-registry.d.ts +45 -0
  29. package/dist/views/device-registry.d.ts.map +1 -0
  30. package/dist/views/device-registry.js +55 -0
  31. package/dist/views/device-registry.js.map +1 -0
  32. package/dist/views/device.d.ts +63 -0
  33. package/dist/views/device.d.ts.map +1 -0
  34. package/dist/views/device.js +59 -0
  35. package/dist/views/device.js.map +1 -0
  36. package/dist/views/params.d.ts +24 -21
  37. package/dist/views/params.d.ts.map +1 -1
  38. package/dist/views/params.js +11 -11
  39. package/dist/views/params.js.map +1 -1
  40. package/docs/onboarding.md +479 -0
  41. package/package.json +23 -2
  42. package/src/constants.ts +141 -100
  43. package/src/index.ts +128 -65
  44. package/src/manifest.ts +131 -0
  45. package/src/registry.ts +400 -0
  46. package/src/scheme.ts +94 -0
  47. package/src/types.ts +19 -1
  48. package/src/views/device-registry.ts +96 -0
  49. package/src/views/device.ts +107 -0
  50. package/src/views/params.ts +26 -23
  51. package/src/vite/dir.ts +85 -0
  52. package/src/vite/index.ts +41 -0
  53. package/src/vite/plugin.ts +409 -0
  54. package/src/vite/theme.d.ts +49 -0
  55. package/src/vue/ThemeDebugPanel.vue +36 -0
  56. package/src/vue/ThemeDebugPanelBody.vue +505 -0
  57. package/src/vue/ThemeRoot.vue +59 -0
  58. package/src/vue/ThemeView.vue +92 -0
  59. package/src/vue/create-theme-state.ts +116 -0
  60. package/src/vue/define-theme-device.ts +157 -0
  61. package/src/vue/device-containers.ts +73 -0
  62. package/src/vue/device-domain.ts +149 -0
  63. package/src/vue/index.ts +98 -0
  64. package/src/vue/panel.ts +16 -0
  65. package/src/vue/plugin.ts +35 -0
  66. package/src/vue/remote-fetch.ts +74 -0
  67. package/src/vue/theme-intent.ts +157 -0
  68. package/src/vue/theme-persistence.ts +136 -0
  69. package/src/vue/theme-runtime.ts +163 -0
  70. package/src/vue/theme-scope.ts +148 -0
  71. package/src/vue/theme-store.ts +58 -0
  72. package/src/vue/theme-views.ts +39 -0
  73. package/src/vue/token-runtime.ts +81 -0
  74. package/src/vue/types.ts +67 -0
  75. package/src/vue/useParentThemeSync.ts +99 -0
  76. package/src/vue/useThemeDebugPanel.ts +192 -0
  77. package/src/vue/useThemeSources.ts +74 -0
  78. package/src/vue/view-page.ts +99 -0
  79. package/src/vue/view-registry.ts +215 -0
  80. package/src/vue/viewport.ts +54 -0
  81. package/src/mode.ts +0 -38
  82. package/src/tone.ts +0 -56
@@ -0,0 +1,299 @@
1
+ /**
2
+ * 主题(配色)注册表 —— **构建层 + 查询层**(内核唯一实现)
3
+ *
4
+ * 把「虚拟模块 `virtual:theme` 扫出来的三个加载表」构建成一张可查询的注册表,
5
+ * 并提供全部查询函数。原先这两层住在宿主(`oauth21/src/theme/{registry,index}.ts`),
6
+ * 2026-10-17 下沉到内核 —— 它们是「主题配色」的**纯机制**,与业务、与框架都无关,
7
+ * 任何前端(Vue / React / SSR)都能复用。
8
+ *
9
+ * === 为什么能下沉、设备白名单怎么处理 ===
10
+ * 构建层的唯一「宿主事实」是**设备取值集合**(哪些目录名算合法设备)。内核不绑定
11
+ * 某个应用的设备词汇表(见 `constants.ts` 的说明),所以 `buildRegistry` 把它收成
12
+ * **参数** `isKnownDevice(raw): boolean` —— 宿主把自己的 `asThemeDevice` 或设备清单
13
+ * 传进来,内核据此判断「设备段是否合法」。
14
+ *
15
+ * === 目录骨架(唯一形态)===
16
+ * ```
17
+ * themes/<包>/<设备>/<页面>/colors/<配色>/{index.ts, theme.scss}
18
+ * ```
19
+ * 一个主题包 = 一种版式(`view ≡ pkg`),所以配色键是「包/设备/页面/配色」**四段**,
20
+ * 没有独立的「版式」段。目录结构契约见 `constants.ts` 的 `THEME_DIR_LEVELS` /
21
+ * `THEME_COLORS_SEGMENT` / `THEME_COLOR_*`。
22
+ *
23
+ * === 键的唯一性作用域 ===
24
+ * 配色 id 唯一性作用域 = 「同包 × 同设备 × 同页面」。跨设备 / 跨包 / 跨页面可以同名
25
+ * (同名 ≠ 冲突:取一份配色时这三段必然同时给出)。同范围内重名按路径排序先到先得
26
+ * 并告警,不静默覆盖。
27
+ *
28
+ * @author yijiu2025
29
+ * @since 2026-10-17(从 oauth21/src/theme 下沉)
30
+ */
31
+ import { DEFAULT_THEME_COLOR, DEFAULT_THEME_ID, DEFAULT_THEME_PACKAGE, DEFAULT_THEME_PAGE, THEME_COLOR_DEF_FILE, THEME_COLOR_STYLE_FILE, THEME_COLORS_SEGMENT, THEME_ID_RE } from './constants.js';
32
+ import { normalizeTone } from './scheme.js';
33
+ /** 按路径排序后再遍历:让"重名先到先得"与文件系统顺序无关 */
34
+ function sortedEntries(mods) {
35
+ return Object.entries(mods).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
36
+ }
37
+ /** 从包定义入口的键里取出包名;不合法返回 null */
38
+ function packageFromKey(key) {
39
+ const id = /^\.\/([^/]+)\/index\.ts$/.exec(key)?.[1];
40
+ return id && THEME_ID_RE.test(id) ? id : null;
41
+ }
42
+ /**
43
+ * 从配色键里取出归属;不合法返回 null
44
+ *
45
+ * 目录只有**一种**形态(见文件头):
46
+ * `./themes/<包>/<设备>/<页面>/colors/<配色>/index.ts`
47
+ * 设备段由宿主注入的 `isKnownDevice` 判断(内核不认设备词汇表)。
48
+ *
49
+ * 🔴 目录结构(`colors` 固定字面段 + 文件名)由 `constants.ts` 派生,与构建插件
50
+ * `scan()` 共享同一来源,避免"扫得到但解析不出"的静默脱节。
51
+ */
52
+ function colorFromKey(key, suffix) {
53
+ const file = suffix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
54
+ const re = new RegExp(`^\\./([^/]+)/([^/]+)/([^/]+)/${THEME_COLORS_SEGMENT}/([^/]+)/${file}$`);
55
+ const m = re.exec(key);
56
+ if (!m)
57
+ return null;
58
+ const pkg = m[1];
59
+ const device = m[2];
60
+ const page = m[3];
61
+ const colorId = m[4];
62
+ if (!pkg || !device || !page || !colorId)
63
+ return null;
64
+ // 设备段也走字符白名单(设备词汇表已收进库内设备域,这里只挡"目录名不是合法 id";
65
+ // 字符合法但未声明的设备目录会被扫进注册表,但查询按设备 id 取、永不命中,无害)。
66
+ if (!THEME_ID_RE.test(pkg) ||
67
+ !THEME_ID_RE.test(device) ||
68
+ !THEME_ID_RE.test(page) ||
69
+ !THEME_ID_RE.test(colorId))
70
+ return null;
71
+ // 一个主题包 = 一种版式 ⇒ 配色归属只到包段(view ≡ pkg,不再单列一段)
72
+ return { pkg, device, page, colorId };
73
+ }
74
+ /**
75
+ * 把三个加载表构建成一张配色注册表
76
+ *
77
+ * @param modules 虚拟模块 `virtual:theme` 的三个加载表
78
+ * @param onWarn 构建期告警回调(可选;内核默认静默以保持零依赖,宿主传 `console.warn`)
79
+ */
80
+ export function buildThemeRegistry(modules, onWarn = () => { }) {
81
+ const { packages: packageModules, colors: colorModules, styles: colorStyles } = modules;
82
+ const registry = new Map();
83
+ /** 包名 → 包定义的版式声明(配色未自带声明时继承它;跨设备共用一份) */
84
+ const packageViews = new Map();
85
+ // 复合键:`包/设备/页面/配色`(四段,避免嵌套 Map 样板)
86
+ const keyOf = (info) => `${info.pkg}/${info.device}/${info.page}/${info.colorId}`;
87
+ // ① 包定义:只提供 meta 与可选的 views 声明(不再充当默认配色)
88
+ for (const [key, mod] of sortedEntries(packageModules)) {
89
+ const pkg = packageFromKey(key);
90
+ const def = mod?.default;
91
+ // 目录名不合规、或忘了 export default,都跳过而不是抛错:一个写坏的包不该让整页起不来。
92
+ if (!pkg || !def || typeof def !== 'object' || !def.meta)
93
+ continue;
94
+ packageViews.set(pkg, def.views);
95
+ }
96
+ // ② 各包下的配色 —— 每条记录都带 pkg + device + page 三段归属
97
+ for (const [key, mod] of sortedEntries(colorModules)) {
98
+ const parsed = colorFromKey(key, THEME_COLOR_DEF_FILE);
99
+ const def = mod?.default;
100
+ if (!parsed || !def || typeof def !== 'object' || !def.meta)
101
+ continue;
102
+ // 包定义缺失(包本身写坏):该配色无处可依,跳过
103
+ if (!packageViews.has(parsed.pkg))
104
+ continue;
105
+ if (registry.has(keyOf(parsed))) {
106
+ onWarn(`[theme] 配色 id「${parsed.colorId}」在「${parsed.pkg}/${parsed.device}/${parsed.page}」内重复` +
107
+ `(${key} 已忽略):同一个包内的配色 id 必须唯一。`);
108
+ continue;
109
+ }
110
+ // 系别:类型上必填,但运行时再兜一次 —— 系别判错的后果是"开夜间不联动",极难归因。
111
+ const tone = normalizeTone(def.tone);
112
+ if (!tone) {
113
+ onWarn(`[theme] 配色「${parsed.colorId}」未声明合法的 tone(light/dark),` +
114
+ `已按 'light' 兜底:${key}`);
115
+ }
116
+ registry.set(keyOf(parsed), {
117
+ pkg: parsed.pkg,
118
+ device: parsed.device,
119
+ page: parsed.page,
120
+ meta: { ...def.meta, id: parsed.colorId },
121
+ tone: tone ?? 'light',
122
+ tokens: def.tokens,
123
+ // 配色自己声明了就用自己的,否则继承包定义 —— 包根写一次,全包配色共用
124
+ views: def.views ?? packageViews.get(parsed.pkg),
125
+ loadStyle: undefined
126
+ });
127
+ }
128
+ // ③ 附加样式:按与 ② 相同的键挂上去,避免歧义
129
+ for (const [key, loader] of sortedEntries(colorStyles)) {
130
+ const parsed = colorFromKey(key, THEME_COLOR_STYLE_FILE);
131
+ if (!parsed)
132
+ continue;
133
+ const record = registry.get(keyOf(parsed));
134
+ // 有 theme.scss 但没 index.ts 的目录视为残缺,忽略其样式
135
+ if (record)
136
+ record.loadStyle = loader;
137
+ }
138
+ return registry;
139
+ }
140
+ /* ---------------------------------------------------------------------------
141
+ * 查询层
142
+ *
143
+ * 查询函数都读**模块级单例 `themeRegistry`**(主题配色注册表本来就是全局单例)。
144
+ * 由宿主在启动时调 `installThemeRegistry(buildThemeRegistry(...))` 一次性装入,
145
+ * 之后所有查询函数直接可用 —— 与宿主旧实现「`export const registry = buildRegistry()`」
146
+ * 同一形态,`export function` 签名不变,断言与调用点几乎不用改。
147
+ * ------------------------------------------------------------------------- */
148
+ /** 模块级注册表单例(宿主 `installThemeRegistry` 装入;未装入时为空表,查询走空兜底) */
149
+ let themeRegistry = new Map();
150
+ /** 装入一张已构建好的配色注册表(宿主在启动时调一次) */
151
+ export function installThemeRegistry(registry) {
152
+ themeRegistry = registry;
153
+ }
154
+ /**
155
+ * 查找一条配色记录
156
+ *
157
+ * 四段都必须给(包 / 设备 / 页面 / 配色)。回退链:
158
+ * ① 精确匹配当前包 × 当前设备 × 当前页面;
159
+ * ② 兜底扫描同包(切包后 pkg 还没跟上的那一帧);
160
+ * ③ 跨包兜底回默认包(当前包没有这个 id 时)。
161
+ */
162
+ function findRecord(id, pkg, device, page) {
163
+ return themeRegistry.get(`${pkg}/${device}/${page}/${id}`) ??
164
+ [...themeRegistry.values()].find(r => r.meta.id === id && r.device === device && r.page === page && r.pkg === pkg) ??
165
+ (pkg !== DEFAULT_THEME_PACKAGE
166
+ ? [...themeRegistry.values()].find(r => r.meta.id === id && r.device === device && r.page === page && r.pkg === DEFAULT_THEME_PACKAGE)
167
+ : undefined);
168
+ }
169
+ /** 某版式下的兜底配色 id:先显式默认色 `white`,没有才退回 id 字母序第一 */
170
+ function defaultColorIdOf(device, page, pkg) {
171
+ const ids = [...themeRegistry.values()]
172
+ .filter(r => r.device === device && r.page === page && r.pkg === pkg)
173
+ .map(r => r.meta.id)
174
+ .sort();
175
+ if (ids.includes(DEFAULT_THEME_COLOR))
176
+ return DEFAULT_THEME_COLOR;
177
+ return ids[0] ?? null;
178
+ }
179
+ /** 注册表为空 / 该范围无配色时给一个空记录兜底 */
180
+ function emptyRecord(device, page) {
181
+ return {
182
+ pkg: DEFAULT_THEME_PACKAGE,
183
+ device,
184
+ page,
185
+ meta: { id: DEFAULT_THEME_ID, name: '默认' },
186
+ tone: 'light',
187
+ tokens: undefined,
188
+ views: undefined,
189
+ loadStyle: undefined
190
+ };
191
+ }
192
+ /** 当前有哪些配色(可选按设备过滤;不传则返回全部) */
193
+ export function listThemes(device) {
194
+ const records = [...themeRegistry.values()];
195
+ return (device ? records.filter(r => r.device === device) : records).map(r => r.meta);
196
+ }
197
+ /**
198
+ * 列出某个包下的全部配色(按 id 去重、按目录名排序)
199
+ *
200
+ * 🔴 排序只按 id 字母序(`black < blue < cyan < rainbow < white`),不给黑白特权。
201
+ * 🔴 view ≡ pkg,所以本函数只列「当前包」下的配色。
202
+ */
203
+ export function listColorsOf(device, page, pkg) {
204
+ const seen = new Map();
205
+ for (const record of themeRegistry.values()) {
206
+ if (record.device !== device || record.page !== page)
207
+ continue;
208
+ if (record.pkg !== pkg)
209
+ continue;
210
+ if (!seen.has(record.meta.id))
211
+ seen.set(record.meta.id, record.meta);
212
+ }
213
+ return [...seen.values()].sort((a, b) => a.id.localeCompare(b.id));
214
+ }
215
+ /** 某配色在指定归属下的明暗系别;未登记返回 null */
216
+ export function toneOfColor(id, device, page, pkg) {
217
+ return findRecord(id, pkg, device, page)?.tone ?? null;
218
+ }
219
+ /** 某配色的系别(任意登记处);给"槽归位"这类没有明确范围的场景用 */
220
+ export function toneOfAnyScope(id) {
221
+ for (const record of themeRegistry.values()) {
222
+ if (record.meta.id === id)
223
+ return record.tone;
224
+ }
225
+ return null;
226
+ }
227
+ /** 某个版式下、指定系别的全部配色 id(按目录名排序) */
228
+ export function listColorIdsOfTone(device, page, pkg, tone) {
229
+ const seen = new Set();
230
+ for (const record of themeRegistry.values()) {
231
+ if (record.device !== device || record.page !== page || record.pkg !== pkg)
232
+ continue;
233
+ if (record.tone !== tone)
234
+ continue;
235
+ seen.add(record.meta.id);
236
+ }
237
+ return [...seen].sort((a, b) => a.localeCompare(b));
238
+ }
239
+ /** 归一化并校验外部传入的配色 id(URL 参数 / 后端下发) */
240
+ export function resolveThemeId(raw, device, page, pkg) {
241
+ if (typeof raw !== 'string')
242
+ return null;
243
+ const id = raw.trim().toLowerCase();
244
+ if (!id || !THEME_ID_RE.test(id))
245
+ return null;
246
+ const match = (record) => record.meta.id === id &&
247
+ (!device || record.device === device) &&
248
+ (!page || record.page === page) &&
249
+ (!pkg || record.pkg === pkg);
250
+ return [...themeRegistry.values()].some(match) ? id : null;
251
+ }
252
+ /**
253
+ * 取配色记录
254
+ *
255
+ * 回退链:指定 id(四段都匹配)→ 同系别首套 → 该范围兜底 → 空记录。
256
+ *
257
+ * 🔴 内核不设「默认设备」(默认设备是哪台是宿主的词汇表),所以 `device` **必填**
258
+ * (无默认值)。宿主在 re-export 壳里补齐自己的默认设备。
259
+ *
260
+ * 🔴 命名参数对象(2026-10-17 收口):此前 `(id, device, pkg, page)` 与宿主
261
+ * `themeRecordFor(pkg, device, page)` 参数顺序不一致(中间三段对着,但首尾互换),
262
+ * 调用点传错只会有「配色找错范围」这种无从归因的症状。现在改为对象,名字即语义。
263
+ */
264
+ export function getThemeRecord(params) {
265
+ const id = params.id;
266
+ const device = params.device;
267
+ const pkg = params.pkg ?? DEFAULT_THEME_PACKAGE;
268
+ const page = params.page ?? DEFAULT_THEME_PAGE;
269
+ const record = findRecord(id, pkg, device, page);
270
+ if (record)
271
+ return record;
272
+ const tone = toneOfAnyScope(id);
273
+ if (tone) {
274
+ const sameTone = listColorIdsOfTone(device, page, pkg, tone)[0];
275
+ const sameToneHit = sameTone ? findRecord(sameTone, pkg, device, page) : undefined;
276
+ if (sameToneHit)
277
+ return sameToneHit;
278
+ }
279
+ const fallback = defaultColorIdOf(device, page, pkg);
280
+ return (fallback && findRecord(fallback, pkg, device, page)) ||
281
+ emptyRecord(device, page);
282
+ }
283
+ /** 该范围内默认用哪套配色;无配色时返回 null */
284
+ export function getDefaultThemeId(params) {
285
+ return defaultColorIdOf(params.device, params.page ?? DEFAULT_THEME_PAGE, params.pkg ?? DEFAULT_THEME_PACKAGE);
286
+ }
287
+ /** 该配色是否登记过(限定哪几段就要求哪几段匹配) */
288
+ export function isKnownTheme(id, device, page, pkg) {
289
+ return resolveThemeId(id, device, page, pkg) !== null;
290
+ }
291
+ /** 某设备某页面下有哪些包登记了配色(面板要按包分组) */
292
+ export function listColorViews(device, page) {
293
+ return [
294
+ ...new Set([...themeRegistry.values()]
295
+ .filter(r => r.device === device && r.page === page)
296
+ .map(r => r.pkg))
297
+ ].sort();
298
+ }
299
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { ThemeMode } from './mode.js';
2
+ import type { ThemeTokenOverrides } from './tokens.js';
3
+ /** 外部下发的主题配置 */
4
+ export interface RemoteThemeConfig {
5
+ /** 配色 id(须已在主题目录里登记) */
6
+ theme?: string;
7
+ /** 明暗三态 */
8
+ mode?: ThemeMode;
9
+ /** @deprecated 旧字段名,等价于 `theme`;保留以兼容已上线的后端实现 */
10
+ skin?: string;
11
+ /**
12
+ * 一组**扁平**的 token 覆写(`{ '--mauth-primary': '#0e7490' }`)
13
+ *
14
+ * ⚠️ 旧的 `{ light, dark }` 两档结构不再接受:配色自带完整底色,明暗档已于
15
+ * 2026-09-25 取消。若这里还认它,等于让删掉的语义从后门回来。
16
+ */
17
+ tokens?: ThemeTokenOverrides;
18
+ }
19
+ /**
20
+ * 把不可信的响应体归一成 `RemoteThemeConfig`
21
+ *
22
+ * @returns 一个有效字段都没有时返回 `null`(调用方按"没配"处理)
23
+ */
24
+ export declare function normalizeRemoteThemeConfig(data: unknown): RemoteThemeConfig | null;
25
+ //# sourceMappingURL=remote-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-config.d.ts","sourceRoot":"","sources":["../src/remote-config.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEvD,gBAAgB;AAChB,MAAM,WAAW,iBAAiB;IAChC,wBAAwB;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW;IACX,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,iDAAiD;IACjD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,MAAM,CAAC,EAAE,mBAAmB,CAAC;CAC9B;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,OAAO,GAAG,iBAAiB,GAAG,IAAI,CAgBlF"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * 外部(后端 / 父窗口)下发的主题配置 —— **只做形状归一,不碰网络**
3
+ *
4
+ * === 为什么归一化属于内核,取数不属于 ===
5
+ * 「响应里哪些字段算数、`skin` 是不是 `theme` 的旧名字、tokens 为什么必须是扁平结构」
6
+ * 这些是**契约**,与宿主用什么方式拿到 JSON 无关;而超时、重试、带不带 cookie、
7
+ * 失败了要不要报错,是**部署方策略**,每个宿主都不一样。所以内核只收契约这一半,
8
+ * 取数留在宿主(见 `oauth21/src/theme/remote.ts`)。
9
+ *
10
+ * === 为什么显式挑选字段,而不是透传 ===
11
+ * 响应结构由后端决定,多一个字段、少一个类型都可能让下游出错。这里逐字段挑选,
12
+ * 顺带挡住数组 / 字符串之类的意外结构。
13
+ *
14
+ * ⚠️ 这里**不做 id 合法性校验**(是否登记过由渲染方用 `resolveThemeId` 判断):
15
+ * 校验要读注册表,放在一处才不会出现两套标准。
16
+ *
17
+ * @author yijiu2025
18
+ * @since 2026-10-17(从 oauth21 的 `theme/remote.ts` 拆出)
19
+ */
20
+ import { normalizeMode } from './mode.js';
21
+ /**
22
+ * 把不可信的响应体归一成 `RemoteThemeConfig`
23
+ *
24
+ * @returns 一个有效字段都没有时返回 `null`(调用方按"没配"处理)
25
+ */
26
+ export function normalizeRemoteThemeConfig(data) {
27
+ if (!data || typeof data !== 'object' || Array.isArray(data))
28
+ return null;
29
+ const raw = data;
30
+ const out = {};
31
+ if (typeof raw.theme === 'string')
32
+ out.theme = raw.theme;
33
+ else if (typeof raw.skin === 'string')
34
+ out.skin = raw.skin;
35
+ const mode = normalizeMode(raw.mode);
36
+ if (mode)
37
+ out.mode = mode;
38
+ if (raw.tokens && typeof raw.tokens === 'object' && !Array.isArray(raw.tokens)) {
39
+ out.tokens = raw.tokens;
40
+ }
41
+ return out.theme || out.skin || out.mode || out.tokens ? out : null;
42
+ }
43
+ //# sourceMappingURL=remote-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-config.js","sourceRoot":"","sources":["../src/remote-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAqB1C;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CAAC,IAAa;IACtD,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1E,MAAM,GAAG,GAAG,IAA+B,CAAC;IAC5C,MAAM,GAAG,GAAsB,EAAE,CAAC;IAElC,IAAI,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ;QAAE,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC;SACpD,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ;QAAE,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;IAE3D,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,IAAI;QAAE,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;IAE1B,IAAI,GAAG,CAAC,MAAM,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAC/E,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAA6B,CAAC;IACjD,CAAC;IAED,OAAO,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;AACtE,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * 配色系别 + 明暗意图(`scheme`)—— 「明暗 ≡ 色系」的完整定义
3
+ *
4
+ * 从 `tone.ts` + `mode.ts` + `remote-config.ts` 合并而来(2026-10-17 内核化简)。
5
+ * 这三者本来就是一体的:tone 是配色自带的底色深浅,mode 是用户的系别意图,
6
+ * 它们通过联动规则(切暗→黑系、切明→白系)互相关联,分三个文件纯属过早拆分。
7
+ *
8
+ * === 联动规则 ===
9
+ * 🔴 明暗 ≡ 色系(用户 2026-09-25 定):「明暗切换就是切换白色和黑色这个色系」
10
+ * mode = 系别意图(light→白系 / dark→黑系 / system→跟随系统偏好)
11
+ * tone = 配色自带的底色(每套配色一个)—— 不是 CSS,只用于联动
12
+ * 点色卡同步 mode(点黑卡→dark、点白卡→light)
13
+ * 切 mode 联动配色(dark→黑系标配、light→白系标配,两侧各自记住上次选择)
14
+ *
15
+ * @author yijiu2025
16
+ */
17
+ import type { ThemeTokenOverrides } from './tokens.js';
18
+ export type ThemeTone = 'light' | 'dark';
19
+ export declare const THEME_TONES: readonly ThemeTone[];
20
+ export declare const TONE_LABELS: Record<ThemeTone, string>;
21
+ export declare function isThemeTone(value: unknown): value is ThemeTone;
22
+ export declare function normalizeTone(raw: unknown): ThemeTone | null;
23
+ export type ThemeMode = 'system' | 'light' | 'dark';
24
+ export declare const MODE_CYCLE: readonly ThemeMode[];
25
+ export declare const MODE_LABELS: Record<ThemeMode, string>;
26
+ export declare function isThemeMode(value: unknown): value is ThemeMode;
27
+ export declare function normalizeMode(raw: unknown): ThemeMode | null;
28
+ export interface RemoteThemeConfig {
29
+ theme?: string;
30
+ mode?: ThemeMode;
31
+ /** @deprecated 旧字段名,等价于 theme */
32
+ skin?: string;
33
+ tokens?: ThemeTokenOverrides;
34
+ }
35
+ export declare function normalizeRemoteThemeConfig(data: unknown): RemoteThemeConfig | null;
36
+ //# sourceMappingURL=scheme.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheme.d.ts","sourceRoot":"","sources":["../src/scheme.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAMvD,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC;AAEzC,eAAO,MAAM,WAAW,EAAE,SAAS,SAAS,EAAsB,CAAC;AAEnE,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAGjD,CAAC;AAEF,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS,CAE9D;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,SAAS,GAAG,IAAI,CAI5D;AAMD,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;AAEpD,eAAO,MAAM,UAAU,EAAE,SAAS,SAAS,EAAgC,CAAC;AAE5E,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAIjD,CAAC;AAEF,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS,CAE9D;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,SAAS,GAAG,IAAI,CAI5D;AAMD,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,iCAAiC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,mBAAmB,CAAC;CAC9B;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,OAAO,GAAG,iBAAiB,GAAG,IAAI,CAgBlF"}
package/dist/scheme.js ADDED
@@ -0,0 +1,47 @@
1
+ export const THEME_TONES = ['light', 'dark'];
2
+ export const TONE_LABELS = {
3
+ light: '白系',
4
+ dark: '黑系'
5
+ };
6
+ export function isThemeTone(value) {
7
+ return value === 'light' || value === 'dark';
8
+ }
9
+ export function normalizeTone(raw) {
10
+ if (typeof raw !== 'string')
11
+ return null;
12
+ const v = raw.trim().toLowerCase();
13
+ return isThemeTone(v) ? v : null;
14
+ }
15
+ export const MODE_CYCLE = ['system', 'light', 'dark'];
16
+ export const MODE_LABELS = {
17
+ system: '跟随系统',
18
+ light: '明',
19
+ dark: '暗'
20
+ };
21
+ export function isThemeMode(value) {
22
+ return value === 'system' || value === 'light' || value === 'dark';
23
+ }
24
+ export function normalizeMode(raw) {
25
+ if (typeof raw !== 'string')
26
+ return null;
27
+ const v = raw.trim().toLowerCase();
28
+ return isThemeMode(v) ? v : null;
29
+ }
30
+ export function normalizeRemoteThemeConfig(data) {
31
+ if (!data || typeof data !== 'object' || Array.isArray(data))
32
+ return null;
33
+ const raw = data;
34
+ const out = {};
35
+ if (typeof raw.theme === 'string')
36
+ out.theme = raw.theme;
37
+ else if (typeof raw.skin === 'string')
38
+ out.skin = raw.skin;
39
+ const mode = normalizeMode(raw.mode);
40
+ if (mode)
41
+ out.mode = mode;
42
+ if (raw.tokens && typeof raw.tokens === 'object' && !Array.isArray(raw.tokens)) {
43
+ out.tokens = raw.tokens;
44
+ }
45
+ return out.theme || out.skin || out.mode || out.tokens ? out : null;
46
+ }
47
+ //# sourceMappingURL=scheme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheme.js","sourceRoot":"","sources":["../src/scheme.ts"],"names":[],"mappings":"AAwBA,MAAM,CAAC,MAAM,WAAW,GAAyB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;AAEnE,MAAM,CAAC,MAAM,WAAW,GAA8B;IACpD,KAAK,EAAE,IAAI;IACX,IAAI,EAAE,IAAI;CACX,CAAC;AAEF,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,OAAO,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,MAAM,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,GAAY;IACxC,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACzC,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACnC,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACnC,CAAC;AAQD,MAAM,CAAC,MAAM,UAAU,GAAyB,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AAE5E,MAAM,CAAC,MAAM,WAAW,GAA8B;IACpD,MAAM,EAAE,MAAM;IACd,KAAK,EAAE,GAAG;IACV,IAAI,EAAE,GAAG;CACV,CAAC;AAEF,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,MAAM,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,GAAY;IACxC,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACzC,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACnC,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACnC,CAAC;AAcD,MAAM,UAAU,0BAA0B,CAAC,IAAa;IACtD,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1E,MAAM,GAAG,GAAG,IAA+B,CAAC;IAC5C,MAAM,GAAG,GAAsB,EAAE,CAAC;IAElC,IAAI,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ;QAAE,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC;SACpD,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ;QAAE,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;IAE3D,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,IAAI;QAAE,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;IAE1B,IAAI,GAAG,CAAC,MAAM,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAC/E,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAA6B,CAAC;IACjD,CAAC;IAED,OAAO,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;AACtE,CAAC"}
package/dist/types.d.ts CHANGED
@@ -58,8 +58,9 @@
58
58
  *
59
59
  * @author yijiu2025
60
60
  */
61
- import type { ThemeTone } from './tone.js';
61
+ import type { ThemeTone } from './scheme.js';
62
62
  import type { ThemeTokenOverrides } from './tokens.js';
63
+ import type { ResolvedThemeManifest } from './manifest.js';
63
64
  /**
64
65
  * 设备维度取值:**由宿主的设备清单决定**(内核不绑定某个应用的设备集合)
65
66
  *
@@ -222,5 +223,20 @@ export interface MauthThemeRecord {
222
223
  /** 该主题附加样式的惰性加载器;无 theme.scss 时为 undefined */
223
224
  loadStyle: (() => Promise<string>) | undefined;
224
225
  }
226
+ /** 构建插件 `themeAssets()` 扫出的主题资产 + 清单(虚拟模块 `virtual:theme` 的形态) */
227
+ export interface VirtualThemeModule {
228
+ /** 包定义:`./<包>/index.ts` */
229
+ packages: Record<string, {
230
+ default: MauthThemePackage;
231
+ }>;
232
+ /** 配色元信息 + tokens:`./<包>/<设备>/<页面>/colors/<配色>/index.ts` */
233
+ colors: Record<string, {
234
+ default: MauthThemeColor;
235
+ }>;
236
+ /** 附加样式(惰性,取编译后的 CSS 字符串):`.../theme.scss` */
237
+ styles: Record<string, () => Promise<string>>;
238
+ /** 主题域清单;目录里没有 `theme.config.ts` 时为 null */
239
+ manifest: ResolvedThemeManifest | null;
240
+ }
225
241
  export {};
226
242
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEvD;;;;;;;;;;GAUG;AACH,KAAK,WAAW,GAAG,MAAM,CAAC;AAE1B;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;OAKG;IACH,EAAE,EAAE,MAAM,CAAC;IACX,YAAY;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,OAAO,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9C,aAAa;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,cAAc,CAAC;IACrB;;;;;;;;;;;;;;OAcG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B;;;;;;;;;;;;;;;;;;;OAmBG;IACH,QAAQ,CAAC,EAAE;QACT,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QACnB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QACjB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;KAC3B,CAAC;CACH;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,cAAc,CAAC;IACrB;;;;;;;;;;;OAWG;IACH,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED,sBAAsB;AACtB,MAAM,WAAW,gBAAgB;IAC/B;;;;;OAKG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;OAKG;IACH,MAAM,EAAE,WAAW,CAAC;IACpB;;;;;;OAMG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,cAAc,CAAC;IACrB;;;;;OAKG;IACH,IAAI,EAAE,SAAS,CAAC;IAChB,4CAA4C;IAC5C,MAAM,EAAE,mBAAmB,GAAG,SAAS,CAAC;IACxC,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IAC1C,8CAA8C;IAC9C,SAAS,EAAE,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,SAAS,CAAC;CAChD"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAE3D;;;;;;;;;;GAUG;AACH,KAAK,WAAW,GAAG,MAAM,CAAC;AAE1B;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;OAKG;IACH,EAAE,EAAE,MAAM,CAAC;IACX,YAAY;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4CAA4C;IAC5C,OAAO,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9C,aAAa;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,cAAc,CAAC;IACrB;;;;;;;;;;;;;;OAcG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B;;;;;;;;;;;;;;;;;;;OAmBG;IACH,QAAQ,CAAC,EAAE;QACT,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QACnB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QACjB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;KAC3B,CAAC;CACH;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,cAAc,CAAC;IACrB;;;;;;;;;;;OAWG;IACH,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED,sBAAsB;AACtB,MAAM,WAAW,gBAAgB;IAC/B;;;;;OAKG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;OAKG;IACH,MAAM,EAAE,WAAW,CAAC;IACpB;;;;;;OAMG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,cAAc,CAAC;IACrB;;;;;OAKG;IACH,IAAI,EAAE,SAAS,CAAC;IAChB,4CAA4C;IAC5C,MAAM,EAAE,mBAAmB,GAAG,SAAS,CAAC;IACxC,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IAC1C,8CAA8C;IAC9C,SAAS,EAAE,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,SAAS,CAAC;CAChD;AAOD,kEAAkE;AAClE,MAAM,WAAW,kBAAkB;IACjC,2BAA2B;IAC3B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,iBAAiB,CAAA;KAAE,CAAC,CAAC;IACzD,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,eAAe,CAAA;KAAE,CAAC,CAAC;IACrD,8CAA8C;IAC9C,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9C,4CAA4C;IAC5C,QAAQ,EAAE,qBAAqB,GAAG,IAAI,CAAC;CACxC"}
@@ -0,0 +1,45 @@
1
+ import type { DeviceDef, DeviceMatchContext } from './device.js';
2
+ export type { DeviceDef, DeviceMatchContext } from './device.js';
3
+ /**
4
+ * 一套设备注册表 —— 宿主「设备这件事」的**全部**能力
5
+ *
6
+ * 所有函数都已绑定传入的清单,调用方不再需要每次传 `defs`。
7
+ * `T` 是容器加载器的类型(纯逻辑侧透传、不关心它到底是什么)。
8
+ */
9
+ export interface DeviceRegistry<T = unknown> {
10
+ /** 设备 id 清单(顺序即优先级)—— 词汇表的**唯一来源** */
11
+ readonly defs: readonly DeviceDef<T>[];
12
+ /** 设备 id 数组(顺序即优先级) */
13
+ readonly devices: readonly string[];
14
+ /** 兜底设备 id */
15
+ readonly defaultDevice: string;
16
+ /** 设备 id → 中文名(调试面板 / 日志用;未知 id 原样返回) */
17
+ deviceLabel(id: string): string;
18
+ /** 归一化外部传入的设备名;不认识的取值返回 null(**不是**落默认设备) */
19
+ asKnownDevice(raw: unknown): string | null;
20
+ /** 从外部输入判定「本次访问属于哪台设备」;返回值永远非空(落 defaultDevice) */
21
+ pickDeviceId(ctx: DeviceMatchContext): string;
22
+ /** 取某设备的容器表(`containers` 是泛型 T,原样透传) */
23
+ containersOf(id: string): ReadonlyMap<string, T>;
24
+ /** 从当前访问信息构造判定上下文 */
25
+ deviceContext(query: Record<string, unknown> | undefined, path: string, viewportIsMobile: boolean): DeviceMatchContext;
26
+ }
27
+ export interface DeviceRegistryOptions {
28
+ /** 兜底设备 id(判定全 miss 时返回);缺省取清单最后一条的 id */
29
+ defaultDevice?: string;
30
+ }
31
+ /**
32
+ * 从一份设备清单装配出整套设备机制
33
+ *
34
+ * @param defs 设备清单(顺序即优先级,末条建议无条件命中作兜底)
35
+ * @param options 兜底设备等可选项
36
+ *
37
+ * @example
38
+ * ```ts
39
+ * const registry = createDeviceRegistry(THEME_DEVICE_DEFS, { defaultDevice: 'mobile' });
40
+ * registry.pickDeviceId(registry.deviceContext(query, path, isMobile));
41
+ * registry.asKnownDevice('mini'); // → 'mini' | null
42
+ * ```
43
+ */
44
+ export declare function createDeviceRegistry<T>(defs: readonly DeviceDef<T>[], options?: DeviceRegistryOptions): DeviceRegistry<T>;
45
+ //# sourceMappingURL=device-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device-registry.d.ts","sourceRoot":"","sources":["../../src/views/device-registry.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGjE,YAAY,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjE;;;;;GAKG;AACH,MAAM,WAAW,cAAc,CAAC,CAAC,GAAG,OAAO;IACzC,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,SAAS,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;IACvC,uBAAuB;IACvB,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,cAAc;IACd,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,yCAAyC;IACzC,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,6CAA6C;IAC7C,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAAC;IAC3C,mDAAmD;IACnD,YAAY,CAAC,GAAG,EAAE,kBAAkB,GAAG,MAAM,CAAC;IAC9C,wCAAwC;IACxC,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACjD,qBAAqB;IACrB,aAAa,CACX,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC1C,IAAI,EAAE,MAAM,EACZ,gBAAgB,EAAE,OAAO,GACxB,kBAAkB,CAAC;CACvB;AAED,MAAM,WAAW,qBAAqB;IACpC,0CAA0C;IAC1C,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EACpC,IAAI,EAAE,SAAS,SAAS,CAAC,CAAC,CAAC,EAAE,EAC7B,OAAO,GAAE,qBAA0B,GAClC,cAAc,CAAC,CAAC,CAAC,CAcnB"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * 设备注册表工厂 —— 「一份设备清单 → 全套判定机制」的**唯一实现**(内核版)
3
+ *
4
+ * === 为什么要有它 ===
5
+ * 宿主(oauth21 等)曾经在 `views/devices.ts` 里既要写清单(`DeviceDef[]`),又要逐个
6
+ * re-export 内核的判定函数(`deviceLabel` / `pickDeviceId` / `containersOf` / …),
7
+ * 每个新项目都要把这一串转发函数抄一遍 —— 那是纯样板。本工厂把「清单 → 机制」收成
8
+ * 一步:宿主只写一份 `DeviceDef[]`,调一次 `createDeviceRegistry(defs)`,全套判定函数
9
+ * 就位,且都绑定了这份清单、参数顺序按宿主直觉收敛(不再要求每个调用点传 defs)。
10
+ *
11
+ * === 与 `views/device.ts` 的关系 ===
12
+ * `device.ts` 是**无状态纯函数**(`pickDeviceId(defs, ctx, …)` 每次传清单),供内核
13
+ * 内部与「清单还没装进注册表」的场景用;本文件是它的**装配层**——把清单闭包进去,
14
+ * 产出「已绑定清单」的面向宿主的 API。组件半边(`containerOf` / `resolveContainerOf`)
15
+ * 住在 `skinsuite/vue` 的 `device-containers.ts`,本文件的工厂也收那一半。
16
+ *
17
+ * === 为什么设备是宽 `string` + 白名单参数 ===
18
+ * 内核不认识宿主的设备词汇表(哪些目录名算合法设备是宿主的部署事实),所以:
19
+ * · 判定函数对外都收 `string`、返回 `string`;
20
+ * · 「合法取值集合」由清单派生(`DeviceRegistry.devices`);
21
+ * · 「归一化外部输入」用清单派生的白名单(`asKnownDevice`)——不再要求宿主另传。
22
+ *
23
+ * @author yijiu2025
24
+ * @since 2026-10-17(从 oauth21/src/theme/views/devices.ts 的转发函数收编)
25
+ */
26
+ import { containersOf } from './device.js';
27
+ import { asDeviceId, deviceContext, deviceLabel, pickDeviceId } from './device.js';
28
+ /**
29
+ * 从一份设备清单装配出整套设备机制
30
+ *
31
+ * @param defs 设备清单(顺序即优先级,末条建议无条件命中作兜底)
32
+ * @param options 兜底设备等可选项
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * const registry = createDeviceRegistry(THEME_DEVICE_DEFS, { defaultDevice: 'mobile' });
37
+ * registry.pickDeviceId(registry.deviceContext(query, path, isMobile));
38
+ * registry.asKnownDevice('mini'); // → 'mini' | null
39
+ * ```
40
+ */
41
+ export function createDeviceRegistry(defs, options = {}) {
42
+ const defaultDevice = options.defaultDevice ?? defs[defs.length - 1]?.id ?? '';
43
+ const devices = defs.map(def => def.id);
44
+ return {
45
+ defs,
46
+ devices,
47
+ defaultDevice,
48
+ deviceLabel: (id) => deviceLabel(defs, id),
49
+ asKnownDevice: (raw) => asDeviceId(devices, raw),
50
+ pickDeviceId: (ctx) => pickDeviceId(defs, ctx, defaultDevice),
51
+ containersOf: (id) => containersOf(defs, id),
52
+ deviceContext
53
+ };
54
+ }
55
+ //# sourceMappingURL=device-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device-registry.js","sourceRoot":"","sources":["../../src/views/device-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAsCnF;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,oBAAoB,CAClC,IAA6B,EAC7B,UAAiC,EAAE;IAEnC,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC;IAC/E,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAExC,OAAO;QACL,IAAI;QACJ,OAAO;QACP,aAAa;QACb,WAAW,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,EAAE,CAAC;QAClD,aAAa,EAAE,CAAC,GAAY,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC;QACzD,YAAY,EAAE,CAAC,GAAuB,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,GAAG,EAAE,aAAa,CAAC;QACjF,YAAY,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,CAAC;QACpD,aAAa;KACd,CAAC;AACJ,CAAC"}