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
package/README.md CHANGED
@@ -27,6 +27,24 @@ npm install skinsuite
27
27
 
28
28
  ---
29
29
 
30
+ ## 接入指南(新前端从零接入)
31
+
32
+ > 要在一个**新的 Vue + Vite 前端**里用上多主题 / 多版式 / 多设备 / 调试面板?从头一步步走:
33
+
34
+ - **完整接入指南**:[`docs/onboarding.md`](./docs/onboarding.md) —— 从安装、目录约定、写主题资产、
35
+ 写声明文件、`vite.config.ts`、`bootstrap.ts`、store、`App.vue`、路由与容器,到验证清单与常见坑,
36
+ 每一步给真实 API + 完整代码。
37
+
38
+ 先看三个入口的分工,再看下面的「30 秒上手」体会单个 API 的用法:
39
+
40
+ | 入口 | 作用 |
41
+ | --- | --- |
42
+ | `skinsuite` | 纯内核(本 README 的 API 全在这里) |
43
+ | `skinsuite/vue` | Vue 绑定:`<ThemeView>` / `<ThemeRoot>` / `createThemeState` / 设备域 |
44
+ | `skinsuite/vite` | 构建期插件:`themeAssets` 扫主题目录生成 `virtual:theme` |
45
+
46
+ ---
47
+
30
48
  ## 30 秒上手
31
49
 
32
50
  ### 1. 安全地接受外部下发的 CSS 变量
@@ -189,7 +207,9 @@ readDeviceParam(query, name, device);
189
207
  | 设备取值集合 | `mobile` / `standard` / `mini` 是**某个 app** 的词汇表,不是机制的一部分 |
190
208
  | 业务资产(`themes/**`) | 版式实现与配色数据是"你的页面长什么样",随应用走 |
191
209
 
192
- 配套的宿主 Vue 适配层(store + DOM 注入 + glob 适配器)计划单发一个包 `skinsuite-vue`,见 [Roadmap](#roadmap)。
210
+ 配套的宿主 Vue 适配层(`<ThemeView>` / `<ThemeRoot>` / 状态机 / 设备域)与 Vite 构建插件
211
+ 已经作为**同包子路径** `skinsuite/vue` 与 `skinsuite/vite` 一起发布 —— 见
212
+ [接入指南](./docs/onboarding.md),不再单发一个包。
193
213
 
194
214
  ### 在自有项目里接入(Vite 示例)
195
215
 
@@ -246,11 +266,13 @@ CSS 自定义属性是能**直接改变渲染**的输入。未经校验就 `setP
246
266
 
247
267
  | 期 | 内容 | 状态 |
248
268
  | --- | --- | --- |
249
- | Stage 1 | 常量与白名单、`types`、`tone`、`mode`、设备维度参数、token 形态与校验 | ✅ 已发布(本版本) |
250
- | Stage 2 | 版式注册表工厂 + 版式选择器(四档链:`?view.<设备>` → `?view` → 包声明 → 环境变量 → 当前包) | 计划中 |
251
- | Stage 3 | 配色注册表与回退链(`createColorRegistry` 等) | 计划中 |
252
- | Stage 4 | store 的纯逻辑(URL 意图归一、明暗联动规则);DOM 与存储拆成适配器 | 计划中 |
253
- | Stage 5 | 配套 Vue 适配层 `skinsuite-vue` | 计划中 |
269
+ | Stage 1 | 常量与白名单、`types`、`tone`、`mode`、设备维度参数、token 形态与校验 | ✅ 已发布 |
270
+ | Stage 2 | 版式注册表工厂 + 版式选择器(`defineThemePage` / `createViewPicker`,`?th[_<设备缩写>]` → 包声明 → 当前包) | ✅ 已发布(`skinsuite/vue`) |
271
+ | Stage 3 | 配色注册表与回退链(`createColorRegistry` 等) | ✅ 已发布 |
272
+ | Stage 4 | store 的纯逻辑(`createThemeState`:URL 意图归一、明暗联动、token 注入) | ✅ 已发布(`skinsuite/vue`) |
273
+ | Stage 5 | Vue 适配层 + Vite 构建插件(`skinsuite/vue` / `skinsuite/vite`) | ✅ 已发布 |
274
+
275
+ > 阶段划分已随机制下沉而完成,上表仅作历史对照。接入方式以 [docs/onboarding.md](./docs/onboarding.md) 为准。
254
276
 
255
277
  在 1.0 之前(0.x),次要版本可能包含重命名 —— 公开出口是显式具名导出,改名即 semver-minor。
256
278
 
@@ -266,6 +288,22 @@ npm test # 先构建,再用 node:test 跑 dist 产物
266
288
 
267
289
  ⚠️ `src/` 里的相对 import **必须带 `.js` 后缀**:包以 `moduleResolution: NodeNext` 编译,TS 会把 `.js` 映射回 `.ts` 源码,而产物里的相对说明符是原样保留的 —— 不带后缀浏览器与 Node 的原生 ESM 都解析不到。
268
290
 
291
+ ### 关于 exports:类型统一指向 `src/`,运行时才分 `dist/`
292
+
293
+ ```jsonc
294
+ ".": { "types": "./src/index.ts", "import": "./dist/index.js" },
295
+ "./vue": { "types": "./src/vue/index.ts", "import": "./src/vue/index.ts" },
296
+ "./vite": { "types": "./src/vite/index.ts", "import": "./src/vite/index.ts" }
297
+ ```
298
+
299
+ `./vue` / `./vite` 含 `.vue` 与构建期代码,**只能以源码形态发布**(`tsc` 编不出 SFC)。
300
+ 于是根入口的类型也必须指向 `src/index.ts`:若它指向 `dist/index.d.ts`,同一个类型在
301
+ 一个程序里会有**两个身份**(dist 的 d.ts 与 vue 层引用的 src),composite 项目做声明
302
+ 产出时直接红 —— `TS2883: The inferred type of 'X' cannot be named without a reference
303
+ to '...' from '../../node_modules/skinsuite/src/types'`。
304
+
305
+ `dist/` 仍然保留(`.js` + `.d.ts` + sourcemap),运行时走它,纯 JS 消费者不受影响。
306
+
269
307
  ---
270
308
 
271
309
  ## License
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * ⚠️ 本文件**零依赖**(不 import 任何东西),也不碰 DOM / 构建器 / 路由:
10
10
  * 这是 `skinsuite` 能被任意前端复用的前提(见
11
- * docs/frontend/theme-package-extraction.md 的「〇、一条原则」)。
11
+ * docs/standards/theme-package-extraction.md 的「〇、一条原则」)。
12
12
  *
13
13
  * @author yijiu2025
14
14
  */
@@ -81,13 +81,50 @@ export declare const DEFAULT_THEME_COLOR = "white";
81
81
  */
82
82
  export declare const DEFAULT_THEME_DARK_COLOR = "black";
83
83
  /**
84
- * 基础版式的**逻辑** id(与版式注册表工厂的默认 `baseId` 是同一约定值)
84
+ * 主题包目录的**结构契约** —— 「目录长什么样」的唯一来源
85
85
  *
86
- * ⚠️ 它**不是**配色注册表里的 view 值:配色记录里的 view 段恒等于包名
87
- * (一个主题包 = 一种版式),`'base'` 永远不会出现在配色键里。
88
- * 外部 `?view=base` 由版式选择器归一化到当前包名,到不了配色注册表。
86
+ * === 为什么要有它(2026-10-17)===
87
+ * 目录骨架原先是一份**隐式约定**,散在三个形态各异的实现里:
88
+ * • 构建插件的 `scan()`:嵌套 for 循环 + 写死的 `'colors'` 字符串
89
+ * • 注册表的 `colorFromKey()`:正则 `\./([^/]+)/([^/]+)/([^/]+)/colors/([^/]+)/…`
90
+ * • `types.ts` 的注释 ASCII 树
91
+ * 三处各自维护对同一件事的认知。改目录结构(比如配色下再分 `tone` 层、或
92
+ * `colors` 改名)时,只要漏改一处,就出现**静默扫不到主题**这种无从归因的脱节
93
+ * (扫描是"读不到就跳过",不报错)——与「设备词汇表散落四处」是同一类坑。
89
94
  *
90
- * 保留它只是为了版式工厂的默认值;新增代码不要拿它当配色查找的 view 参数。
95
+ * 现在收敛成下面这一组常量:**段序列 + 固定字面段 + 叶子文件名**。构建插件扫描
96
+ * 目录、注册表解析键,都从这里派生,不再各自手写。
97
+ *
98
+ * === 它**只**承诺「结构形态」,不承诺「取值集合」===
99
+ * 段里 `pkg` / `device` / `page` / `colorId` 的**合法取值**(有哪些设备、有哪些包)
100
+ * 是宿主的词汇表,内核不绑定(设备集合在宿主 `devices.ts`,包集合由文件系统决定)。
101
+ * 这里只承诺"有这么几段、顺序如此、其中 `colors` 是固定字面段"。
102
+ *
103
+ * === 目录形态(唯一形态,2026-09-26 收窄后)===
104
+ * ```
105
+ * themes/<包>/<设备>/<页面>/colors/<配色>/{index.ts, theme.scss}
106
+ * ```
107
+ * 配色与版式**同级**,「版式」不是一层目录(变体版式请新建主题包)。
108
+ * 曾支持的 6 段变体形态(`<页面>/<版式>/colors/`)已删除。
109
+ */
110
+ /** 配色键里的**固定字面段**:`colors` 是目录结构的一部分,不是取值段 */
111
+ export declare const THEME_COLORS_SEGMENT = "colors";
112
+ /** 配色定义文件(`<配色>/index.ts`)与附加样式(`<配色>/theme.scss`)的文件名 */
113
+ export declare const THEME_COLOR_DEF_FILE = "index.ts";
114
+ export declare const THEME_COLOR_STYLE_FILE = "theme.scss";
115
+ /** 主题包定义入口(`<包>/index.ts`)的文件名 */
116
+ export declare const THEME_PACKAGE_DEF_FILE = "index.ts";
117
+ /**
118
+ * 主题包目录的**取值目录段**(`themes/` 之下、从包到配色叶子的真实目录层)
119
+ *
120
+ * 与 `THEME_COLORS_SEGMENT`(固定字面段)、`THEME_COLOR_DEF_FILE` /
121
+ * `THEME_COLOR_STYLE_FILE`(叶子文件名)共同构成「目录长什么样」的完整契约。
122
+ * 顺序即目录深度:`['pkg','device','page','colorId']` 对应
123
+ * `themes/<pkg>/<device>/<page>/colors/<colorId>/`。
124
+ *
125
+ * 构建插件的 `scan()` 用它做**递归下降**(每段一层目录),注册表的 `colorFromKey()`
126
+ * 用它拼正则(键 = `./` + 这些段,`page` 与 `colorId` 之间插入 `THEME_COLORS_SEGMENT`)。
127
+ * 改目录结构(配色下再分层、`colors` 改名)只动这一处。
91
128
  */
92
- export declare const BASE_VIEW_ID = "base";
129
+ export declare const THEME_DIR_LEVELS: readonly ["pkg", "device", "page", "colorId"];
93
130
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,QAAiB,CAAC;AAE1C;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,YAAY,CAAC;AAE1C;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,YAAY,CAAC;AAE/C;;;;;;;;;;;;;;;GAeG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,UAAU,CAAC;AAE1C;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,mBAAmB,UAAU,CAAC;AAE3C;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,UAAU,CAAC;AAEhD;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,SAAS,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW,QAAiB,CAAC;AAE1C;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,YAAY,CAAC;AAE1C;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,YAAY,CAAC;AAE/C;;;;;;;;;;;;;;;GAeG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,UAAU,CAAC;AAE1C;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,mBAAmB,UAAU,CAAC;AAE3C;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,UAAU,CAAC;AAEhD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,8CAA8C;AAC9C,eAAO,MAAM,oBAAoB,WAAW,CAAC;AAE7C,0DAA0D;AAC1D,eAAO,MAAM,oBAAoB,aAAa,CAAC;AAC/C,eAAO,MAAM,sBAAsB,eAAe,CAAC;AAEnD,kCAAkC;AAClC,eAAO,MAAM,sBAAsB,aAAa,CAAC;AAEjD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,gBAAgB,+CAAgD,CAAC"}
package/dist/constants.js CHANGED
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * ⚠️ 本文件**零依赖**(不 import 任何东西),也不碰 DOM / 构建器 / 路由:
10
10
  * 这是 `skinsuite` 能被任意前端复用的前提(见
11
- * docs/frontend/theme-package-extraction.md 的「〇、一条原则」)。
11
+ * docs/standards/theme-package-extraction.md 的「〇、一条原则」)。
12
12
  *
13
13
  * @author yijiu2025
14
14
  */
@@ -81,13 +81,50 @@ export const DEFAULT_THEME_COLOR = 'white';
81
81
  */
82
82
  export const DEFAULT_THEME_DARK_COLOR = 'black';
83
83
  /**
84
- * 基础版式的**逻辑** id(与版式注册表工厂的默认 `baseId` 是同一约定值)
84
+ * 主题包目录的**结构契约** —— 「目录长什么样」的唯一来源
85
85
  *
86
- * ⚠️ 它**不是**配色注册表里的 view 值:配色记录里的 view 段恒等于包名
87
- * (一个主题包 = 一种版式),`'base'` 永远不会出现在配色键里。
88
- * 外部 `?view=base` 由版式选择器归一化到当前包名,到不了配色注册表。
86
+ * === 为什么要有它(2026-10-17)===
87
+ * 目录骨架原先是一份**隐式约定**,散在三个形态各异的实现里:
88
+ * • 构建插件的 `scan()`:嵌套 for 循环 + 写死的 `'colors'` 字符串
89
+ * • 注册表的 `colorFromKey()`:正则 `\./([^/]+)/([^/]+)/([^/]+)/colors/([^/]+)/…`
90
+ * • `types.ts` 的注释 ASCII 树
91
+ * 三处各自维护对同一件事的认知。改目录结构(比如配色下再分 `tone` 层、或
92
+ * `colors` 改名)时,只要漏改一处,就出现**静默扫不到主题**这种无从归因的脱节
93
+ * (扫描是"读不到就跳过",不报错)——与「设备词汇表散落四处」是同一类坑。
89
94
  *
90
- * 保留它只是为了版式工厂的默认值;新增代码不要拿它当配色查找的 view 参数。
95
+ * 现在收敛成下面这一组常量:**段序列 + 固定字面段 + 叶子文件名**。构建插件扫描
96
+ * 目录、注册表解析键,都从这里派生,不再各自手写。
97
+ *
98
+ * === 它**只**承诺「结构形态」,不承诺「取值集合」===
99
+ * 段里 `pkg` / `device` / `page` / `colorId` 的**合法取值**(有哪些设备、有哪些包)
100
+ * 是宿主的词汇表,内核不绑定(设备集合在宿主 `devices.ts`,包集合由文件系统决定)。
101
+ * 这里只承诺"有这么几段、顺序如此、其中 `colors` 是固定字面段"。
102
+ *
103
+ * === 目录形态(唯一形态,2026-09-26 收窄后)===
104
+ * ```
105
+ * themes/<包>/<设备>/<页面>/colors/<配色>/{index.ts, theme.scss}
106
+ * ```
107
+ * 配色与版式**同级**,「版式」不是一层目录(变体版式请新建主题包)。
108
+ * 曾支持的 6 段变体形态(`<页面>/<版式>/colors/`)已删除。
109
+ */
110
+ /** 配色键里的**固定字面段**:`colors` 是目录结构的一部分,不是取值段 */
111
+ export const THEME_COLORS_SEGMENT = 'colors';
112
+ /** 配色定义文件(`<配色>/index.ts`)与附加样式(`<配色>/theme.scss`)的文件名 */
113
+ export const THEME_COLOR_DEF_FILE = 'index.ts';
114
+ export const THEME_COLOR_STYLE_FILE = 'theme.scss';
115
+ /** 主题包定义入口(`<包>/index.ts`)的文件名 */
116
+ export const THEME_PACKAGE_DEF_FILE = 'index.ts';
117
+ /**
118
+ * 主题包目录的**取值目录段**(`themes/` 之下、从包到配色叶子的真实目录层)
119
+ *
120
+ * 与 `THEME_COLORS_SEGMENT`(固定字面段)、`THEME_COLOR_DEF_FILE` /
121
+ * `THEME_COLOR_STYLE_FILE`(叶子文件名)共同构成「目录长什么样」的完整契约。
122
+ * 顺序即目录深度:`['pkg','device','page','colorId']` 对应
123
+ * `themes/<pkg>/<device>/<page>/colors/<colorId>/`。
124
+ *
125
+ * 构建插件的 `scan()` 用它做**递归下降**(每段一层目录),注册表的 `colorFromKey()`
126
+ * 用它拼正则(键 = `./` + 这些段,`page` 与 `colorId` 之间插入 `THEME_COLORS_SEGMENT`)。
127
+ * 改目录结构(配色下再分层、`colors` 改名)只动这一处。
91
128
  */
92
- export const BASE_VIEW_ID = 'base';
129
+ export const THEME_DIR_LEVELS = ['pkg', 'device', 'page', 'colorId'];
93
130
  //# sourceMappingURL=constants.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,cAAc,CAAC;AAE1C;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,SAAS,CAAC;AAE1C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,SAAS,CAAC;AAE/C;;;;;;;;;;;;;;;GAeG;AAEH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAO,CAAC;AAE1C;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,OAAO,CAAC;AAE3C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,OAAO,CAAC;AAEhD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC"}
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,cAAc,CAAC;AAE1C;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,SAAS,CAAC;AAE1C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,SAAS,CAAC;AAE/C;;;;;;;;;;;;;;;GAeG;AAEH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAO,CAAC;AAE1C;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,OAAO,CAAC;AAE3C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,OAAO,CAAC;AAEhD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,8CAA8C;AAC9C,MAAM,CAAC,MAAM,oBAAoB,GAAG,QAAQ,CAAC;AAE7C,0DAA0D;AAC1D,MAAM,CAAC,MAAM,oBAAoB,GAAG,UAAU,CAAC;AAC/C,MAAM,CAAC,MAAM,sBAAsB,GAAG,YAAY,CAAC;AAEnD,kCAAkC;AAClC,MAAM,CAAC,MAAM,sBAAsB,GAAG,UAAU,CAAC;AAEjD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAU,CAAC"}
package/dist/index.d.ts CHANGED
@@ -11,7 +11,7 @@
11
11
  * • **构建器耦合**(`import.meta.glob` / `import.meta.env`)→ 由宿主适配器提供
12
12
  * • **路由**(vue-router)→ 应用自己的世界观
13
13
  * • **业务资产**(`themes/**` 的版式与配色实现)→ 随应用走
14
- * 见 `docs/frontend/theme-package-extraction.md` 的「〇、一条原则」与「五、四个注入点契约」。
14
+ * 见 `docs/standards/theme-package-extraction.md` 的「〇、一条原则」与「五、四个注入点契约」。
15
15
  *
16
16
  * === 为什么用显式具名导出,而不是 `export *` ===
17
17
  * 立项文档 §四 有一张"公开 API 面"的表,本文件就是那张表的**落地**:表里没有的名字
@@ -29,14 +29,40 @@
29
29
  *
30
30
  * @author yijiu2025
31
31
  */
32
- export { THEME_ID_RE, DEFAULT_THEME_ID, DEFAULT_THEME_PACKAGE, DEFAULT_THEME_PAGE, DEFAULT_THEME_COLOR, DEFAULT_THEME_DARK_COLOR, BASE_VIEW_ID } from './constants.js';
33
- export { THEME_TONES, TONE_LABELS, isThemeTone, normalizeTone } from './tone.js';
34
- export type { ThemeTone } from './tone.js';
35
- export { MODE_CYCLE, MODE_LABELS, isThemeMode, normalizeMode } from './mode.js';
36
- export type { ThemeMode } from './mode.js';
32
+ export { THEME_ID_RE, DEFAULT_THEME_ID, DEFAULT_THEME_PACKAGE, DEFAULT_THEME_PAGE, DEFAULT_THEME_COLOR, DEFAULT_THEME_DARK_COLOR, THEME_DIR_LEVELS, THEME_COLORS_SEGMENT, THEME_COLOR_DEF_FILE, THEME_COLOR_STYLE_FILE, THEME_PACKAGE_DEF_FILE } from './constants.js';
33
+ export { THEME_TONES, TONE_LABELS, isThemeTone, normalizeTone } from './scheme.js';
34
+ export type { ThemeTone } from './scheme.js';
35
+ export { MODE_CYCLE, MODE_LABELS, isThemeMode, normalizeMode } from './scheme.js';
36
+ export type { ThemeMode } from './scheme.js';
37
37
  export { isSafeTokenName, isSafeTokenValue, isSafeTokenEntry, sanitizeOverrides } from './tokens.js';
38
38
  export type { ThemeTokenOverrides, SanitizeResult, ThemeTokenLayers } from './tokens.js';
39
39
  export type { MauthThemeMeta, MauthThemePackage, MauthThemeColor, MauthThemeRecord } from './types.js';
40
+ export { buildThemeRegistry, installThemeRegistry } from './registry.js';
41
+ export type { ThemeAssetModules } from './registry.js';
42
+ export { listThemes, listColorsOf, toneOfColor, toneOfAnyScope, listColorIdsOfTone, resolveThemeId, getThemeRecord, getDefaultThemeId, isKnownTheme, listColorViews } from './registry.js';
40
43
  export { DEVICE_SCOPED_PARAMS, readDeviceParam } from './views/params.js';
41
44
  export type { DeviceScopedParam } from './views/params.js';
45
+ export { asDeviceId, containersOf, deviceContext, deviceLabel, pickDeviceId } from './views/device.js';
46
+ export type { DeviceDef, DeviceMatchContext } from './views/device.js';
47
+ /**
48
+ * 设备注册表工厂:一份 `DeviceDef[]` 清单 → 全套已绑定的判定机制。
49
+ * 宿主不再逐个转发判定函数,只写清单、调一次工厂。
50
+ */
51
+ export { createDeviceRegistry } from './views/device-registry.js';
52
+ export type { DeviceRegistry, DeviceRegistryOptions } from './views/device-registry.js';
53
+ /**
54
+ * 外部下发的主题配置:**只归一形状,不碰网络**
55
+ *
56
+ * 取数(超时 / 重试 / cookie 策略)属于宿主,故不在此。
57
+ */
58
+ export { normalizeRemoteThemeConfig } from './scheme.js';
59
+ export type { RemoteThemeConfig } from './scheme.js';
60
+ /**
61
+ * 主题域清单(主题目录里那份 `theme.config.ts` 的契约):调试开关
62
+ *
63
+ * 它是**主题域自带**的配置(跟主题包一起走);「主题目录在哪」不在这里 ——
64
+ * 那是宿主的部署事实,由宿主的 env 提供,换目录不必改代码也不必改这个包。
65
+ */
66
+ export { defineThemeManifest, isThemeDebugVisible, resolveThemeManifest, installThemeManifest, themeManifest } from './manifest.js';
67
+ export type { ResolvedThemeDebug, ResolvedThemeManifest, ThemeDebugMode, ThemeManifest } from './manifest.js';
42
68
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACxB,YAAY,EACb,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACjF,YAAY,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAE3C,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAChF,YAAY,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAE3C,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,mBAAmB,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEzF,YAAY,EACV,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAC1E,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACtB,gBAAgB,EAClB,oBAAoB,EACpB,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,EACvB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACnF,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAClF,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,mBAAmB,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEzF,YAAY,EACV,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACzE,YAAY,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EACL,UAAU,EACR,YAAY,EACd,WAAW,EACX,cAAc,EACd,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,cAAc,EACf,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAC1E,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE3D,OAAO,EACL,UAAU,EACV,YAAY,EACZ,aAAa,EACb,WAAW,EACX,YAAY,EACb,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvE;;;GAGG;AACH,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,YAAY,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAExF;;;;GAIG;AACH,OAAO,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AACzD,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD;;;;;GAKG;AACH,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,aAAa,EACd,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,kBAAkB,EAClB,qBAAqB,EACrB,cAAc,EACd,aAAa,EACd,MAAM,eAAe,CAAC"}
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@
11
11
  * • **构建器耦合**(`import.meta.glob` / `import.meta.env`)→ 由宿主适配器提供
12
12
  * • **路由**(vue-router)→ 应用自己的世界观
13
13
  * • **业务资产**(`themes/**` 的版式与配色实现)→ 随应用走
14
- * 见 `docs/frontend/theme-package-extraction.md` 的「〇、一条原则」与「五、四个注入点契约」。
14
+ * 见 `docs/standards/theme-package-extraction.md` 的「〇、一条原则」与「五、四个注入点契约」。
15
15
  *
16
16
  * === 为什么用显式具名导出,而不是 `export *` ===
17
17
  * 立项文档 §四 有一张"公开 API 面"的表,本文件就是那张表的**落地**:表里没有的名字
@@ -29,9 +29,30 @@
29
29
  *
30
30
  * @author yijiu2025
31
31
  */
32
- export { THEME_ID_RE, DEFAULT_THEME_ID, DEFAULT_THEME_PACKAGE, DEFAULT_THEME_PAGE, DEFAULT_THEME_COLOR, DEFAULT_THEME_DARK_COLOR, BASE_VIEW_ID } from './constants.js';
33
- export { THEME_TONES, TONE_LABELS, isThemeTone, normalizeTone } from './tone.js';
34
- export { MODE_CYCLE, MODE_LABELS, isThemeMode, normalizeMode } from './mode.js';
32
+ export { THEME_ID_RE, DEFAULT_THEME_ID, DEFAULT_THEME_PACKAGE, DEFAULT_THEME_PAGE, DEFAULT_THEME_COLOR, DEFAULT_THEME_DARK_COLOR, THEME_DIR_LEVELS, THEME_COLORS_SEGMENT, THEME_COLOR_DEF_FILE, THEME_COLOR_STYLE_FILE, THEME_PACKAGE_DEF_FILE } from './constants.js';
33
+ export { THEME_TONES, TONE_LABELS, isThemeTone, normalizeTone } from './scheme.js';
34
+ export { MODE_CYCLE, MODE_LABELS, isThemeMode, normalizeMode } from './scheme.js';
35
35
  export { isSafeTokenName, isSafeTokenValue, isSafeTokenEntry, sanitizeOverrides } from './tokens.js';
36
+ export { buildThemeRegistry, installThemeRegistry } from './registry.js';
37
+ export { listThemes, listColorsOf, toneOfColor, toneOfAnyScope, listColorIdsOfTone, resolveThemeId, getThemeRecord, getDefaultThemeId, isKnownTheme, listColorViews } from './registry.js';
36
38
  export { DEVICE_SCOPED_PARAMS, readDeviceParam } from './views/params.js';
39
+ export { asDeviceId, containersOf, deviceContext, deviceLabel, pickDeviceId } from './views/device.js';
40
+ /**
41
+ * 设备注册表工厂:一份 `DeviceDef[]` 清单 → 全套已绑定的判定机制。
42
+ * 宿主不再逐个转发判定函数,只写清单、调一次工厂。
43
+ */
44
+ export { createDeviceRegistry } from './views/device-registry.js';
45
+ /**
46
+ * 外部下发的主题配置:**只归一形状,不碰网络**
47
+ *
48
+ * 取数(超时 / 重试 / cookie 策略)属于宿主,故不在此。
49
+ */
50
+ export { normalizeRemoteThemeConfig } from './scheme.js';
51
+ /**
52
+ * 主题域清单(主题目录里那份 `theme.config.ts` 的契约):调试开关
53
+ *
54
+ * 它是**主题域自带**的配置(跟主题包一起走);「主题目录在哪」不在这里 ——
55
+ * 那是宿主的部署事实,由宿主的 env 提供,换目录不必改代码也不必改这个包。
56
+ */
57
+ export { defineThemeManifest, isThemeDebugVisible, resolveThemeManifest, installThemeManifest, themeManifest } from './manifest.js';
37
58
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACxB,YAAY,EACb,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAGjF,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAGhF,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,aAAa,CAAC;AAUrB,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,wBAAwB,EACtB,gBAAgB,EAClB,oBAAoB,EACpB,oBAAoB,EACpB,sBAAsB,EACtB,sBAAsB,EACvB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGnF,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGlF,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,aAAa,CAAC;AAUrB,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAEzE,OAAO,EACL,UAAU,EACR,YAAY,EACd,WAAW,EACX,cAAc,EACd,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,cAAc,EACf,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAG1E,OAAO,EACL,UAAU,EACV,YAAY,EACZ,aAAa,EACb,WAAW,EACX,YAAY,EACb,MAAM,mBAAmB,CAAC;AAG3B;;;GAGG;AACH,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAGlE;;;;GAIG;AACH,OAAO,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AAGzD;;;;;GAKG;AACH,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,aAAa,EACd,MAAM,eAAe,CAAC"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * 主题域清单(manifest)—— 主题目录里那份 `theme.config.ts` 的契约
3
+ *
4
+ * === 它只回答一个问题 ===
5
+ * 调试面板**什么时候出现**(`debug`)
6
+ *
7
+ * === 为什么不再声明「路径 → 页面」投影(routes)===
8
+ * 早先清单里有一份 `routes`(哪条路径算哪个页面),理由是「配色与版式的查找范围是
9
+ * 包 × 设备 × 页面」。但页面身份如今由 `<ThemeView page="...">` **显式传入**(容器
10
+ * 文件位置即身份),并由 `setActivePageView` 维护进 store 的 `activePage` —— 不再
11
+ * 需要「从路径反推页面」。而「路径 → 页面」的映射本身是 **router 的职责**(路由表
12
+ * 由 `listPageViews()` 自动生成,页面名就编码在 path 里),主题清单再声明一份就是
13
+ * 第二个真相,两处不一致时只有「面板认不出页面」这种无从归因的静默症状。
14
+ *
15
+ * 于是清单收敛为**只声明一件事**,其余全部由机制推导:
16
+ * · 主题根目录在哪 → `.env` 的 `VITE_THEME_DIR`
17
+ * · 有哪些设备 → `defineThemeDevice` 声明(设备域)
18
+ * · 有哪些页面 → `defineThemePage` 声明(页面域)
19
+ * · 有哪些配色 → 主题包目录结构(扫出来的,不在这里声明)
20
+ *
21
+ * @author yijiu2025
22
+ * @since 2026-10-17(2026-10-18 移除 routes 投影:页面身份由 ThemeView 显式传入)
23
+ */
24
+ /**
25
+ * 调试面板开关
26
+ *
27
+ * `off` 永不出现(产物里也不下载面板,见宿主的异步加载入口)
28
+ * `url` 带触发参数才出现 —— **生产缺省**:真机没有 DevTools,
29
+ * 线上排查"这个租户颜色不对"时能直接切主题看效果是刚需
30
+ * `always` 首次渲染就出现(演示 / 联调环境)
31
+ */
32
+ export type ThemeDebugMode = 'off' | 'url' | 'always';
33
+ /** 清单的输入形态(`defineThemeManifest` 的参数,字段全可省) */
34
+ export interface ThemeManifest {
35
+ debug?: {
36
+ enabled?: ThemeDebugMode;
37
+ /** 触发参数名,缺省 `debug` */
38
+ query?: string;
39
+ /** 触发参数值,缺省 `theme`(即 `?debug=theme`) */
40
+ value?: string;
41
+ };
42
+ }
43
+ /** 补全默认值后的清单 */
44
+ export interface ResolvedThemeManifest {
45
+ debug: {
46
+ enabled: ThemeDebugMode;
47
+ query: string;
48
+ value: string;
49
+ };
50
+ }
51
+ /** 调试开关的补全值(供 `isThemeDebugVisible` 的参数类型使用) */
52
+ export type ResolvedThemeDebug = ResolvedThemeManifest['debug'];
53
+ /**
54
+ * 声明并校验主题清单
55
+ *
56
+ * 与白名单常量同一口径:配置写错是开发者错误,应该在启动时就炸,
57
+ * 而不是等到"面板不出现"这种无从归因的现象。
58
+ */
59
+ export declare function defineThemeManifest(manifest?: ThemeManifest): ResolvedThemeManifest;
60
+ /**
61
+ * 调试面板此刻是否该出现
62
+ *
63
+ * `url` 档用 `query` 里的 `query` 键比对 `value`(缺省 `?debug=theme`)。
64
+ * 参数表只要求"能按键取值"的形态(vue-router 的 `route.query`、`URLSearchParams`
65
+ * 转出来的普通对象都满足),库不绑定任何一种 —— 它不碰路由。
66
+ *
67
+ * ⚠️ 值可能是数组(`?debug=theme&debug=other`):任一元素命中即算命中,
68
+ * 与 vue-router 的 `LocationQuery` 形态一致。
69
+ */
70
+ export declare function isThemeDebugVisible(debug: ResolvedThemeDebug, query: Readonly<Record<string, unknown>>): boolean;
71
+ /**
72
+ * 解析运行时拿到的清单(构建插件扫出来的 `theme.config.ts`)
73
+ *
74
+ * 目录里没写清单时用机制默认值(面板退回 `url` 档),而不是让页面起不来。
75
+ * 这是「从构建产物到运行时单例」的收口点:清单写错在 `defineThemeManifest` 求值期就炸,
76
+ * 缺失则在这里兜底。
77
+ */
78
+ export declare function resolveThemeManifest(manifest: ResolvedThemeManifest | null | undefined): ResolvedThemeManifest;
79
+ /** 装入已解析的清单(`virtual:theme` 自动装配时调一次) */
80
+ export declare function installThemeManifest(manifest: ResolvedThemeManifest): void;
81
+ /** 当前清单单例(调试面板可见性用) */
82
+ export declare function themeManifest(): ResolvedThemeManifest;
83
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,KAAK,GAAG,QAAQ,CAAC;AAEtD,+CAA+C;AAC/C,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE;QACN,OAAO,CAAC,EAAE,cAAc,CAAC;QACzB,uBAAuB;QACvB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,yCAAyC;QACzC,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAED,gBAAgB;AAChB,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE;QAAE,OAAO,EAAE,cAAc,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAClE;AAED,gDAAgD;AAChD,MAAM,MAAM,kBAAkB,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAIhE;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,GAAE,aAAkB,GAAG,qBAAqB,CAevF;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,kBAAkB,EACzB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACvC,OAAO,CAMT;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,qBAAqB,GAAG,IAAI,GAAG,SAAS,GACjD,qBAAqB,CAEvB;AAYD,yCAAyC;AACzC,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,qBAAqB,GAAG,IAAI,CAE1E;AAED,uBAAuB;AACvB,wBAAgB,aAAa,IAAI,qBAAqB,CAErD"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * 主题域清单(manifest)—— 主题目录里那份 `theme.config.ts` 的契约
3
+ *
4
+ * === 它只回答一个问题 ===
5
+ * 调试面板**什么时候出现**(`debug`)
6
+ *
7
+ * === 为什么不再声明「路径 → 页面」投影(routes)===
8
+ * 早先清单里有一份 `routes`(哪条路径算哪个页面),理由是「配色与版式的查找范围是
9
+ * 包 × 设备 × 页面」。但页面身份如今由 `<ThemeView page="...">` **显式传入**(容器
10
+ * 文件位置即身份),并由 `setActivePageView` 维护进 store 的 `activePage` —— 不再
11
+ * 需要「从路径反推页面」。而「路径 → 页面」的映射本身是 **router 的职责**(路由表
12
+ * 由 `listPageViews()` 自动生成,页面名就编码在 path 里),主题清单再声明一份就是
13
+ * 第二个真相,两处不一致时只有「面板认不出页面」这种无从归因的静默症状。
14
+ *
15
+ * 于是清单收敛为**只声明一件事**,其余全部由机制推导:
16
+ * · 主题根目录在哪 → `.env` 的 `VITE_THEME_DIR`
17
+ * · 有哪些设备 → `defineThemeDevice` 声明(设备域)
18
+ * · 有哪些页面 → `defineThemePage` 声明(页面域)
19
+ * · 有哪些配色 → 主题包目录结构(扫出来的,不在这里声明)
20
+ *
21
+ * @author yijiu2025
22
+ * @since 2026-10-17(2026-10-18 移除 routes 投影:页面身份由 ThemeView 显式传入)
23
+ */
24
+ const DEBUG_MODES = ['off', 'url', 'always'];
25
+ /**
26
+ * 声明并校验主题清单
27
+ *
28
+ * 与白名单常量同一口径:配置写错是开发者错误,应该在启动时就炸,
29
+ * 而不是等到"面板不出现"这种无从归因的现象。
30
+ */
31
+ export function defineThemeManifest(manifest = {}) {
32
+ const debug = manifest.debug ?? {};
33
+ const enabled = debug.enabled ?? 'url';
34
+ if (!DEBUG_MODES.includes(enabled)) {
35
+ throw new Error(`[theme] debug.enabled 只能是 ${DEBUG_MODES.join(' / ')},收到「${String(enabled)}」。`);
36
+ }
37
+ const query = debug.query ?? 'debug';
38
+ const value = debug.value ?? 'theme';
39
+ if (!query.trim() || !value.trim()) {
40
+ throw new Error('[theme] debug.query / debug.value 必须是非空字符串。');
41
+ }
42
+ return { debug: { enabled, query, value } };
43
+ }
44
+ /**
45
+ * 调试面板此刻是否该出现
46
+ *
47
+ * `url` 档用 `query` 里的 `query` 键比对 `value`(缺省 `?debug=theme`)。
48
+ * 参数表只要求"能按键取值"的形态(vue-router 的 `route.query`、`URLSearchParams`
49
+ * 转出来的普通对象都满足),库不绑定任何一种 —— 它不碰路由。
50
+ *
51
+ * ⚠️ 值可能是数组(`?debug=theme&debug=other`):任一元素命中即算命中,
52
+ * 与 vue-router 的 `LocationQuery` 形态一致。
53
+ */
54
+ export function isThemeDebugVisible(debug, query) {
55
+ if (debug.enabled === 'off')
56
+ return false;
57
+ if (debug.enabled === 'always')
58
+ return true;
59
+ const raw = query[debug.query];
60
+ const values = Array.isArray(raw) ? raw : [raw];
61
+ return values.some(item => typeof item === 'string' && item === debug.value);
62
+ }
63
+ /**
64
+ * 解析运行时拿到的清单(构建插件扫出来的 `theme.config.ts`)
65
+ *
66
+ * 目录里没写清单时用机制默认值(面板退回 `url` 档),而不是让页面起不来。
67
+ * 这是「从构建产物到运行时单例」的收口点:清单写错在 `defineThemeManifest` 求值期就炸,
68
+ * 缺失则在这里兜底。
69
+ */
70
+ export function resolveThemeManifest(manifest) {
71
+ return manifest ?? defineThemeManifest({});
72
+ }
73
+ /**
74
+ * 模块级清单单例 —— 「清单从哪来」的收口点
75
+ *
76
+ * 与配色注册表(`installThemeRegistry`)同一范式:`virtual:theme` 虚拟模块被 import 的
77
+ * 那一刻(模块求值期副作用)把 resolve 后的清单装进来,之后内核各处(调试面板可见性)
78
+ * 直接读本单例,**无需宿主 import `virtual:theme` 再转发**。
79
+ * 未装入时为机制默认(面板 `url` 档),而不是让应用起不来。
80
+ */
81
+ let themeManifestSingleton = defineThemeManifest({});
82
+ /** 装入已解析的清单(`virtual:theme` 自动装配时调一次) */
83
+ export function installThemeManifest(manifest) {
84
+ themeManifestSingleton = manifest;
85
+ }
86
+ /** 当前清单单例(调试面板可见性用) */
87
+ export function themeManifest() {
88
+ return themeManifestSingleton;
89
+ }
90
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AA+BH,MAAM,WAAW,GAA8B,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;AAExE;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,WAA0B,EAAE;IAC9D,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,IAAI,EAAE,CAAC;IACnC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC;IACvC,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CACb,6BAA6B,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,CAC/E,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,OAAO,CAAC;IACrC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,OAAO,CAAC;IACrC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IACjE,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC;AAC9C,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB,CACjC,KAAyB,EACzB,KAAwC;IAExC,IAAI,KAAK,CAAC,OAAO,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IAC1C,IAAI,KAAK,CAAC,OAAO,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC5C,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC/B,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAChD,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,KAAK,CAAC,KAAK,CAAC,CAAC;AAC/E,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAClC,QAAkD;IAElD,OAAO,QAAQ,IAAI,mBAAmB,CAAC,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED;;;;;;;GAOG;AACH,IAAI,sBAAsB,GAA0B,mBAAmB,CAAC,EAAE,CAAC,CAAC;AAE5E,yCAAyC;AACzC,MAAM,UAAU,oBAAoB,CAAC,QAA+B;IAClE,sBAAsB,GAAG,QAAQ,CAAC;AACpC,CAAC;AAED,uBAAuB;AACvB,MAAM,UAAU,aAAa;IAC3B,OAAO,sBAAsB,CAAC;AAChC,CAAC"}
@@ -0,0 +1,73 @@
1
+ import type { MauthThemeColor, MauthThemeMeta, MauthThemePackage, MauthThemeRecord } from './types.js';
2
+ import { type ThemeTone } from './scheme.js';
3
+ /**
4
+ * 虚拟模块 `virtual:theme` 产出的三个加载表(由 `skinsuite/vite` 的 `themeAssets()`
5
+ * 在构建期扫描生成)。键相对主题包目录 `themes/`(`./<包>/...`)。
6
+ */
7
+ export interface ThemeAssetModules {
8
+ /** 包定义:`./<包>/index.ts` */
9
+ packages: Record<string, {
10
+ default: MauthThemePackage;
11
+ }>;
12
+ /** 配色元信息 + tokens:`./<包>/<设备>/<页面>/colors/<配色>/index.ts` */
13
+ colors: Record<string, {
14
+ default: MauthThemeColor;
15
+ }>;
16
+ /** 附加样式(惰性,取编译后的 CSS 字符串):`.../theme.scss` */
17
+ styles: Record<string, () => Promise<string>>;
18
+ }
19
+ /**
20
+ * 把三个加载表构建成一张配色注册表
21
+ *
22
+ * @param modules 虚拟模块 `virtual:theme` 的三个加载表
23
+ * @param onWarn 构建期告警回调(可选;内核默认静默以保持零依赖,宿主传 `console.warn`)
24
+ */
25
+ export declare function buildThemeRegistry(modules: ThemeAssetModules, onWarn?: (msg: string) => void): Map<string, MauthThemeRecord>;
26
+ /** 装入一张已构建好的配色注册表(宿主在启动时调一次) */
27
+ export declare function installThemeRegistry(registry: Map<string, MauthThemeRecord>): void;
28
+ /** 当前有哪些配色(可选按设备过滤;不传则返回全部) */
29
+ export declare function listThemes(device?: string): MauthThemeMeta[];
30
+ /**
31
+ * 列出某个包下的全部配色(按 id 去重、按目录名排序)
32
+ *
33
+ * 🔴 排序只按 id 字母序(`black < blue < cyan < rainbow < white`),不给黑白特权。
34
+ * 🔴 view ≡ pkg,所以本函数只列「当前包」下的配色。
35
+ */
36
+ export declare function listColorsOf(device: string, page: string, pkg: string): MauthThemeMeta[];
37
+ /** 某配色在指定归属下的明暗系别;未登记返回 null */
38
+ export declare function toneOfColor(id: string, device: string, page: string, pkg: string): ThemeTone | null;
39
+ /** 某配色的系别(任意登记处);给"槽归位"这类没有明确范围的场景用 */
40
+ export declare function toneOfAnyScope(id: string): ThemeTone | null;
41
+ /** 某个版式下、指定系别的全部配色 id(按目录名排序) */
42
+ export declare function listColorIdsOfTone(device: string, page: string, pkg: string, tone: ThemeTone): string[];
43
+ /** 归一化并校验外部传入的配色 id(URL 参数 / 后端下发) */
44
+ export declare function resolveThemeId(raw: unknown, device?: string, page?: string, pkg?: string): string | null;
45
+ /**
46
+ * 取配色记录
47
+ *
48
+ * 回退链:指定 id(四段都匹配)→ 同系别首套 → 该范围兜底 → 空记录。
49
+ *
50
+ * 🔴 内核不设「默认设备」(默认设备是哪台是宿主的词汇表),所以 `device` **必填**
51
+ * (无默认值)。宿主在 re-export 壳里补齐自己的默认设备。
52
+ *
53
+ * 🔴 命名参数对象(2026-10-17 收口):此前 `(id, device, pkg, page)` 与宿主
54
+ * `themeRecordFor(pkg, device, page)` 参数顺序不一致(中间三段对着,但首尾互换),
55
+ * 调用点传错只会有「配色找错范围」这种无从归因的症状。现在改为对象,名字即语义。
56
+ */
57
+ export declare function getThemeRecord(params: {
58
+ id: string;
59
+ device: string;
60
+ pkg?: string;
61
+ page?: string;
62
+ }): MauthThemeRecord;
63
+ /** 该范围内默认用哪套配色;无配色时返回 null */
64
+ export declare function getDefaultThemeId(params: {
65
+ device: string;
66
+ page?: string;
67
+ pkg?: string;
68
+ }): string | null;
69
+ /** 该配色是否登记过(限定哪几段就要求哪几段匹配) */
70
+ export declare function isKnownTheme(id: unknown, device?: string, page?: string, pkg?: string): boolean;
71
+ /** 某设备某页面下有哪些包登记了配色(面板要按包分组) */
72
+ export declare function listColorViews(device: string, page: string): string[];
73
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAwCA,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAM5D;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,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;CAC/C;AA2DD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,iBAAiB,EAC1B,MAAM,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAe,GACvC,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAiE/B;AAcD,gCAAgC;AAChC,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,GAAG,IAAI,CAElF;AAmDD,+BAA+B;AAC/B,wBAAgB,UAAU,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,cAAc,EAAE,CAG5D;AAID;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,cAAc,EAAE,CAQxF;AAED,gCAAgC;AAChC,wBAAgB,WAAW,CACzB,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,GACV,SAAS,GAAG,IAAI,CAElB;AAED,uCAAuC;AACvC,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAK3D;AAED,iCAAiC;AACjC,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,SAAS,GACd,MAAM,EAAE,CAQV;AAED,sCAAsC;AACtC,wBAAgB,cAAc,CAC5B,GAAG,EAAE,OAAO,EACZ,MAAM,CAAC,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,MAAM,EACb,GAAG,CAAC,EAAE,MAAM,GACX,MAAM,GAAG,IAAI,CAUf;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GAAG,gBAAgB,CAgBnB;AAED,8BAA8B;AAC9B,wBAAgB,iBAAiB,CAAC,MAAM,EAAE;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,GAAG,MAAM,GAAG,IAAI,CAEhB;AAED,8BAA8B;AAC9B,wBAAgB,YAAY,CAAC,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAE/F;AAGD,gCAAgC;AAChC,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAQrE"}