@proteus-vue/component-ir 0.1.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/README.md +62 -0
- package/dist/audit.d.ts +45 -0
- package/dist/conformance.d.ts +40 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +1140 -0
- package/dist/map.d.ts +11 -0
- package/dist/primitives.d.ts +35 -0
- package/dist/schema.d.ts +62 -0
- package/dist/to-ir.d.ts +13 -0
- package/dist/validate.d.ts +21 -0
- package/package.json +27 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1140 @@
|
|
|
1
|
+
// src/schema.ts
|
|
2
|
+
var SEMANTIC_ENUM = [
|
|
3
|
+
// —— 布局原语(G-32 ① Layout 12:G-22 泛化——swiper/scroll-view/movable 消灭为属性)——
|
|
4
|
+
"layout.box",
|
|
5
|
+
"layout.inline",
|
|
6
|
+
"layout.stack",
|
|
7
|
+
"layout.grid",
|
|
8
|
+
"layout.fluid",
|
|
9
|
+
"layout.adaptive",
|
|
10
|
+
"layout.fit",
|
|
11
|
+
"layout.spacer",
|
|
12
|
+
"layout.divider",
|
|
13
|
+
"layout.scroll",
|
|
14
|
+
"layout.virtual-list",
|
|
15
|
+
"layout.masonry",
|
|
16
|
+
// ★G-31 B4:Fluid 体系扩展语义(有明确系统原生对应——原则 #10.8)
|
|
17
|
+
"layout.split",
|
|
18
|
+
"layout.safe",
|
|
19
|
+
"layout.sidebar",
|
|
20
|
+
// —— 基础 UI 原语(G-32 ② UI 18 + 既有按钮)——
|
|
21
|
+
"ui.text",
|
|
22
|
+
"ui.heading",
|
|
23
|
+
"ui.rich-text",
|
|
24
|
+
"ui.icon",
|
|
25
|
+
"ui.image",
|
|
26
|
+
"ui.avatar",
|
|
27
|
+
"ui.media",
|
|
28
|
+
"ui.canvas",
|
|
29
|
+
"ui.svg",
|
|
30
|
+
"ui.input",
|
|
31
|
+
"ui.textarea",
|
|
32
|
+
"ui.select",
|
|
33
|
+
"ui.checkbox",
|
|
34
|
+
"ui.radio",
|
|
35
|
+
"ui.switch",
|
|
36
|
+
"ui.slider",
|
|
37
|
+
"ui.picker",
|
|
38
|
+
"ui.form",
|
|
39
|
+
"ui.button",
|
|
40
|
+
// 既有按钮(小程序对照 row 14)
|
|
41
|
+
// ★G-31 B4:既有组件对齐(p-list-view/p-nav-bar)
|
|
42
|
+
"ui.list",
|
|
43
|
+
"ui.nav",
|
|
44
|
+
// —— 容器/导航原语(G-32 ③ Shell 10)——
|
|
45
|
+
"shell.page",
|
|
46
|
+
"shell.nav",
|
|
47
|
+
"shell.tabbar",
|
|
48
|
+
"shell.segment",
|
|
49
|
+
"shell.drawer",
|
|
50
|
+
"shell.modal",
|
|
51
|
+
"shell.popover",
|
|
52
|
+
"shell.toast",
|
|
53
|
+
"shell.action-sheet",
|
|
54
|
+
// —— 交互/手势原语(G-32 ④ Gesture——组件形态 2 个;v-gesture: 指令归绑定层不产生 C-IR 节点)——
|
|
55
|
+
"gesture.draggable",
|
|
56
|
+
"gesture.scrollable",
|
|
57
|
+
// —— 工程原语(G-32 ⑥ Engineering——组件形态 3 个;Hook/路由方法归 API 层)——
|
|
58
|
+
"engineering.router-link",
|
|
59
|
+
"engineering.transition",
|
|
60
|
+
"engineering.animate",
|
|
61
|
+
// —— 能力入口(G-28 组件化:p-* 能力入口组件;useXxx Hook 归 API 层不产生 C-IR 节点)——
|
|
62
|
+
"capability.scan-qr",
|
|
63
|
+
"capability.pick-photo",
|
|
64
|
+
"capability.location"
|
|
65
|
+
];
|
|
66
|
+
var COMPONENT_IR_SCHEMA = {
|
|
67
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
68
|
+
$id: "https://proteus.dev/schemas/component-ir.json",
|
|
69
|
+
title: "Proteus Component IR (C-IR)",
|
|
70
|
+
type: "object",
|
|
71
|
+
required: ["tag", "semantic", "props", "children"],
|
|
72
|
+
properties: {
|
|
73
|
+
tag: { type: "string", pattern: "^p-[a-z0-9-]+$" },
|
|
74
|
+
semantic: { type: "string", enum: SEMANTIC_ENUM },
|
|
75
|
+
props: { type: "object", additionalProperties: true },
|
|
76
|
+
children: { type: "array", items: { $ref: "#" }, default: [] },
|
|
77
|
+
capabilities: { type: "array", items: { type: "object", required: ["name"] } },
|
|
78
|
+
degradation: { type: "object", additionalProperties: { enum: ["supported", "fallback", "unsupported"] } }
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
var TAG_SEMANTIC_MAP = {
|
|
82
|
+
// G-32 ① 布局原语(12)
|
|
83
|
+
"p-box": "layout.box",
|
|
84
|
+
"p-inline": "layout.inline",
|
|
85
|
+
"p-stack": "layout.stack",
|
|
86
|
+
"p-grid": "layout.grid",
|
|
87
|
+
"p-fluid": "layout.fluid",
|
|
88
|
+
"p-adaptive": "layout.adaptive",
|
|
89
|
+
"p-fit": "layout.fit",
|
|
90
|
+
"p-spacer": "layout.spacer",
|
|
91
|
+
"p-divider": "layout.divider",
|
|
92
|
+
"p-scroll": "layout.scroll",
|
|
93
|
+
"p-virtual-list": "layout.virtual-list",
|
|
94
|
+
"p-masonry": "layout.masonry",
|
|
95
|
+
// G-32 ② UI 原语(18)
|
|
96
|
+
"p-text": "ui.text",
|
|
97
|
+
"p-heading": "ui.heading",
|
|
98
|
+
"p-rich-text": "ui.rich-text",
|
|
99
|
+
"p-icon": "ui.icon",
|
|
100
|
+
"p-image": "ui.image",
|
|
101
|
+
"p-avatar": "ui.avatar",
|
|
102
|
+
"p-media": "ui.media",
|
|
103
|
+
"p-canvas": "ui.canvas",
|
|
104
|
+
"p-svg": "ui.svg",
|
|
105
|
+
"p-input": "ui.input",
|
|
106
|
+
"p-textarea": "ui.textarea",
|
|
107
|
+
"p-select": "ui.select",
|
|
108
|
+
"p-checkbox": "ui.checkbox",
|
|
109
|
+
"p-radio": "ui.radio",
|
|
110
|
+
"p-switch": "ui.switch",
|
|
111
|
+
"p-slider": "ui.slider",
|
|
112
|
+
"p-picker": "ui.picker",
|
|
113
|
+
"p-form": "ui.form",
|
|
114
|
+
"p-button": "ui.button",
|
|
115
|
+
// 既有按钮
|
|
116
|
+
// G-32 ③ Shell 原语(10)
|
|
117
|
+
"p-page": "shell.page",
|
|
118
|
+
"p-nav": "shell.nav",
|
|
119
|
+
"p-tabbar": "shell.tabbar",
|
|
120
|
+
"p-segment": "shell.segment",
|
|
121
|
+
"p-drawer": "shell.drawer",
|
|
122
|
+
"p-modal": "shell.modal",
|
|
123
|
+
"p-popover": "shell.popover",
|
|
124
|
+
"p-toast": "shell.toast",
|
|
125
|
+
"p-action-sheet": "shell.action-sheet",
|
|
126
|
+
"p-split": "layout.split",
|
|
127
|
+
// ★已落地绑定(G-32 S10 分栏语义——layout.split 承载)
|
|
128
|
+
// G-32 ④ Gesture 组件形态(2)
|
|
129
|
+
"p-draggable": "gesture.draggable",
|
|
130
|
+
"p-scrollable": "gesture.scrollable",
|
|
131
|
+
// G-32 ⑥ Engineering 组件形态(3)
|
|
132
|
+
"p-router-link": "engineering.router-link",
|
|
133
|
+
// ★E18 组件形态(p- 前缀产 C-IR)
|
|
134
|
+
"router-link": "engineering.router-link",
|
|
135
|
+
// ★兼容别名(Vue Router 风格 <router-link> 标签;非 catalog 条目——p-view 先例 G-31 B4)
|
|
136
|
+
"p-transition": "engineering.transition",
|
|
137
|
+
"p-animate": "engineering.animate",
|
|
138
|
+
// G-31 能力入口
|
|
139
|
+
"p-scan-qr": "capability.scan-qr",
|
|
140
|
+
"p-pick-photo": "capability.pick-photo",
|
|
141
|
+
"p-location": "capability.location",
|
|
142
|
+
// ★G-31 B4 现有组件对齐(src/components 实际标签 → L1 语义)
|
|
143
|
+
"p-view": "layout.box",
|
|
144
|
+
// 原子容器 = p-box 角色
|
|
145
|
+
"p-list-view": "ui.list",
|
|
146
|
+
"p-nav-bar": "ui.nav",
|
|
147
|
+
// ★G-31 B4 Fluid 体系扩展语义(有明确系统原生对应——原则 #10.8)
|
|
148
|
+
"p-safe": "layout.safe",
|
|
149
|
+
"p-sidebar": "layout.sidebar"
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// src/validate.ts
|
|
153
|
+
var DEFAULT_DESIGN_WIDTH = 375;
|
|
154
|
+
function validateComponentIR(ir) {
|
|
155
|
+
const diags = [];
|
|
156
|
+
if (!ir || typeof ir !== "object") {
|
|
157
|
+
return [{ code: "CIR_INVALID", message: "C-IR \u5FC5\u987B\u4E3A\u5BF9\u8C61" }];
|
|
158
|
+
}
|
|
159
|
+
const node = ir;
|
|
160
|
+
if (typeof node.tag !== "string" || !/^p-[a-z0-9-]+$/.test(node.tag)) {
|
|
161
|
+
diags.push({ code: "CIR_INVALID_TAG", message: `tag="${String(node.tag)}" \u975E\u6CD5\u2014\u2014\u5FC5\u987B p- \u524D\u7F00 + \u8BED\u4E49\u540D\u8BCD\uFF08G-31.1\uFF09` });
|
|
162
|
+
}
|
|
163
|
+
if (typeof node.semantic !== "string" || SEMANTIC_ENUM.indexOf(node.semantic) < 0) {
|
|
164
|
+
diags.push({ code: "CIR_INVALID_SEMANTIC", message: `semantic="${String(node.semantic)}" \u975E\u6CD5\u2014\u2014\u987B\u4E3A\u8BED\u4E49\u679A\u4E3E\uFF08<domain>.<kind>\uFF09` });
|
|
165
|
+
}
|
|
166
|
+
if (node.props === null || typeof node.props !== "object") {
|
|
167
|
+
diags.push({ code: "CIR_INVALID_PROPS", message: "props \u5FC5\u987B\u4E3A\u5BF9\u8C61" });
|
|
168
|
+
}
|
|
169
|
+
const caps = node.capabilities;
|
|
170
|
+
if (Array.isArray(caps) && caps.length) {
|
|
171
|
+
const declared = new Set(Object.keys(node.degradation ?? {}));
|
|
172
|
+
for (const cap2 of caps) {
|
|
173
|
+
if (typeof cap2 === "object" && cap2 !== null && typeof cap2.name === "string" && !declared.has(cap2.name)) {
|
|
174
|
+
diags.push({ code: "CMP006", message: `capability "${cap2.name}" \u672A\u58F0\u660E degradation\uFF08supported/fallback/unsupported\uFF09\u2014\u2014G-31.2` });
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (Array.isArray(node.children)) {
|
|
179
|
+
for (const child of node.children) {
|
|
180
|
+
diags.push.apply(diags, validateComponentIR(child));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return diags;
|
|
184
|
+
}
|
|
185
|
+
function validateGridConstraints(props, designWidth = DEFAULT_DESIGN_WIDTH) {
|
|
186
|
+
const minColWidth = props.minColWidth ?? props.minColWidth;
|
|
187
|
+
const maxCols = props.maxCols;
|
|
188
|
+
if (typeof minColWidth === "number" && typeof maxCols === "number" && maxCols > 1) {
|
|
189
|
+
const needed = minColWidth * maxCols;
|
|
190
|
+
if (needed > designWidth) {
|
|
191
|
+
return [
|
|
192
|
+
{
|
|
193
|
+
code: "GRID_CONFLICT",
|
|
194
|
+
message: `min-col-width=${minColWidth} \xD7 max-cols=${maxCols} = ${needed} > \u8BBE\u8BA1\u5BBD ${designWidth}\u2014\u2014max-cols=${maxCols} \u6C38\u4E0D\u8FBE\uFF08\u6C38\u8FDC\u5355\u5217\uFF09\uFF0C\u8BF7\u8C03\u5C0F min-col-width \u6216 max-cols`
|
|
195
|
+
}
|
|
196
|
+
];
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return [];
|
|
200
|
+
}
|
|
201
|
+
function validateComponentTree(ir, designWidth = DEFAULT_DESIGN_WIDTH) {
|
|
202
|
+
const diags = validateComponentIR(ir);
|
|
203
|
+
if (ir && typeof ir === "object") {
|
|
204
|
+
const node = ir;
|
|
205
|
+
if (node.props && typeof node.props === "object") {
|
|
206
|
+
diags.push.apply(diags, validateGridConstraints(node.props, designWidth));
|
|
207
|
+
}
|
|
208
|
+
if (Array.isArray(node.children)) {
|
|
209
|
+
for (const child of node.children) {
|
|
210
|
+
diags.push.apply(diags, validateComponentTree(child, designWidth));
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return diags;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// src/map.ts
|
|
218
|
+
var SEMANTIC_BACKEND_MAP = {
|
|
219
|
+
// —— 布局原语(G-22 四原语 + box/adaptive)——
|
|
220
|
+
"layout.box": {
|
|
221
|
+
"vue-dom": "div.proteus-box",
|
|
222
|
+
"native-ios": "UIView",
|
|
223
|
+
"native-android": "FrameLayout",
|
|
224
|
+
"native-harmony": "Stack",
|
|
225
|
+
skyline: "view",
|
|
226
|
+
flutter: "Container",
|
|
227
|
+
headless: "box"
|
|
228
|
+
},
|
|
229
|
+
"layout.stack": {
|
|
230
|
+
"vue-dom": "div.proteus-stack",
|
|
231
|
+
"native-ios": "UIStackView",
|
|
232
|
+
"native-android": "LinearLayout",
|
|
233
|
+
"native-harmony": "Flex",
|
|
234
|
+
skyline: "view.flex",
|
|
235
|
+
flutter: "Flex",
|
|
236
|
+
headless: "stack"
|
|
237
|
+
},
|
|
238
|
+
"layout.grid": {
|
|
239
|
+
"vue-dom": "div.proteus-grid",
|
|
240
|
+
"native-ios": "UICollectionView",
|
|
241
|
+
"native-android": "GridLayoutManager",
|
|
242
|
+
"native-harmony": "Grid",
|
|
243
|
+
skyline: "grid",
|
|
244
|
+
// Skyline 原生 grid 组件(编译期静态 WXSS)
|
|
245
|
+
flutter: "GridView",
|
|
246
|
+
headless: "grid"
|
|
247
|
+
},
|
|
248
|
+
"layout.fluid": {
|
|
249
|
+
"vue-dom": "div.proteus-fluid",
|
|
250
|
+
"native-ios": "AutoLayout",
|
|
251
|
+
"native-android": "ConstraintLayout",
|
|
252
|
+
"native-harmony": "Flex.fluid",
|
|
253
|
+
skyline: "view.fluid",
|
|
254
|
+
flutter: "Wrap",
|
|
255
|
+
headless: "fluid"
|
|
256
|
+
},
|
|
257
|
+
"layout.adaptive": {
|
|
258
|
+
"vue-dom": "div.proteus-adaptive",
|
|
259
|
+
"native-ios": "UISheet",
|
|
260
|
+
"native-android": "BottomSheetDialog",
|
|
261
|
+
"native-harmony": "Sheet",
|
|
262
|
+
// @ohos.arkui.advanced
|
|
263
|
+
skyline: "half-screen",
|
|
264
|
+
flutter: "showModal",
|
|
265
|
+
headless: "adaptive"
|
|
266
|
+
},
|
|
267
|
+
"layout.fit": {
|
|
268
|
+
"vue-dom": "div.proteus-fit",
|
|
269
|
+
"native-ios": "intrinsicSize",
|
|
270
|
+
"native-android": "wrapContent",
|
|
271
|
+
"native-harmony": "fitContent",
|
|
272
|
+
skyline: "view.fit",
|
|
273
|
+
flutter: "IntrinsicWidth",
|
|
274
|
+
headless: "fit"
|
|
275
|
+
},
|
|
276
|
+
// ★G-31 B4:Fluid 体系扩展语义(五端映射)
|
|
277
|
+
"layout.split": {
|
|
278
|
+
"vue-dom": "div.proteus-split",
|
|
279
|
+
"native-ios": "UISplitViewController",
|
|
280
|
+
"native-android": "SlidingPaneLayout",
|
|
281
|
+
"native-harmony": "SideBarContainer",
|
|
282
|
+
skyline: "view.split",
|
|
283
|
+
flutter: "Row",
|
|
284
|
+
headless: "split"
|
|
285
|
+
},
|
|
286
|
+
"layout.safe": {
|
|
287
|
+
"vue-dom": "div.proteus-safe",
|
|
288
|
+
"native-ios": "safeAreaLayoutGuide",
|
|
289
|
+
"native-android": "WindowInsets",
|
|
290
|
+
"native-harmony": "getAvoidArea",
|
|
291
|
+
skyline: "env.safe-area",
|
|
292
|
+
flutter: "SafeArea",
|
|
293
|
+
headless: "safe"
|
|
294
|
+
},
|
|
295
|
+
"layout.sidebar": {
|
|
296
|
+
"vue-dom": "div.proteus-sidebar",
|
|
297
|
+
"native-ios": "UISplitViewController.side",
|
|
298
|
+
"native-android": "NavigationRail",
|
|
299
|
+
"native-harmony": "SideBarContainer",
|
|
300
|
+
skyline: "view.sidebar",
|
|
301
|
+
flutter: "NavigationRail",
|
|
302
|
+
headless: "sidebar"
|
|
303
|
+
},
|
|
304
|
+
// —— 基础 UI 原语 ——
|
|
305
|
+
"ui.text": {
|
|
306
|
+
"vue-dom": "span",
|
|
307
|
+
"native-ios": "UILabel",
|
|
308
|
+
"native-android": "TextView",
|
|
309
|
+
"native-harmony": "Text",
|
|
310
|
+
skyline: "text",
|
|
311
|
+
flutter: "Text",
|
|
312
|
+
headless: "text"
|
|
313
|
+
},
|
|
314
|
+
"ui.button": {
|
|
315
|
+
"vue-dom": "button",
|
|
316
|
+
"native-ios": "UIButton",
|
|
317
|
+
"native-android": "Button",
|
|
318
|
+
"native-harmony": "Button",
|
|
319
|
+
skyline: "button",
|
|
320
|
+
flutter: "FilledButton",
|
|
321
|
+
headless: "button"
|
|
322
|
+
},
|
|
323
|
+
"ui.image": {
|
|
324
|
+
"vue-dom": "img",
|
|
325
|
+
"native-ios": "UIImageView",
|
|
326
|
+
"native-android": "ImageView",
|
|
327
|
+
"native-harmony": "Image",
|
|
328
|
+
skyline: "image",
|
|
329
|
+
flutter: "Image",
|
|
330
|
+
headless: "image"
|
|
331
|
+
},
|
|
332
|
+
"ui.input": {
|
|
333
|
+
"vue-dom": "input",
|
|
334
|
+
"native-ios": "UITextField",
|
|
335
|
+
"native-android": "EditText",
|
|
336
|
+
"native-harmony": "TextInput",
|
|
337
|
+
skyline: "input",
|
|
338
|
+
flutter: "TextField",
|
|
339
|
+
headless: "input"
|
|
340
|
+
},
|
|
341
|
+
"ui.list": {
|
|
342
|
+
"vue-dom": "div.proteus-list",
|
|
343
|
+
"native-ios": "UITableView",
|
|
344
|
+
"native-android": "RecyclerView",
|
|
345
|
+
"native-harmony": "List",
|
|
346
|
+
skyline: "list-view",
|
|
347
|
+
flutter: "ListView",
|
|
348
|
+
headless: "list"
|
|
349
|
+
},
|
|
350
|
+
"ui.nav": {
|
|
351
|
+
"vue-dom": "nav",
|
|
352
|
+
"native-ios": "UINavigationController",
|
|
353
|
+
"native-android": "NavigationRail",
|
|
354
|
+
"native-harmony": "Navigation",
|
|
355
|
+
skyline: "navigator",
|
|
356
|
+
flutter: "Navigator",
|
|
357
|
+
headless: "nav"
|
|
358
|
+
},
|
|
359
|
+
// —— 能力入口(G-28 组件化)——
|
|
360
|
+
"capability.scan-qr": {
|
|
361
|
+
"vue-dom": "button.proteus-scan-qr",
|
|
362
|
+
"native-ios": "AVCaptureSession",
|
|
363
|
+
"native-android": "CameraX",
|
|
364
|
+
"native-harmony": "ScanKit",
|
|
365
|
+
skyline: "wx.scanCode",
|
|
366
|
+
flutter: "scanQR",
|
|
367
|
+
headless: "scan-qr"
|
|
368
|
+
},
|
|
369
|
+
"capability.pick-photo": {
|
|
370
|
+
"vue-dom": "input.proteus-pick-photo",
|
|
371
|
+
"native-ios": "UIImagePicker",
|
|
372
|
+
"native-android": "PhotoPicker",
|
|
373
|
+
"native-harmony": "PhotoViewPicker",
|
|
374
|
+
skyline: "wx.chooseMedia",
|
|
375
|
+
flutter: "pickPhoto",
|
|
376
|
+
headless: "pick-photo"
|
|
377
|
+
},
|
|
378
|
+
"capability.location": {
|
|
379
|
+
"vue-dom": "button.proteus-location",
|
|
380
|
+
"native-ios": "CLLocationManager",
|
|
381
|
+
"native-android": "FusedLocation",
|
|
382
|
+
"native-harmony": "geoLocationManager",
|
|
383
|
+
skyline: "wx.getLocation",
|
|
384
|
+
flutter: "getLocation",
|
|
385
|
+
headless: "location"
|
|
386
|
+
},
|
|
387
|
+
// ★G-32 B1:新增 implemented 语义(16 个——布局补齐 6 + UI 补齐 4(+textarea) + Shell 4)
|
|
388
|
+
"layout.inline": {
|
|
389
|
+
"vue-dom": "div.proteus-inline",
|
|
390
|
+
"native-ios": "UITextAttachment",
|
|
391
|
+
"native-android": "TextView.inline",
|
|
392
|
+
"native-harmony": "Span",
|
|
393
|
+
skyline: "view.inline",
|
|
394
|
+
flutter: "InlineSpan",
|
|
395
|
+
headless: "inline"
|
|
396
|
+
},
|
|
397
|
+
"layout.spacer": {
|
|
398
|
+
"vue-dom": "div.proteus-spacer",
|
|
399
|
+
"native-ios": "UILayoutGuide",
|
|
400
|
+
"native-android": "Space",
|
|
401
|
+
"native-harmony": "Blank",
|
|
402
|
+
// ArkUI Blank 弹性空白
|
|
403
|
+
skyline: "view.spacer",
|
|
404
|
+
flutter: "Spacer",
|
|
405
|
+
headless: "spacer"
|
|
406
|
+
},
|
|
407
|
+
"layout.divider": {
|
|
408
|
+
"vue-dom": "hr.proteus-divider",
|
|
409
|
+
"native-ios": "UIView.divider",
|
|
410
|
+
"native-android": "View.divider",
|
|
411
|
+
"native-harmony": "Divider",
|
|
412
|
+
// ArkUI Divider
|
|
413
|
+
skyline: "view.divider",
|
|
414
|
+
flutter: "Divider",
|
|
415
|
+
headless: "divider"
|
|
416
|
+
},
|
|
417
|
+
"layout.scroll": {
|
|
418
|
+
"vue-dom": "div.proteus-scroll",
|
|
419
|
+
"native-ios": "UIScrollView",
|
|
420
|
+
"native-android": "ScrollView",
|
|
421
|
+
"native-harmony": "Scroll",
|
|
422
|
+
// ArkUI Scroll
|
|
423
|
+
skyline: "scroll-view",
|
|
424
|
+
flutter: "ScrollView",
|
|
425
|
+
headless: "scroll"
|
|
426
|
+
},
|
|
427
|
+
"layout.virtual-list": {
|
|
428
|
+
"vue-dom": "div.proteus-virtual-list",
|
|
429
|
+
"native-ios": "UICollectionView",
|
|
430
|
+
"native-android": "RecyclerView",
|
|
431
|
+
"native-harmony": "List",
|
|
432
|
+
skyline: "list-view",
|
|
433
|
+
flutter: "ListView",
|
|
434
|
+
headless: "virtual-list"
|
|
435
|
+
},
|
|
436
|
+
"layout.masonry": {
|
|
437
|
+
"vue-dom": "div.proteus-masonry",
|
|
438
|
+
"native-ios": "UICollectionView.masonry",
|
|
439
|
+
"native-android": "StaggeredGridLayoutManager",
|
|
440
|
+
"native-harmony": "WaterFlow",
|
|
441
|
+
// ArkUI WaterFlow 瀑布流
|
|
442
|
+
skyline: "grid.masonry",
|
|
443
|
+
flutter: "SliverMasonryGrid",
|
|
444
|
+
headless: "masonry"
|
|
445
|
+
},
|
|
446
|
+
"ui.heading": {
|
|
447
|
+
"vue-dom": "div.proteus-heading",
|
|
448
|
+
"native-ios": "UILabel.heading",
|
|
449
|
+
"native-android": "TextView.heading",
|
|
450
|
+
"native-harmony": "Text.heading",
|
|
451
|
+
skyline: "text.heading",
|
|
452
|
+
flutter: "Text.heading",
|
|
453
|
+
headless: "heading"
|
|
454
|
+
},
|
|
455
|
+
"ui.icon": {
|
|
456
|
+
"vue-dom": "span.proteus-icon",
|
|
457
|
+
"native-ios": "UIImageView.icon",
|
|
458
|
+
"native-android": "ImageView.icon",
|
|
459
|
+
"native-harmony": "SymbolGlyph",
|
|
460
|
+
// ArkUI 符号图标
|
|
461
|
+
skyline: "icon",
|
|
462
|
+
flutter: "Icon",
|
|
463
|
+
headless: "icon"
|
|
464
|
+
},
|
|
465
|
+
"ui.textarea": {
|
|
466
|
+
"vue-dom": "textarea",
|
|
467
|
+
"native-ios": "UITextView",
|
|
468
|
+
"native-android": "EditText.multiline",
|
|
469
|
+
"native-harmony": "TextArea",
|
|
470
|
+
// ArkUI TextArea
|
|
471
|
+
skyline: "textarea",
|
|
472
|
+
flutter: "TextField.multiline",
|
|
473
|
+
headless: "textarea"
|
|
474
|
+
},
|
|
475
|
+
"ui.switch": {
|
|
476
|
+
"vue-dom": "div.proteus-switch",
|
|
477
|
+
"native-ios": "UISwitch",
|
|
478
|
+
"native-android": "Switch",
|
|
479
|
+
"native-harmony": "Toggle",
|
|
480
|
+
// ArkUI Toggle
|
|
481
|
+
skyline: "switch",
|
|
482
|
+
flutter: "Switch",
|
|
483
|
+
headless: "switch"
|
|
484
|
+
},
|
|
485
|
+
"ui.slider": {
|
|
486
|
+
"vue-dom": "div.proteus-slider",
|
|
487
|
+
"native-ios": "UISlider",
|
|
488
|
+
"native-android": "SeekBar",
|
|
489
|
+
"native-harmony": "Slider",
|
|
490
|
+
// ArkUI Slider
|
|
491
|
+
skyline: "slider",
|
|
492
|
+
flutter: "Slider",
|
|
493
|
+
headless: "slider"
|
|
494
|
+
},
|
|
495
|
+
"shell.nav": {
|
|
496
|
+
"vue-dom": "nav.proteus-nav",
|
|
497
|
+
"native-ios": "UINavigationBar",
|
|
498
|
+
"native-android": "Toolbar",
|
|
499
|
+
"native-harmony": "NavigationBar",
|
|
500
|
+
skyline: "navigator",
|
|
501
|
+
flutter: "AppBar",
|
|
502
|
+
headless: "nav"
|
|
503
|
+
},
|
|
504
|
+
"shell.tabbar": {
|
|
505
|
+
"vue-dom": "nav.proteus-tabbar",
|
|
506
|
+
"native-ios": "UITabBar",
|
|
507
|
+
"native-android": "BottomNavigationView",
|
|
508
|
+
"native-harmony": "Tabs",
|
|
509
|
+
// ArkUI Tabs
|
|
510
|
+
skyline: "tabbar",
|
|
511
|
+
flutter: "BottomNavigationBar",
|
|
512
|
+
headless: "tabbar"
|
|
513
|
+
},
|
|
514
|
+
"shell.drawer": {
|
|
515
|
+
"vue-dom": "aside.proteus-drawer",
|
|
516
|
+
"native-ios": "UIView.drawer",
|
|
517
|
+
"native-android": "DrawerLayout",
|
|
518
|
+
"native-harmony": "Panel",
|
|
519
|
+
skyline: "view.drawer",
|
|
520
|
+
flutter: "Drawer",
|
|
521
|
+
headless: "drawer"
|
|
522
|
+
},
|
|
523
|
+
"shell.modal": {
|
|
524
|
+
"vue-dom": "div.proteus-modal",
|
|
525
|
+
"native-ios": "UIAlertController",
|
|
526
|
+
"native-android": "Dialog",
|
|
527
|
+
"native-harmony": "CustomDialog",
|
|
528
|
+
skyline: "modal",
|
|
529
|
+
flutter: "showDialog",
|
|
530
|
+
headless: "modal"
|
|
531
|
+
},
|
|
532
|
+
// ★G-32 B4:Shell 补齐(page/segment/popover/action-sheet)+ UI 补齐(rich-text/avatar/media/canvas/svg/select/checkbox/radio/picker/form)
|
|
533
|
+
"shell.page": {
|
|
534
|
+
"vue-dom": "div.proteus-page",
|
|
535
|
+
"native-ios": "UIViewController",
|
|
536
|
+
"native-android": "Activity",
|
|
537
|
+
"native-harmony": "Page",
|
|
538
|
+
// ArkUI Page
|
|
539
|
+
skyline: "page",
|
|
540
|
+
flutter: "Scaffold",
|
|
541
|
+
headless: "page"
|
|
542
|
+
},
|
|
543
|
+
"shell.segment": {
|
|
544
|
+
"vue-dom": "div.proteus-segment",
|
|
545
|
+
"native-ios": "UISegmentedControl",
|
|
546
|
+
"native-android": "TabLayout",
|
|
547
|
+
"native-harmony": "Segmented",
|
|
548
|
+
// ArkUI 分段器
|
|
549
|
+
skyline: "segment",
|
|
550
|
+
flutter: "SegmentedButton",
|
|
551
|
+
headless: "segment"
|
|
552
|
+
},
|
|
553
|
+
"shell.popover": {
|
|
554
|
+
"vue-dom": "div.proteus-popover",
|
|
555
|
+
"native-ios": "UIPopoverController",
|
|
556
|
+
"native-android": "PopupWindow",
|
|
557
|
+
"native-harmony": "Popup",
|
|
558
|
+
// ArkUI Popup
|
|
559
|
+
skyline: "view.popover",
|
|
560
|
+
flutter: "showMenu",
|
|
561
|
+
headless: "popover"
|
|
562
|
+
},
|
|
563
|
+
"shell.action-sheet": {
|
|
564
|
+
"vue-dom": "div.proteus-action-sheet",
|
|
565
|
+
"native-ios": "UIAlertController.actionSheet",
|
|
566
|
+
"native-android": "BottomSheet",
|
|
567
|
+
"native-harmony": "ActionSheet",
|
|
568
|
+
// ArkUI ActionSheetDialog
|
|
569
|
+
skyline: "action-sheet",
|
|
570
|
+
flutter: "showModalBottomSheet",
|
|
571
|
+
headless: "action-sheet"
|
|
572
|
+
},
|
|
573
|
+
"ui.rich-text": {
|
|
574
|
+
"vue-dom": "div.proteus-rich-text",
|
|
575
|
+
"native-ios": "UITextView.attributed",
|
|
576
|
+
"native-android": "TextView.html",
|
|
577
|
+
"native-harmony": "RichText",
|
|
578
|
+
// ArkUI RichText
|
|
579
|
+
skyline: "rich-text",
|
|
580
|
+
flutter: "RichText",
|
|
581
|
+
headless: "rich-text"
|
|
582
|
+
},
|
|
583
|
+
"ui.avatar": {
|
|
584
|
+
"vue-dom": "div.proteus-avatar",
|
|
585
|
+
"native-ios": "UIImageView.avatar",
|
|
586
|
+
"native-android": "ImageView.avatar",
|
|
587
|
+
"native-harmony": "Image.avatar",
|
|
588
|
+
skyline: "image.avatar",
|
|
589
|
+
flutter: "CircleAvatar",
|
|
590
|
+
headless: "avatar"
|
|
591
|
+
},
|
|
592
|
+
"ui.media": {
|
|
593
|
+
"vue-dom": "div.proteus-media",
|
|
594
|
+
"native-ios": "AVPlayerView",
|
|
595
|
+
"native-android": "VideoView",
|
|
596
|
+
"native-harmony": "Video",
|
|
597
|
+
// ArkUI Video
|
|
598
|
+
skyline: "video",
|
|
599
|
+
flutter: "VideoPlayer",
|
|
600
|
+
headless: "media"
|
|
601
|
+
},
|
|
602
|
+
"ui.canvas": {
|
|
603
|
+
"vue-dom": "canvas",
|
|
604
|
+
"native-ios": "UIView.canvas",
|
|
605
|
+
"native-android": "SurfaceView",
|
|
606
|
+
"native-harmony": "Canvas",
|
|
607
|
+
// ArkUI Canvas
|
|
608
|
+
skyline: "canvas",
|
|
609
|
+
flutter: "CustomPaint",
|
|
610
|
+
headless: "canvas"
|
|
611
|
+
},
|
|
612
|
+
"ui.svg": {
|
|
613
|
+
"vue-dom": "svg",
|
|
614
|
+
"native-ios": "UIView.svg",
|
|
615
|
+
"native-android": "VectorDrawable",
|
|
616
|
+
"native-harmony": "Shape",
|
|
617
|
+
// ArkUI Shape
|
|
618
|
+
skyline: "view.svg",
|
|
619
|
+
flutter: "SvgPicture",
|
|
620
|
+
headless: "svg"
|
|
621
|
+
},
|
|
622
|
+
"ui.select": {
|
|
623
|
+
"vue-dom": "div.proteus-select",
|
|
624
|
+
"native-ios": "UIPickerView",
|
|
625
|
+
"native-android": "Spinner",
|
|
626
|
+
"native-harmony": "Select",
|
|
627
|
+
// ArkUI Select
|
|
628
|
+
skyline: "picker",
|
|
629
|
+
flutter: "DropdownButton",
|
|
630
|
+
headless: "select"
|
|
631
|
+
},
|
|
632
|
+
"ui.checkbox": {
|
|
633
|
+
"vue-dom": "div.proteus-checkbox",
|
|
634
|
+
"native-ios": "UIButton.checkbox",
|
|
635
|
+
"native-android": "CheckBox",
|
|
636
|
+
"native-harmony": "Checkbox",
|
|
637
|
+
// ArkUI Checkbox
|
|
638
|
+
skyline: "checkbox",
|
|
639
|
+
flutter: "Checkbox",
|
|
640
|
+
headless: "checkbox"
|
|
641
|
+
},
|
|
642
|
+
"ui.radio": {
|
|
643
|
+
"vue-dom": "div.proteus-radio",
|
|
644
|
+
"native-ios": "UIButton.radio",
|
|
645
|
+
"native-android": "RadioButton",
|
|
646
|
+
"native-harmony": "Radio",
|
|
647
|
+
// ArkUI Radio
|
|
648
|
+
skyline: "radio",
|
|
649
|
+
flutter: "Radio",
|
|
650
|
+
headless: "radio"
|
|
651
|
+
},
|
|
652
|
+
"ui.picker": {
|
|
653
|
+
"vue-dom": "div.proteus-picker",
|
|
654
|
+
"native-ios": "UIDatePicker",
|
|
655
|
+
"native-android": "DatePicker",
|
|
656
|
+
"native-harmony": "DatePicker",
|
|
657
|
+
// ArkUI DatePicker
|
|
658
|
+
skyline: "picker-view",
|
|
659
|
+
flutter: "showDatePicker",
|
|
660
|
+
headless: "picker"
|
|
661
|
+
},
|
|
662
|
+
"ui.form": {
|
|
663
|
+
"vue-dom": "form",
|
|
664
|
+
"native-ios": "UIView.form",
|
|
665
|
+
"native-android": "LinearLayout.form",
|
|
666
|
+
"native-harmony": "FormComponent",
|
|
667
|
+
// ArkUI FormComponent
|
|
668
|
+
skyline: "form",
|
|
669
|
+
flutter: "Form",
|
|
670
|
+
headless: "form"
|
|
671
|
+
},
|
|
672
|
+
// ★G-32 B4 ④ Gesture 组件形态(drag/scrollable——指令/useGesture 属绑定层不产渲染节点)
|
|
673
|
+
"gesture.draggable": {
|
|
674
|
+
"vue-dom": "div.proteus-draggable",
|
|
675
|
+
"native-ios": "UIPanGestureRecognizer",
|
|
676
|
+
"native-android": "GestureDetector",
|
|
677
|
+
"native-harmony": "PanGesture",
|
|
678
|
+
// ArkUI PanGesture
|
|
679
|
+
skyline: "movable-view",
|
|
680
|
+
flutter: "Draggable",
|
|
681
|
+
headless: "draggable"
|
|
682
|
+
},
|
|
683
|
+
"gesture.scrollable": {
|
|
684
|
+
"vue-dom": "div.proteus-scrollable",
|
|
685
|
+
"native-ios": "UIScrollView.gesture",
|
|
686
|
+
"native-android": "NestedScrollView",
|
|
687
|
+
"native-harmony": "Scroll.gesture",
|
|
688
|
+
// ArkUI Scroll + 手势增强
|
|
689
|
+
skyline: "scroll-view",
|
|
690
|
+
flutter: "Scrollable",
|
|
691
|
+
headless: "scrollable"
|
|
692
|
+
},
|
|
693
|
+
// ★G-32 B5 尾巴:E18 声明式导航组件形态(p-router-link——语义「导航目标」;点击 emit('navigate') 由工程层执行)
|
|
694
|
+
"engineering.router-link": {
|
|
695
|
+
"vue-dom": "a.proteus-router-link",
|
|
696
|
+
"native-ios": "UIButton.link",
|
|
697
|
+
"native-android": "TextView.link",
|
|
698
|
+
"native-harmony": "Text.link",
|
|
699
|
+
skyline: "navigator",
|
|
700
|
+
// Skyline <navigator> 声明式导航
|
|
701
|
+
flutter: "TextButton",
|
|
702
|
+
headless: "router-link"
|
|
703
|
+
},
|
|
704
|
+
// ★G-32 B5 续二:工程原语动画组件形态(E19 transition / E20 animate——纯 CSS 声明语义;Hook 归 API 层不产节点)
|
|
705
|
+
"engineering.transition": {
|
|
706
|
+
"vue-dom": "div.proteus-transition",
|
|
707
|
+
"native-ios": "UIView.transition",
|
|
708
|
+
// UIView transition(with:duration:options:) 显隐过渡
|
|
709
|
+
"native-android": "View.animate.transition",
|
|
710
|
+
// ObjectAnimator alpha/translation 组合
|
|
711
|
+
"native-harmony": "animateTo.transition",
|
|
712
|
+
// ArkUI animateTo 显隐过渡
|
|
713
|
+
skyline: "view.transition",
|
|
714
|
+
// Skyline transition CSS
|
|
715
|
+
flutter: "AnimatedOpacity",
|
|
716
|
+
headless: "transition"
|
|
717
|
+
},
|
|
718
|
+
"engineering.animate": {
|
|
719
|
+
"vue-dom": "div.proteus-animate",
|
|
720
|
+
"native-ios": "CAKeyframeAnimation",
|
|
721
|
+
// 核心动画关键帧
|
|
722
|
+
"native-android": "ValueAnimator",
|
|
723
|
+
// 属性动画
|
|
724
|
+
"native-harmony": "Animator.transition",
|
|
725
|
+
// ArkUI Animator
|
|
726
|
+
skyline: "view.animation",
|
|
727
|
+
// Skyline animation CSS
|
|
728
|
+
flutter: "AnimationController",
|
|
729
|
+
headless: "animate"
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
function mapSemanticToBackend(semantic, backendId) {
|
|
733
|
+
const row = SEMANTIC_BACKEND_MAP[semantic];
|
|
734
|
+
if (!row) return null;
|
|
735
|
+
return row[backendId] ?? null;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// src/to-ir.ts
|
|
739
|
+
function toComponentIR(tag, props = {}, children = []) {
|
|
740
|
+
if (!tag.startsWith("p-")) return null;
|
|
741
|
+
const semantic = TAG_SEMANTIC_MAP[tag];
|
|
742
|
+
if (!semantic) return null;
|
|
743
|
+
return { tag, semantic, props: { ...props }, children };
|
|
744
|
+
}
|
|
745
|
+
function toComponentTree(tag, props = {}, children = []) {
|
|
746
|
+
const irChildren = [];
|
|
747
|
+
for (const c of children) {
|
|
748
|
+
const sub = toComponentTree(c.tag, c.props, c.children ?? []);
|
|
749
|
+
if (sub) irChildren.push(sub);
|
|
750
|
+
}
|
|
751
|
+
return toComponentIR(tag, props, irChildren);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// src/primitives.ts
|
|
755
|
+
var LAYOUT = [
|
|
756
|
+
{ id: "L1", kind: "layout", semantic: "layout.box", tag: "p-box", props: ["aspectRatio", "overflow"], mpEquiv: "<view>", tier: "L1", status: "implemented" },
|
|
757
|
+
{ id: "L2", kind: "layout", semantic: "layout.inline", tag: "p-inline", props: ["wrap"], mpEquiv: "<text> \u5185\u8054", tier: "L1", status: "implemented" },
|
|
758
|
+
{ id: "L3", kind: "layout", semantic: "layout.stack", tag: "p-stack", props: ["direction", "gap", "align", "wrap", "snap", "loop"], mpEquiv: "flex + scroll-view + swiper", tier: "L1", status: "implemented" },
|
|
759
|
+
{ id: "L4", kind: "layout", semantic: "layout.grid", tag: "p-grid", props: ["minColWidth", "maxCols", "gap", "autoFlow"], mpEquiv: "<view> + CSS Grid", tier: "L1", status: "implemented" },
|
|
760
|
+
{ id: "L5", kind: "layout", semantic: "layout.fluid", tag: "p-fluid", props: ["breakpoints", "minItemWidth"], mpEquiv: "\u54CD\u5E94\u5F0F CSS", tier: "L1", status: "implemented" },
|
|
761
|
+
{ id: "L6", kind: "layout", semantic: "layout.adaptive", tag: "p-adaptive", props: ["sheet", "dialog", "popover", "drawer"], mpEquiv: "\u65E0\uFF08\u5BB9\u5668\u5BBD\u5EA6\u8BED\u4E49\u65AD\u70B9\uFF09", tier: "L1", status: "implemented" },
|
|
762
|
+
{ id: "L7", kind: "layout", semantic: "layout.fit", tag: "p-fit", props: ["mode"], mpEquiv: "fit-content", tier: "L1", status: "implemented" },
|
|
763
|
+
{ id: "L8", kind: "layout", semantic: "layout.spacer", tag: "p-spacer", props: ["grow", "shrink"], mpEquiv: "flex:1", tier: "L1", status: "implemented" },
|
|
764
|
+
{ id: "L9", kind: "layout", semantic: "layout.divider", tag: "p-divider", props: ["orientation", "inset"], mpEquiv: "<view> + border", tier: "L1", status: "implemented" },
|
|
765
|
+
{ id: "L10", kind: "layout", semantic: "layout.scroll", tag: "p-scroll", props: ["axis", "paging", "refresh", "indicator"], mpEquiv: "<scroll-view>", tier: "L1", status: "implemented" },
|
|
766
|
+
{ id: "L11", kind: "layout", semantic: "layout.virtual-list", tag: "p-virtual-list", props: ["itemSize", "buffer", "direction"], mpEquiv: "<scroll-view> + \u624B\u52A8\u56DE\u6536", tier: "L1", status: "implemented" },
|
|
767
|
+
{ id: "L12", kind: "layout", semantic: "layout.masonry", tag: "p-masonry", props: ["colCount", "gap"], mpEquiv: "\u7B2C\u4E09\u65B9\u7011\u5E03\u6D41", tier: "L1", status: "implemented" }
|
|
768
|
+
];
|
|
769
|
+
var UI = [
|
|
770
|
+
{ id: "U1", kind: "ui", semantic: "ui.text", tag: "p-text", props: ["content", "selectable", "truncate", "align"], mpEquiv: "<text>", tier: "L1", status: "implemented" },
|
|
771
|
+
{ id: "U2", kind: "ui", semantic: "ui.heading", tag: "p-heading", props: ["level"], mpEquiv: "<h1>-<h6>", tier: "L1", status: "implemented" },
|
|
772
|
+
{ id: "U3", kind: "ui", semantic: "ui.rich-text", tag: "p-rich-text", props: ["source", "schema"], mpEquiv: "<rich-text>", tier: "L1", status: "implemented" },
|
|
773
|
+
{ id: "U4", kind: "ui", semantic: "ui.icon", tag: "p-icon", props: ["name", "size", "color", "spin"], mpEquiv: "<icon>", tier: "L1", status: "implemented" },
|
|
774
|
+
{ id: "U5", kind: "ui", semantic: "ui.image", tag: "p-image", props: ["src", "fit", "placeholder", "lazy"], mpEquiv: "<image>", tier: "L1", status: "implemented" },
|
|
775
|
+
{ id: "U6", kind: "ui", semantic: "ui.avatar", tag: "p-avatar", props: ["src", "shape", "size", "fallback"], mpEquiv: "\u7EC4\u5408", tier: "L1", status: "implemented" },
|
|
776
|
+
{ id: "U7", kind: "ui", semantic: "ui.media", tag: "p-media", props: ["kind", "controls", "autoplay", "poster", "loop", "muted", "pictureInPicture"], mpEquiv: "<video>+<audio>", tier: "L1", status: "implemented" },
|
|
777
|
+
{ id: "U8", kind: "ui", semantic: "ui.canvas", tag: "p-canvas", props: ["engine", "resolution"], mpEquiv: "<canvas>", tier: "L1", status: "implemented" },
|
|
778
|
+
{ id: "U9", kind: "ui", semantic: "ui.svg", tag: "p-svg", props: ["path", "viewbox"], mpEquiv: "\u65E0", tier: "L1", status: "implemented" },
|
|
779
|
+
{ id: "U10", kind: "ui", semantic: "ui.input", tag: "p-input", props: ["type", "mask", "validation", "clearable"], mpEquiv: "<input>", tier: "L1", status: "implemented" },
|
|
780
|
+
{ id: "U11", kind: "ui", semantic: "ui.textarea", tag: "p-textarea", props: ["autosize", "maxLength", "count"], mpEquiv: "<textarea>", tier: "L1", status: "implemented" },
|
|
781
|
+
{ id: "U12", kind: "ui", semantic: "ui.select", tag: "p-select", props: ["options", "multiple", "searchable", "cascader"], mpEquiv: "<picker> \u90E8\u5206", tier: "L1", status: "implemented" },
|
|
782
|
+
{ id: "U13", kind: "ui", semantic: "ui.checkbox", tag: "p-checkbox", props: ["checked", "indeterminate", "group"], mpEquiv: "<checkbox>", tier: "L1", status: "implemented" },
|
|
783
|
+
{ id: "U14", kind: "ui", semantic: "ui.radio", tag: "p-radio", props: ["value", "group"], mpEquiv: "<radio>", tier: "L1", status: "implemented" },
|
|
784
|
+
{ id: "U15", kind: "ui", semantic: "ui.switch", tag: "p-switch", props: ["checked", "loading"], mpEquiv: "<switch>", tier: "L1", status: "implemented" },
|
|
785
|
+
{ id: "U16", kind: "ui", semantic: "ui.slider", tag: "p-slider", props: ["min", "max", "step", "range"], mpEquiv: "<slider>", tier: "L1", status: "implemented" },
|
|
786
|
+
{ id: "U17", kind: "ui", semantic: "ui.picker", tag: "p-picker", props: ["mode", "start", "end"], mpEquiv: "<picker>", tier: "L1", status: "implemented" },
|
|
787
|
+
{ id: "U18", kind: "ui", semantic: "ui.form", tag: "p-form", props: ["model", "rules", "layout"], mpEquiv: "\u7EC4\u5408", tier: "L1", status: "implemented" }
|
|
788
|
+
];
|
|
789
|
+
var SHELL = [
|
|
790
|
+
{ id: "S1", kind: "shell", semantic: "shell.page", tag: "p-page", props: ["title", "statusBar", "pullRefresh"], mpEquiv: "<page>", tier: "L1", status: "implemented" },
|
|
791
|
+
{ id: "S2", kind: "shell", semantic: "shell.nav", tag: "p-nav", props: ["title", "transparent"], mpEquiv: "\u5BFC\u822A\u680F\u914D\u7F6E", tier: "L1", status: "implemented" },
|
|
792
|
+
{ id: "S3", kind: "shell", semantic: "shell.tabbar", tag: "p-tabbar", props: ["tabs", "active", "badge"], mpEquiv: "<tabbar>", tier: "L1", status: "implemented" },
|
|
793
|
+
{ id: "S4", kind: "shell", semantic: "shell.segment", tag: "p-segment", props: ["options", "active"], mpEquiv: "<segment>", tier: "L1", status: "implemented" },
|
|
794
|
+
{ id: "S5", kind: "shell", semantic: "shell.drawer", tag: "p-drawer", props: ["side", "width", "overlay"], mpEquiv: "\u7EC4\u5408", tier: "L1", status: "implemented" },
|
|
795
|
+
{ id: "S6", kind: "shell", semantic: "shell.modal", tag: "p-modal", props: ["open", "dismissible", "sheet", "dialog", "alert"], mpEquiv: "<modal>+wx.showModal", tier: "L1", status: "implemented" },
|
|
796
|
+
{ id: "S7", kind: "shell", semantic: "shell.popover", tag: "p-popover", props: ["trigger", "placement"], mpEquiv: "\u7EC4\u5408", tier: "L1", status: "implemented" },
|
|
797
|
+
{ id: "S8", kind: "shell", semantic: "shell.toast", tag: "p-toast", props: ["message", "duration", "type"], mpEquiv: "wx.showToast", tier: "L1", status: "planned" },
|
|
798
|
+
{ id: "S9", kind: "shell", semantic: "shell.action-sheet", tag: "p-action-sheet", props: ["actions", "cancel"], mpEquiv: "wx.showActionSheet", tier: "L1", status: "implemented" },
|
|
799
|
+
{ id: "S10", kind: "shell", semantic: "layout.split", tag: "p-split", props: ["breakpoint", "ratio", "collapse"], mpEquiv: "\u65E0\uFF08\u5206\u680F\u5E03\u5C40\uFF09", tier: "L1", status: "implemented" }
|
|
800
|
+
// ★已落地绑定:分栏语义由 layout.split 承载(G-32 文档为 shell.split——机器事实以实现为准)
|
|
801
|
+
];
|
|
802
|
+
var GESTURE = [
|
|
803
|
+
{ id: "G1", kind: "gesture", semantic: "gesture.tap", api: "v-gesture:tap", props: ["count"], mpEquiv: "bindtap", tier: "L1", status: "planned" },
|
|
804
|
+
{ id: "G2", kind: "gesture", semantic: "gesture.longpress", api: "v-gesture:longpress", props: ["duration", "onEnd"], mpEquiv: "bindlongpress", tier: "L1", status: "planned" },
|
|
805
|
+
{ id: "G3", kind: "gesture", semantic: "gesture.swipe", api: "v-gesture:swipe", props: ["direction", "threshold"], mpEquiv: "bindswipe", tier: "L1", status: "planned" },
|
|
806
|
+
{ id: "G4", kind: "gesture", semantic: "gesture.pan", api: "v-gesture:pan", props: ["axis", "bounds"], mpEquiv: "bindtouchmove", tier: "L1", status: "planned" },
|
|
807
|
+
{ id: "G5", kind: "gesture", semantic: "gesture.pinch", api: "v-gesture:pinch", props: ["scale", "onChange"], mpEquiv: "touchstart/move \u7EC4\u5408", tier: "L1", status: "planned" },
|
|
808
|
+
{ id: "G6", kind: "gesture", semantic: "gesture.rotate", api: "v-gesture:rotate", props: ["angle"], mpEquiv: "\u7EC4\u5408", tier: "L1", status: "planned" },
|
|
809
|
+
{ id: "G7", kind: "gesture", semantic: "gesture.press", api: "v-gesture:press", props: ["force"], mpEquiv: "3D Touch", tier: "L1", status: "planned" },
|
|
810
|
+
{ id: "G8", kind: "gesture", semantic: "gesture.draggable", tag: "p-draggable", props: ["ghost", "snapToGrid", "onDrop"], mpEquiv: "movable-view", tier: "L1", status: "implemented" },
|
|
811
|
+
{ id: "G9", kind: "gesture", semantic: "gesture.scrollable", tag: "p-scrollable", props: ["bounce", "refresh", "loadMore"], mpEquiv: "<scroll-view>", tier: "L1", status: "implemented" },
|
|
812
|
+
{ id: "G10", kind: "gesture", semantic: "gesture.use-gesture", api: "useGesture()", props: ["recognizers", "simultaneous"], mpEquiv: "\u65E0", tier: "L1", status: "planned" }
|
|
813
|
+
];
|
|
814
|
+
function cap(id, name, api, mpEquiv, returnType) {
|
|
815
|
+
return { id, kind: "capability", semantic: `capability.${name}`, api, props: [returnType], mpEquiv, tier: "L1", status: "planned" };
|
|
816
|
+
}
|
|
817
|
+
var CAPABILITY = [
|
|
818
|
+
// 7.1 设备/硬件(15)
|
|
819
|
+
cap("C1", "camera", "useCamera()", "wx.createCameraContext", "Result<Media>"),
|
|
820
|
+
cap("C2", "microphone", "useMicrophone()", "RecorderManager", "Result<AudioBuffer>"),
|
|
821
|
+
{ id: "C3", kind: "capability", semantic: "capability.location", api: "useLocation()", props: ["Result<Coords>"], mpEquiv: "wx.getLocation", tier: "L1", status: "implemented" },
|
|
822
|
+
cap("C4", "map", "useMap()", "wx.createMapContext", "MapController"),
|
|
823
|
+
cap("C5", "sensor", "useSensor()", "onAccelerometer/onCompass/onGyroscope", "SensorStream"),
|
|
824
|
+
cap("C6", "vibrate", "useVibrate()", "wx.vibrateShort/Long", "void"),
|
|
825
|
+
cap("C7", "battery", "useBattery()", "wx.getBatteryInfo", "BatteryInfo"),
|
|
826
|
+
cap("C8", "network", "useNetwork()", "wx.getNetworkType", "NetworkType"),
|
|
827
|
+
cap("C9", "clipboard", "useClipboard()", "wx.set/getClipboardData", "Result<string>"),
|
|
828
|
+
cap("C10", "screen", "useScreen()", "wx.getSystemInfo", "ScreenInfo"),
|
|
829
|
+
cap("C11", "device", "useDevice()", "wx.getSystemInfo", "DeviceInfo"),
|
|
830
|
+
cap("C12", "orientation", "useOrientation()", "wx.onDeviceOrientationChange", "Orientation"),
|
|
831
|
+
cap("C13", "brightness", "useBrightness()", "wx.setScreenBrightness", "Result<void>"),
|
|
832
|
+
cap("C14", "keyboard", "useKeyboard()", "wx.onKeyboardHeightChange", "KeyboardInfo"),
|
|
833
|
+
cap("C15", "storage", "useStorage()", "wx.set/getStorage", "StorageAPI"),
|
|
834
|
+
// 7.2 系统/OS(10)
|
|
835
|
+
cap("C16", "permission", "usePermission()", "wx.authorize", "Result<PermissionStatus>"),
|
|
836
|
+
cap("C17", "notification", "useNotification()", "wx.requestSubscribeMessage", "NotificationAPI"),
|
|
837
|
+
cap("C18", "share", "useShare()", "wx.shareAppMessage", "Result<void>"),
|
|
838
|
+
cap("C19", "contact", "useContact()", "wx.chooseContact", "Result<Contact[]>"),
|
|
839
|
+
cap("C20", "calendar", "useCalendar()", "wx.addPhoneCalendar", "CalendarAPI"),
|
|
840
|
+
cap("C21", "phone-call", "usePhoneCall()", "wx.makePhoneCall", "Result<void>"),
|
|
841
|
+
cap("C22", "sms", "useSMS()", "wx.??\uFF08\u53D7\u9650\uFF09", "Result<void>"),
|
|
842
|
+
cap("C23", "app-lifecycle", "useAppLifecycle()", "App.onLaunch/onShow", "LifecycleHooks"),
|
|
843
|
+
cap("C24", "page-lifecycle", "usePageLifecycle()", "Page.onLoad/onShow", "LifecycleHooks"),
|
|
844
|
+
cap("C25", "background", "useBackground()", "wx.onBackground", "BackgroundAPI"),
|
|
845
|
+
// 7.3 通信/数据(10)
|
|
846
|
+
cap("C26", "fetch", "useFetch()", "wx.request", "Promise<T>"),
|
|
847
|
+
cap("C27", "websocket", "useWebSocket()", "wx.connectSocket", "WSConnection"),
|
|
848
|
+
cap("C28", "socket-task", "useSocketTask()", "wx.SocketTask", "SocketTask"),
|
|
849
|
+
cap("C29", "upload", "useUpload()", "wx.uploadFile", "Progress<Result>"),
|
|
850
|
+
cap("C30", "download", "useDownload()", "wx.downloadFile", "Progress<Result>"),
|
|
851
|
+
cap("C31", "data-channel", "useDataChannel()", "wx...\uFF08\u76F4\u64AD/\u5B9E\u65F6\uFF09", "Channel"),
|
|
852
|
+
cap("C32", "cookie", "useCookie()", "\u65E0", "CookieJar"),
|
|
853
|
+
cap("C33", "auth", "useAuth()", "\u7EC4\u5408", "AuthState"),
|
|
854
|
+
cap("C34", "analytics", "useAnalytics()", "wx.reportEvent", "TrackAPI"),
|
|
855
|
+
cap("C35", "log", "useLog()", "console + \u4E0A\u62A5", "Logger"),
|
|
856
|
+
// 7.4 扩展能力(10)
|
|
857
|
+
cap("C36", "bluetooth", "useBluetooth()", "wx.openBluetoothAdapter", "BluetoothAPI"),
|
|
858
|
+
cap("C37", "nfc", "useNFC()", "wx.getHCEState", "NFCAPI"),
|
|
859
|
+
cap("C38", "biometric", "useBiometric()", "wx.checkIsSupportFingerPrint", "Result<boolean>"),
|
|
860
|
+
cap("C39", "face-id", "useFaceID()", "\u7EC4\u5408", "Result<boolean>"),
|
|
861
|
+
cap("C40", "payment", "usePayment()", "wx.requestPayment", "Result<PayResult>"),
|
|
862
|
+
cap("C41", "login", "useLogin()", "wx.login", "Result<Token>"),
|
|
863
|
+
cap("C42", "qr-code", "useQRCode()", "wx.scanCode + canvas", "Result<string>"),
|
|
864
|
+
cap("C43", "file-system", "useFileSystem()", "wx.getFileSystemManager", "FSAdapter"),
|
|
865
|
+
cap("C44", "archive", "useArchive()", "wx.compressFile", "Result<void>"),
|
|
866
|
+
cap("C45", "shortcut", "useShortcut()", "wx.addToDesktop", "Result<void>"),
|
|
867
|
+
cap("C46", "in-app-purchase", "useInAppPurchase()", "wx.requestPayment \u6269\u5C55", "Result<Receipt>"),
|
|
868
|
+
// 7.5 平台特有(4——C47-C50,此前 7.4 末 C46,总 50)
|
|
869
|
+
{ id: "C47", kind: "capability", semantic: "capability.mini-program", api: "useMiniProgram()", props: ["MPContext"], mpEquiv: "wx.navigateToMiniProgram", tier: "L1", status: "planned" },
|
|
870
|
+
{ id: "C48", kind: "capability", semantic: "capability.embedded", api: "useEmbedded()", props: ["HostContext"], mpEquiv: "\u65E0\uFF08\u88AB\u5BBF\u4E3B\u5D4C\u5165\uFF09", tier: "L1", status: "planned" },
|
|
871
|
+
{ id: "C49", kind: "capability", semantic: "capability.live", api: "useLive()", props: ["LiveRoom"], mpEquiv: "wx...\uFF08\u76F4\u64AD\u7EC4\u4EF6\uFF09", tier: "L1", status: "planned" },
|
|
872
|
+
{ id: "C50", kind: "capability", semantic: "capability.extension", api: "useExtension()", props: ["ExtensionAPI"], mpEquiv: "\u65E0\uFF08\u63D2\u4EF6/\u6269\u5C55\u70B9 G-21\uFF09", tier: "L1", status: "planned" }
|
|
873
|
+
];
|
|
874
|
+
var ENGINEERING = [
|
|
875
|
+
{ id: "E1", kind: "engineering", semantic: "engineering.state", api: "useState()", mpEquiv: "data", tier: "L1", status: "planned" },
|
|
876
|
+
{ id: "E2", kind: "engineering", semantic: "engineering.computed", api: "useComputed()", mpEquiv: "computed", tier: "L1", status: "planned" },
|
|
877
|
+
{ id: "E3", kind: "engineering", semantic: "engineering.watch", api: "useWatch()", mpEquiv: "watch", tier: "L1", status: "planned" },
|
|
878
|
+
{ id: "E4", kind: "engineering", semantic: "engineering.store", api: "useStore()", mpEquiv: "getApp().globalData", tier: "L1", status: "planned" },
|
|
879
|
+
{ id: "E5", kind: "engineering", semantic: "engineering.provide-inject", api: "useProvide()/useInject()", mpEquiv: "\u65E0", tier: "L1", status: "planned" },
|
|
880
|
+
{ id: "E6", kind: "engineering", semantic: "engineering.lifecycle", api: "useLifecycle()", mpEquiv: "onLoad/onShow/onHide/onUnload", tier: "L1", status: "planned" },
|
|
881
|
+
{ id: "E7", kind: "engineering", semantic: "engineering.ready", api: "useReady()", mpEquiv: "onReady", tier: "L1", status: "planned" },
|
|
882
|
+
{ id: "E8", kind: "engineering", semantic: "engineering.error-boundary", api: "useErrorBoundary()", mpEquiv: "onError", tier: "L1", status: "planned" },
|
|
883
|
+
{ id: "E9", kind: "engineering", semantic: "engineering.page-param", api: "usePageParam()", mpEquiv: "onLoad(options)", tier: "L1", status: "planned" },
|
|
884
|
+
{ id: "E10", kind: "engineering", semantic: "engineering.route", api: "useRoute()", mpEquiv: "getCurrentPages()", tier: "L1", status: "planned" },
|
|
885
|
+
{ id: "E11", kind: "engineering", semantic: "engineering.router-push", api: "router.push()", mpEquiv: "wx.navigateTo", tier: "L1", status: "planned" },
|
|
886
|
+
{ id: "E12", kind: "engineering", semantic: "engineering.router-replace", api: "router.replace()", mpEquiv: "wx.redirectTo", tier: "L1", status: "planned" },
|
|
887
|
+
{ id: "E13", kind: "engineering", semantic: "engineering.router-back", api: "router.back()", mpEquiv: "wx.navigateBack", tier: "L1", status: "planned" },
|
|
888
|
+
{ id: "E14", kind: "engineering", semantic: "engineering.router-switch-tab", api: "router.switchTab()", mpEquiv: "wx.switchTab", tier: "L1", status: "planned" },
|
|
889
|
+
{ id: "E15", kind: "engineering", semantic: "engineering.router-relaunch", api: "router.reLaunch()", mpEquiv: "wx.reLaunch", tier: "L1", status: "planned" },
|
|
890
|
+
{ id: "E16", kind: "engineering", semantic: "engineering.router-before-each", api: "router.beforeEach()", mpEquiv: "onLaunch \u624B\u52A8", tier: "L1", status: "planned" },
|
|
891
|
+
{ id: "E17", kind: "engineering", semantic: "engineering.router-after-each", api: "router.afterEach()", mpEquiv: "\u65E0", tier: "L1", status: "planned" },
|
|
892
|
+
{ id: "E18", kind: "engineering", semantic: "engineering.router-link", tag: "p-router-link", props: ["to", "replace", "switchTab"], mpEquiv: "<navigator>", tier: "L1", status: "implemented" },
|
|
893
|
+
{ id: "E19", kind: "engineering", semantic: "engineering.transition", tag: "p-transition", props: ["name", "mode"], mpEquiv: "transition CSS", tier: "L1", status: "implemented" },
|
|
894
|
+
{ id: "E20", kind: "engineering", semantic: "engineering.animate", tag: "p-animate", props: ["keyframes", "duration"], mpEquiv: "animation CSS", tier: "L1", status: "implemented" },
|
|
895
|
+
{ id: "E21", kind: "engineering", semantic: "engineering.animation", api: "useAnimation()", mpEquiv: "wx.createAnimation", tier: "L1", status: "planned" },
|
|
896
|
+
{ id: "E22", kind: "engineering", semantic: "engineering.gesture-animation", api: "useGestureAnimation()", mpEquiv: "\u7EC4\u5408", tier: "L1", status: "planned" },
|
|
897
|
+
{ id: "E23", kind: "engineering", semantic: "engineering.scroll-animation", api: "useScrollAnimation()", mpEquiv: "\u7EC4\u5408", tier: "L1", status: "planned" },
|
|
898
|
+
{ id: "E24", kind: "engineering", semantic: "engineering.devtools", api: "useDevTools()", mpEquiv: "\u5C0F\u7A0B\u5E8F DevTools", tier: "L1", status: "planned" },
|
|
899
|
+
{ id: "E25", kind: "engineering", semantic: "engineering.inspector", api: "useInspector()", mpEquiv: "\u65E0", tier: "L1", status: "planned" },
|
|
900
|
+
{ id: "E26", kind: "engineering", semantic: "engineering.performance", api: "usePerformance()", mpEquiv: "wx.reportPerformance", tier: "L1", status: "planned" },
|
|
901
|
+
{ id: "E27", kind: "engineering", semantic: "engineering.define-component", api: "defineComponent()", mpEquiv: "Component()", tier: "L1", status: "planned" },
|
|
902
|
+
{ id: "E28", kind: "engineering", semantic: "engineering.define-capability", api: "defineCapability()", mpEquiv: "\u65E0", tier: "L1", status: "planned" }
|
|
903
|
+
];
|
|
904
|
+
var PRIMITIVE_CATALOG = [...LAYOUT, ...UI, ...SHELL, ...GESTURE, ...CAPABILITY, ...ENGINEERING];
|
|
905
|
+
function componentPrimitives(kinds = ["layout", "ui", "shell", "gesture", "engineering"]) {
|
|
906
|
+
return PRIMITIVE_CATALOG.filter((p) => p.tag && kinds.includes(p.kind));
|
|
907
|
+
}
|
|
908
|
+
function implementedPrimitives() {
|
|
909
|
+
return PRIMITIVE_CATALOG.filter((p) => p.status === "implemented");
|
|
910
|
+
}
|
|
911
|
+
function primitiveById(id) {
|
|
912
|
+
return PRIMITIVE_CATALOG.find((p) => p.id === id);
|
|
913
|
+
}
|
|
914
|
+
function primitiveBySemantic(semantic) {
|
|
915
|
+
return PRIMITIVE_CATALOG.find((p) => p.semantic === semantic);
|
|
916
|
+
}
|
|
917
|
+
function primitiveByTag(tag) {
|
|
918
|
+
return PRIMITIVE_CATALOG.find((p) => p.tag === tag);
|
|
919
|
+
}
|
|
920
|
+
function checkPrimitiveCatalog() {
|
|
921
|
+
const errors = [];
|
|
922
|
+
if (PRIMITIVE_CATALOG.length !== 128) errors.push(`\u6E05\u5355\u957F\u5EA6 ${PRIMITIVE_CATALOG.length} \u2260 128`);
|
|
923
|
+
const ids = new Set(PRIMITIVE_CATALOG.map((p) => p.id));
|
|
924
|
+
if (ids.size !== PRIMITIVE_CATALOG.length) errors.push("id \u91CD\u590D");
|
|
925
|
+
const sems = new Set(PRIMITIVE_CATALOG.map((p) => p.semantic));
|
|
926
|
+
if (sems.size !== PRIMITIVE_CATALOG.length) errors.push("semantic \u91CD\u590D");
|
|
927
|
+
const tags = PRIMITIVE_CATALOG.filter((p) => p.tag).map((p) => p.tag);
|
|
928
|
+
if (new Set(tags).size !== tags.length) errors.push("tag \u91CD\u590D");
|
|
929
|
+
return errors;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// src/conformance.ts
|
|
933
|
+
function extractSemanticTree(snap) {
|
|
934
|
+
return { semantic: snap.semantic, children: snap.children.map(extractSemanticTree) };
|
|
935
|
+
}
|
|
936
|
+
function checkComponentSnapshot(backendId, snapshot) {
|
|
937
|
+
const errors = [];
|
|
938
|
+
const unverified = [];
|
|
939
|
+
let nodes = 0;
|
|
940
|
+
function visit(snap, path) {
|
|
941
|
+
nodes++;
|
|
942
|
+
if (snap.semantic !== snap.type) {
|
|
943
|
+
const row = SEMANTIC_BACKEND_MAP[snap.semantic];
|
|
944
|
+
if (!row) {
|
|
945
|
+
unverified.push({ path, semantic: snap.semantic, expected: null, actual: snap.control, reason: "no-row" });
|
|
946
|
+
} else {
|
|
947
|
+
const mapped = row[backendId];
|
|
948
|
+
if (mapped === void 0) {
|
|
949
|
+
unverified.push({ path, semantic: snap.semantic, expected: null, actual: snap.control, reason: "no-column" });
|
|
950
|
+
} else if (snap.control !== mapped) {
|
|
951
|
+
errors.push({ path, semantic: snap.semantic, expected: mapped, actual: snap.control, reason: "mismatch" });
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
} else {
|
|
955
|
+
unverified.push({ path, semantic: snap.semantic, expected: null, actual: snap.control, reason: "compat" });
|
|
956
|
+
}
|
|
957
|
+
snap.children.forEach((c, i) => visit(c, path ? `${path}/${i}` : String(i)));
|
|
958
|
+
}
|
|
959
|
+
visit(snapshot, "");
|
|
960
|
+
return { ok: errors.length === 0, backendId, nodes, errors, unverified };
|
|
961
|
+
}
|
|
962
|
+
function checkSemanticCoverage(minBackends = 3) {
|
|
963
|
+
const gaps = [];
|
|
964
|
+
for (const p of implementedPrimitives()) {
|
|
965
|
+
const row = SEMANTIC_BACKEND_MAP[p.semantic];
|
|
966
|
+
const backends = row ? Object.keys(row) : [];
|
|
967
|
+
if (backends.length < minBackends) gaps.push({ semantic: p.semantic, backends });
|
|
968
|
+
}
|
|
969
|
+
return gaps;
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
// src/audit.ts
|
|
973
|
+
var MP_COMPONENTS = [
|
|
974
|
+
{ mp: "<view>", proteus: "layout.box / layout.stack", status: "ok", group: "component" },
|
|
975
|
+
{ mp: "<text>", proteus: "ui.text / ui.heading", status: "ok", group: "component" },
|
|
976
|
+
{ mp: "<image>", proteus: "ui.image", status: "ok", group: "component" },
|
|
977
|
+
{ mp: "<scroll-view>", proteus: "layout.scroll / layout.virtual-list", status: "ok", group: "component" },
|
|
978
|
+
{ mp: "<swiper>", proteus: "layout.stack snap/loop\uFF08\u6D88\u706D\u4E3A\u5C5E\u6027\uFF09", status: "ok", group: "component" },
|
|
979
|
+
{ mp: "<swiper-item>", proteus: "layout.stack \u5B50\u9879", status: "ok", group: "component" },
|
|
980
|
+
{ mp: "<movable-area>", proteus: "gesture.scrollable \u5BB9\u5668", status: "ok", group: "component" },
|
|
981
|
+
{ mp: "<movable-view>", proteus: "gesture.draggable", status: "ok", group: "component" },
|
|
982
|
+
{ mp: "<cover-view>", proteus: "p-overlay (L2)", status: "compat", group: "component" },
|
|
983
|
+
{ mp: "<cover-image>", proteus: "p-overlay + ui.image", status: "compat", group: "component" },
|
|
984
|
+
{ mp: "<icon>", proteus: "ui.icon", status: "ok", group: "component" },
|
|
985
|
+
{ mp: "<progress>", proteus: "p-progress (L2)", status: "compat", group: "component" },
|
|
986
|
+
{ mp: "<rich-text>", proteus: "ui.rich-text", status: "ok", group: "component" },
|
|
987
|
+
{ mp: "<button>", proteus: "ui.button", status: "ok", group: "component" },
|
|
988
|
+
{ mp: "<form>", proteus: "ui.form", status: "ok", group: "component" },
|
|
989
|
+
{ mp: "<input>", proteus: "ui.input", status: "ok", group: "component" },
|
|
990
|
+
{ mp: "<textarea>", proteus: "ui.textarea", status: "ok", group: "component" },
|
|
991
|
+
{ mp: "<checkbox>", proteus: "ui.checkbox", status: "ok", group: "component" },
|
|
992
|
+
{ mp: "<radio>", proteus: "ui.radio", status: "ok", group: "component" },
|
|
993
|
+
{ mp: "<picker>", proteus: "ui.picker / ui.select", status: "ok", group: "component" },
|
|
994
|
+
{ mp: "<picker-view>", proteus: "ui.picker mode=wheel", status: "ok", group: "component" },
|
|
995
|
+
{ mp: "<slider>", proteus: "ui.slider", status: "ok", group: "component" },
|
|
996
|
+
{ mp: "<switch>", proteus: "ui.switch", status: "ok", group: "component" },
|
|
997
|
+
{ mp: "<label>", proteus: "p-label (L2)", status: "compat", group: "component" },
|
|
998
|
+
{ mp: "<navigator>", proteus: "engineering.router-link / router.*", status: "ok", group: "component" },
|
|
999
|
+
{ mp: "<audio>", proteus: "ui.media kind=audio\uFF08\u6D88\u706D\u4E3A\u5C5E\u6027\uFF09", status: "ok", group: "component" },
|
|
1000
|
+
{ mp: "<video>", proteus: "ui.media kind=video\uFF08\u6D88\u706D\u4E3A\u5C5E\u6027\uFF09", status: "ok", group: "component" },
|
|
1001
|
+
{ mp: "<camera>", proteus: "p-camera (L2) + capability.camera", status: "ok", group: "component" },
|
|
1002
|
+
{ mp: "<live-player>", proteus: "ui.media kind=live\uFF08\u6D88\u706D\u4E3A\u5C5E\u6027\uFF09", status: "ok", group: "component" },
|
|
1003
|
+
{ mp: "<live-pusher>", proteus: "ui.media kind=live mode=push", status: "ok", group: "component" },
|
|
1004
|
+
{ mp: "<canvas>", proteus: "ui.canvas", status: "ok", group: "component" },
|
|
1005
|
+
{ mp: "<map>", proteus: "p-map (L2) + capability.map", status: "ok", group: "component" },
|
|
1006
|
+
{ mp: "<web-view>", proteus: "p-webview (L2)", status: "compat", group: "component" },
|
|
1007
|
+
{ mp: "<editor>", proteus: "ui.rich-text editable", status: "ok", group: "component" },
|
|
1008
|
+
{ mp: "<ad>", proteus: "p-ad (L2)", status: "compat", group: "component" },
|
|
1009
|
+
{ mp: "<official-account>", proteus: "capability.mini-program\uFF08\u5FAE\u4FE1\u79C1\u6709\uFF09", status: "private", group: "component" },
|
|
1010
|
+
{ mp: "<open-data>", proteus: "capability.mini-program\uFF08\u5FAE\u4FE1\u79C1\u6709\uFF09", status: "private", group: "component" },
|
|
1011
|
+
{ mp: "<share-element>", proteus: "p-share-element (L2)", status: "compat", group: "component" },
|
|
1012
|
+
{ mp: "<aria-component>", proteus: "p-aria (L2)", status: "compat", group: "component" },
|
|
1013
|
+
{ mp: "<page-container>", proteus: "p-page-container (L2)", status: "compat", group: "component" },
|
|
1014
|
+
{ mp: "<voip-room>", proteus: "capability.mini-program\uFF08\u5FAE\u4FE1 VOIP\uFF09", status: "private", group: "component" },
|
|
1015
|
+
{ mp: "<guild-room>", proteus: "capability.mini-program\uFF08\u5FAE\u4FE1\u6E38\u620F\uFF09", status: "private", group: "component" }
|
|
1016
|
+
];
|
|
1017
|
+
var MP_API_GROUPS = [
|
|
1018
|
+
{ mp: "wx.request/upload/download/websocket\uFF08\u7F51\u7EDC\uFF09", proteus: "capability.fetch/upload/download/websocket", status: "ok", group: "api" },
|
|
1019
|
+
{ mp: "wx.requestPayment", proteus: "capability.payment", status: "ok", group: "api" },
|
|
1020
|
+
{ mp: "wx.chooseImage/chooseMedia/previewImage\uFF08\u5A92\u4F53\uFF09", proteus: "capability.camera + ui.image preview", status: "ok", group: "api" },
|
|
1021
|
+
{ mp: "wx.startRecord/RecorderManager\uFF08\u5F55\u97F3\uFF09", proteus: "capability.microphone", status: "ok", group: "api" },
|
|
1022
|
+
{ mp: "wx.createVideoContext/CameraContext", proteus: "capability.media + ui.media", status: "ok", group: "api" },
|
|
1023
|
+
{ mp: "wx.scanCode", proteus: "capability.qr-code / capability.scan-qr", status: "ok", group: "api" },
|
|
1024
|
+
{ mp: "wx.saveImageToPhotosAlbum", proteus: "capability.album (L2)", status: "compat", group: "api" },
|
|
1025
|
+
{ mp: "wx.getFileSystemManager/*\uFF08\u6587\u4EF6 30+\uFF09", proteus: "capability.file-system", status: "ok", group: "api" },
|
|
1026
|
+
{ mp: "wx.compressFile/unzip", proteus: "capability.archive", status: "ok", group: "api" },
|
|
1027
|
+
{ mp: "wx.set/get/remove/clearStorage(+Sync)", proteus: "capability.storage", status: "ok", group: "api" },
|
|
1028
|
+
{ mp: "wx.getLocation/chooseLocation/openLocation", proteus: "capability.location", status: "ok", group: "api" },
|
|
1029
|
+
{ mp: "wx.createMapContext", proteus: "capability.map", status: "ok", group: "api" },
|
|
1030
|
+
{ mp: "wx.getSystemInfo\uFF08\u8BBE\u5907/\u5C4F\u5E55/\u7F51\u7EDC/\u7535\u91CF/\u4EAE\u5EA6/\u65B9\u5411/\u9707\u52A8/\u4F20\u611F\u5668/\u526A\u8D34\u677F/\u7535\u8BDD\uFF09", proteus: "capability.device/screen/network/battery/brightness/orientation/vibrate/sensor/clipboard/phone-call", status: "ok", group: "api" },
|
|
1031
|
+
{ mp: "wx.openBluetoothAdapter\uFF08\u84DD\u7259 20+\uFF09", proteus: "capability.bluetooth", status: "ok", group: "api" },
|
|
1032
|
+
{ mp: "wx.getHCEState\uFF08NFC\uFF09", proteus: "capability.nfc", status: "ok", group: "api" },
|
|
1033
|
+
{ mp: "wx.checkIsSupportFingerPrint/FaceID", proteus: "capability.biometric / face-id", status: "ok", group: "api" },
|
|
1034
|
+
{ mp: "wx.showToast/showLoading/showModal/showActionSheet", proteus: "shell.toast + capability.toast/loading + shell.modal + shell.action-sheet", status: "ok", group: "api" },
|
|
1035
|
+
{ mp: "wx.setNavigationBarTitle/Color", proteus: "shell.nav", status: "ok", group: "api" },
|
|
1036
|
+
{ mp: "wx.setTabBarItem/Style/hide/show", proteus: "shell.tabbar", status: "ok", group: "api" },
|
|
1037
|
+
{ mp: "wx.pageScrollTo", proteus: "capability.page-scroll", status: "ok", group: "api" },
|
|
1038
|
+
{ mp: "wx.createAnimation", proteus: "engineering.animation", status: "ok", group: "api" },
|
|
1039
|
+
{ mp: "wx.createSelectorQuery/IntersectionObserver", proteus: "capability.element / intersection", status: "ok", group: "api" },
|
|
1040
|
+
{ mp: "wx.navigateTo/redirectTo/navigateBack/switchTab/reLaunch", proteus: "engineering.router-push/replace/back/switch-tab/relaunch", status: "ok", group: "api" },
|
|
1041
|
+
{ mp: "wx.getCurrentPages", proteus: "engineering.route", status: "ok", group: "api" },
|
|
1042
|
+
{ mp: "App()/Page() \u751F\u547D\u5468\u671F/getApp()", proteus: "engineering.lifecycle + capability.app-lifecycle/page-lifecycle", status: "ok", group: "api" },
|
|
1043
|
+
{ mp: "wx.shareAppMessage/requestSubscribeMessage", proteus: "capability.share / notification", status: "ok", group: "api" },
|
|
1044
|
+
{ mp: "wx.login/checkSession/getUserInfo/authorize", proteus: "capability.login / auth / permission", status: "ok", group: "api" },
|
|
1045
|
+
{ mp: "wx.getUpdateManager", proteus: "capability.update (L2)", status: "compat", group: "api" },
|
|
1046
|
+
{ mp: "wx.requestWeChatPay/navigateToMiniProgram/\u6A21\u677F\u6D88\u606F/\u5BA2\u670D\uFF08\u5FAE\u4FE1\u79C1\u6709\uFF09", proteus: "capability.mini-program", status: "private", group: "api" }
|
|
1047
|
+
];
|
|
1048
|
+
var MP_MAPPING_MATRIX = [...MP_COMPONENTS, ...MP_API_GROUPS];
|
|
1049
|
+
function auditMiniprogramCoverage(matrix = MP_MAPPING_MATRIX) {
|
|
1050
|
+
const missing = matrix.filter((i) => i.status === "missing");
|
|
1051
|
+
const missingItems = missing;
|
|
1052
|
+
const ok = matrix.filter((i) => i.status === "ok").length;
|
|
1053
|
+
const compat = matrix.filter((i) => i.status === "compat").length;
|
|
1054
|
+
const priv = matrix.filter((i) => i.status === "private").length;
|
|
1055
|
+
const total = matrix.length;
|
|
1056
|
+
return {
|
|
1057
|
+
total,
|
|
1058
|
+
ok,
|
|
1059
|
+
compat,
|
|
1060
|
+
private: priv,
|
|
1061
|
+
missing: missingItems.length,
|
|
1062
|
+
percent: total === 0 ? 0 : Math.round((total - missingItems.length) / total * 100),
|
|
1063
|
+
pass: missingItems.length === 0,
|
|
1064
|
+
missingItems
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
function auditCatalogConsistency() {
|
|
1068
|
+
const issues = [];
|
|
1069
|
+
const enumSet = new Set(SEMANTIC_ENUM);
|
|
1070
|
+
const catalogTags = PRIMITIVE_CATALOG.filter((p) => p.tag);
|
|
1071
|
+
for (const p of catalogTags) {
|
|
1072
|
+
const tag = p.tag;
|
|
1073
|
+
if (TAG_SEMANTIC_MAP[tag] !== p.semantic) {
|
|
1074
|
+
issues.push({ rule: "C1", detail: `catalog ${p.id} (${tag}) \u8BED\u4E49=${p.semantic} \u2260 TAG_SEMANTIC_MAP=${TAG_SEMANTIC_MAP[tag] ?? "<\u672A\u767B\u8BB0>"}` });
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
for (const p of catalogTags) {
|
|
1078
|
+
if (!enumSet.has(p.semantic)) {
|
|
1079
|
+
issues.push({ rule: "C2", detail: `catalog ${p.id} \u8BED\u4E49 ${p.semantic} \u4E0D\u5728 SEMANTIC_ENUM` });
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
for (const [tag, sem] of Object.entries(TAG_SEMANTIC_MAP)) {
|
|
1083
|
+
if (!enumSet.has(sem)) {
|
|
1084
|
+
issues.push({ rule: "C3", detail: `${tag} \u2192 ${sem}\uFF08TAG_SEMANTIC_MAP \u503C\u4E0D\u5728 SEMANTIC_ENUM\uFF09` });
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
for (const p of PRIMITIVE_CATALOG.filter((x) => x.status === "implemented")) {
|
|
1088
|
+
if (!SEMANTIC_BACKEND_MAP[p.semantic]) {
|
|
1089
|
+
issues.push({ rule: "C4", detail: `${p.id} ${p.semantic}\uFF08implemented \u4F46 SEMANTIC_BACKEND_MAP \u65E0\u884C\uFF09` });
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
for (const sem of enumSet) {
|
|
1093
|
+
if (!SEMANTIC_BACKEND_MAP[sem]) {
|
|
1094
|
+
const inCatalog = PRIMITIVE_CATALOG.some((p) => p.semantic === sem);
|
|
1095
|
+
if (!inCatalog) {
|
|
1096
|
+
issues.push({ rule: "C5", detail: `${sem} \u5728 SEMANTIC_ENUM \u4F46\u4E0D\u5728 catalog\uFF08\u5B64\u7ACB\u8BED\u4E49\uFF09` });
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
return issues;
|
|
1101
|
+
}
|
|
1102
|
+
function formatCoverageReport(report) {
|
|
1103
|
+
const lines = [
|
|
1104
|
+
`G-32.1 \u5C0F\u7A0B\u5E8F\u80FD\u529B\u8986\u76D6\u5BA1\u8BA1\uFF1A`,
|
|
1105
|
+
` \u603B\u8BA1 ${report.total} \xB7 \u2705 L1 ${report.ok}\uFF08${Math.round(report.ok / report.total * 100)}%\uFF09\xB7 \u{1F504} L2/compat ${report.compat} \xB7 \u2B1B \u79C1\u6709 ${report.private} \xB7 \u274C \u7F3A\u5931 ${report.missing}`,
|
|
1106
|
+
` \u8986\u76D6\u7387 ${report.percent}% ${report.pass ? "\u2705 \u8FBE\u6807\uFF080 \u7F3A\u5931\uFF09" : "\u274C \u672A\u8FBE\u6807\uFF08CI \u7EA2\uFF09"}`
|
|
1107
|
+
];
|
|
1108
|
+
if (report.missingItems.length) {
|
|
1109
|
+
lines.push(" \u7F3A\u5931\u9879\uFF1A");
|
|
1110
|
+
for (const m of report.missingItems) lines.push(` \u274C ${m.mp} \u2192 ${m.proteus}`);
|
|
1111
|
+
}
|
|
1112
|
+
return lines.join("\n");
|
|
1113
|
+
}
|
|
1114
|
+
export {
|
|
1115
|
+
COMPONENT_IR_SCHEMA,
|
|
1116
|
+
DEFAULT_DESIGN_WIDTH,
|
|
1117
|
+
MP_MAPPING_MATRIX,
|
|
1118
|
+
PRIMITIVE_CATALOG,
|
|
1119
|
+
SEMANTIC_BACKEND_MAP,
|
|
1120
|
+
SEMANTIC_ENUM,
|
|
1121
|
+
TAG_SEMANTIC_MAP,
|
|
1122
|
+
auditCatalogConsistency,
|
|
1123
|
+
auditMiniprogramCoverage,
|
|
1124
|
+
checkComponentSnapshot,
|
|
1125
|
+
checkPrimitiveCatalog,
|
|
1126
|
+
checkSemanticCoverage,
|
|
1127
|
+
componentPrimitives,
|
|
1128
|
+
extractSemanticTree,
|
|
1129
|
+
formatCoverageReport,
|
|
1130
|
+
implementedPrimitives,
|
|
1131
|
+
mapSemanticToBackend,
|
|
1132
|
+
primitiveById,
|
|
1133
|
+
primitiveBySemantic,
|
|
1134
|
+
primitiveByTag,
|
|
1135
|
+
toComponentIR,
|
|
1136
|
+
toComponentTree,
|
|
1137
|
+
validateComponentIR,
|
|
1138
|
+
validateComponentTree,
|
|
1139
|
+
validateGridConstraints
|
|
1140
|
+
};
|