@proteus-vue/component-ir 0.1.1-beta.0 → 0.3.0-beta.10
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 +1 -1
- package/dist/degradation.d.ts +115 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.js +461 -36
- package/dist/mp-spec-coverage.d.ts +37 -0
- package/dist/primitives.d.ts +1 -1
- package/dist/schema.d.ts +43 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { PrimitiveDef } from './primitives';
|
|
2
|
+
/** 端轴:仅「真有 Backend 实现」的端(见文件头诚实边界) */
|
|
3
|
+
export type DegradationEnd = 'mp' | 'web';
|
|
4
|
+
/** 降级三态(§2) */
|
|
5
|
+
export type DegradationTier = 'supported' | 'fallback' | 'unsupported';
|
|
6
|
+
/** 单属性降级声明 */
|
|
7
|
+
export interface DegradationEntry {
|
|
8
|
+
mp: DegradationTier;
|
|
9
|
+
web: DegradationTier;
|
|
10
|
+
/** 降级行为(任一端非 supported 时必填——EA-5 §4 反黑盒) */
|
|
11
|
+
behavior?: string;
|
|
12
|
+
}
|
|
13
|
+
export type DegradationMap = Record<string, DegradationEntry>;
|
|
14
|
+
/**
|
|
15
|
+
* ★按 tag 限定的 mp: fallback(2026-09-19 新增机制):属性在 Web 有实现、MP 无平台对等。
|
|
16
|
+
*
|
|
17
|
+
* 与 STYLE_WEB_FALLBACK 等「按属性名全局」的表不同——这里**按 tag 限定**,因为本仓存在
|
|
18
|
+
* **跨组件同名属性**(实测:`loop` 是 `<video>/<audio>` 的官方属性、`snap` 是 `<draggable-sheet>`
|
|
19
|
+
* 的官方属性,而框架的 `p-stack` 另有自己的语义 `loop`/`snap`)——按属性名全局判 fallback 会误伤。
|
|
20
|
+
*
|
|
21
|
+
* 依据:`p-stack` 的 `snap`/`loop` 是「轮播」语义消灭形态(G-31 §2.2、rules.md 拒绝 `<p-swiper>`),
|
|
22
|
+
* 但 MP 端容器滚动本身不成立——**Skyline 的 view 不滚动(CSS overflow 无效)**,这是本仓已实测
|
|
23
|
+
* 的结论(skyline-pitfalls S14 + `p-scroll` 同款告警:「小程序 view 不滚动——请改用 p-scroll-view」)。
|
|
24
|
+
* 容器不滚动 ⇒ 吸附/回环无载体(CSS scroll-snap 亦然)。
|
|
25
|
+
* fallback 行为 = 普通排列 + `capabilityWarnOnce` 控制台提示(可观察,非静默失效)。
|
|
26
|
+
* ★MP 端如需真轮播:用 `p-scroll-view` 的 `paging-enabled`(官方 Skyline 翻页属性,原生支持)。
|
|
27
|
+
*/
|
|
28
|
+
declare const TAG_PROP_MP_FALLBACK: Record<string, Record<string, string>>;
|
|
29
|
+
/** ★导出供降级门禁做「显式登记的 tag 限定 fallback」豁免(区别于跨组件同名导致的误报) */
|
|
30
|
+
export declare const TAG_SCOPED_MP_FALLBACK: typeof TAG_PROP_MP_FALLBACK;
|
|
31
|
+
/** 规则表登记的全部「非缺省判定」属性名(供门禁做**陈旧规则**检测——规则指向不存在的属性即失效) */
|
|
32
|
+
export declare const RULE_REGISTERED_PROPS: string[];
|
|
33
|
+
/** 宿主组件族 tag(web:fallback 的来源) */
|
|
34
|
+
export declare const HOST_FALLBACK_TAGS: string[];
|
|
35
|
+
/** 对单个属性判定降级三元组(端轴可扩展) */
|
|
36
|
+
export declare function degradeProp(tag: string | undefined, prop: string): DegradationEntry;
|
|
37
|
+
/** 组件原语(有 tag 者)的属性降级表:tag → { prop → entry } */
|
|
38
|
+
export declare function degradationOf(prim: PrimitiveDef): DegradationMap;
|
|
39
|
+
/** 全量降级表(SSOT:PRIMITIVE_CATALOG 中所有带 tag 的原语) */
|
|
40
|
+
export declare const DEGRADATION_TABLE: Record<string, DegradationMap>;
|
|
41
|
+
/**
|
|
42
|
+
* ★性能预计算(2026-09-18):tag → { kebab 属性名 → true },**只收 `mp: unsupported` 的属性**。
|
|
43
|
+
*
|
|
44
|
+
* 编译器编译每个元素都要判「该属性在 mp 端是否 unsupported」。此前编译器在 `serializeElement`
|
|
45
|
+
* 里**逐元素**新建 Map 并对每个属性名跑 kebabCase → 实测 `compile-vue-sfc` 基准从 0.573ms
|
|
46
|
+
* 涨到 0.765ms(+33%,推过 1.2x 警告线)。改为模块加载时预计算一次:
|
|
47
|
+
* 每元素仅剩 **一次 kebabCase + 一次 Set.has**,零分配。
|
|
48
|
+
*
|
|
49
|
+
* 语义等价:诊断只关心 `mp === 'unsupported'`(`fallback` 有意降级、不报),故只需该子集。
|
|
50
|
+
*/
|
|
51
|
+
export declare const MP_UNSUPPORTED_PROPS: Record<string, Set<string>>;
|
|
52
|
+
export interface DegradationIssue {
|
|
53
|
+
tag: string;
|
|
54
|
+
prop: string;
|
|
55
|
+
/**
|
|
56
|
+
* missing = 空白格(保留以兼容既有消费方;表由规则构建后不再产生);
|
|
57
|
+
* behavior-gap = 非 supported 但无可观察行为;table-divergence = 表与规则判定不一致。
|
|
58
|
+
*/
|
|
59
|
+
kind: 'missing' | 'behavior-gap' | 'table-divergence';
|
|
60
|
+
detail: string;
|
|
61
|
+
}
|
|
62
|
+
export interface DegradationReport {
|
|
63
|
+
/** 组件原语数 */
|
|
64
|
+
components: number;
|
|
65
|
+
/** 已声明属性数 */
|
|
66
|
+
props: number;
|
|
67
|
+
/** 各端各态计数 */
|
|
68
|
+
counts: Record<DegradationEnd, Record<DegradationTier, number>>;
|
|
69
|
+
/** fallback 已声明可观察行为数 / 需要行为数(反黑盒) */
|
|
70
|
+
behaviorDeclared: number;
|
|
71
|
+
behaviorNeeded: number;
|
|
72
|
+
issues: DegradationIssue[];
|
|
73
|
+
ok: boolean;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* 降级门禁(EA-5):
|
|
77
|
+
* ① 每个 props 里的属性都必须在降级表中有声明(缺格 = 违反 EA-5,对齐 03 §3「不允许空白格」);
|
|
78
|
+
* ② 任一端为 fallback/unsupported 时必须有 behavior(可观察降级行为,§4 反黑盒)——
|
|
79
|
+
* 「只有状态、无行为」视为不诚实降级,判 FAIL。
|
|
80
|
+
*
|
|
81
|
+
* ★作用域(诚实说明):默认审计 PRIMITIVE_CATALOG 的组件原语属性(62 组件)。
|
|
82
|
+
* 组件库实际有 73 个 p-* 组件(`packages/components/*`)——其中 11 个(p-button、p-view、
|
|
83
|
+
* p-scroll-view、p-nav-bar、p-list-view、p-safe、p-sidebar、p-location、p-pick-photo、
|
|
84
|
+
* p-scan-qr、p-svg-canvas)尚未登记进 catalog。传 `extraSpecs` 可把这批也纳入审计,
|
|
85
|
+
* 由 CLI 脚本(scripts/audit-degradation.mjs)从组件源码提供,从而与端对齐标尺同口径(全 73 组件)。
|
|
86
|
+
*/
|
|
87
|
+
export declare function auditDegradation(extraSpecs?: Array<{
|
|
88
|
+
tag: string;
|
|
89
|
+
props: string[];
|
|
90
|
+
}>): DegradationReport;
|
|
91
|
+
/** 人类可读报告 */
|
|
92
|
+
export declare function formatDegradationReport(r: DegradationReport): string;
|
|
93
|
+
/** 编译期诊断码(对齐 03-degradation-tiers.md §6) */
|
|
94
|
+
export declare const PROP_NO_DEGRADATION = "PROP_NO_DEGRADATION";
|
|
95
|
+
export interface PropDiagnostic {
|
|
96
|
+
code: typeof PROP_NO_DEGRADATION;
|
|
97
|
+
tag: string;
|
|
98
|
+
prop: string;
|
|
99
|
+
end: DegradationEnd;
|
|
100
|
+
message: string;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* ★M6 编译期门禁:目标端**无法实现**的属性被业务侧使用时产生 fail-closed 诊断。
|
|
104
|
+
* 依据 03-degradation-tiers.md §2「unsupported = 本端无法实现 → 编译期警告/报错(fail-closed)」与 §6。
|
|
105
|
+
*
|
|
106
|
+
* `fallback` **不**产生诊断——降级是有意的可观察行为(行为已在表中声明);
|
|
107
|
+
* 只有 `unsupported` 才是「无论如何都会静默失败」,必须由编译器拦住。
|
|
108
|
+
*
|
|
109
|
+
* @param table 可选降级表(缺省 = 模块级 DEGRADATION_TABLE)。注入用以便测试无 unsupported 现状下的诊断路径。
|
|
110
|
+
* @returns 命中 unsupported 时返回诊断;否则 null。
|
|
111
|
+
*/
|
|
112
|
+
export declare function checkPropDegradation(tag: string, prop: string, end: DegradationEnd, table?: Record<string, DegradationMap>): PropDiagnostic | null;
|
|
113
|
+
/** 扫描一个元素的全部属性,返回所有 unsupported 诊断(供编译器批量收集) */
|
|
114
|
+
export declare function collectPropDiagnostics(tag: string, props: string[], end: DegradationEnd, table?: Record<string, DegradationMap>): PropDiagnostic[];
|
|
115
|
+
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { COMPONENT_IR_SCHEMA, SEMANTIC_ENUM, TAG_SEMANTIC_MAP } from './schema';
|
|
2
|
-
export type { ComponentIR } from './schema';
|
|
1
|
+
export { COMPONENT_IR_SCHEMA, SEMANTIC_ENUM, TAG_SEMANTIC_MAP, FRAMEWORK_INTERNAL_TAGS, TAG_SEMANTIC_ALIASES } from './schema';
|
|
2
|
+
export type { ComponentIR, TagAliasDecl } from './schema';
|
|
3
3
|
export { validateComponentIR, validateGridConstraints, validateComponentTree, DEFAULT_DESIGN_WIDTH } from './validate';
|
|
4
4
|
export type { CIRDiagnostic } from './validate';
|
|
5
5
|
export { SEMANTIC_BACKEND_MAP, mapSemanticToBackend } from './map';
|
|
@@ -11,5 +11,7 @@ export type { PrimitiveDef, PrimitiveKind, PrimitiveStatus } from './primitives'
|
|
|
11
11
|
export { MP_MAPPING_MATRIX, auditMiniprogramCoverage, auditMatrixReferences, auditCatalogConsistency, formatCoverageReport } from './audit';
|
|
12
12
|
export type { MatrixRefIssue } from './audit';
|
|
13
13
|
export type { CoverageReport, MpCoverageStatus, MpMatrixItem, ConsistencyIssue } from './audit';
|
|
14
|
-
export { classifySpecApi, classifySpecComponent, auditSpecCoverage, SPEC_COVERED, SPEC_PLANNED, SPEC_PRIVATE, SPEC_NA, SPEC_COMPONENT_OVERRIDE, SPEC_RATCHET } from './mp-spec-coverage';
|
|
14
|
+
export { classifySpecApi, classifySpecComponent, auditSpecCoverage, auditSpecOverrideRefs, GESTURE_HANDLER_EXPECTED, SPEC_COVERED, SPEC_PLANNED, SPEC_PRIVATE, SPEC_NA, SPEC_COMPONENT_OVERRIDE, SPEC_RATCHET } from './mp-spec-coverage';
|
|
15
15
|
export type { MpSpecStatus, MpSpecClass, MpOfficialSpec, SpecCoverageReport } from './mp-spec-coverage';
|
|
16
|
+
export { DEGRADATION_TABLE, MP_UNSUPPORTED_PROPS, degradeProp, degradationOf, auditDegradation, formatDegradationReport, checkPropDegradation, collectPropDiagnostics, RULE_REGISTERED_PROPS, HOST_FALLBACK_TAGS, PROP_NO_DEGRADATION, } from './degradation';
|
|
17
|
+
export type { DegradationEnd, DegradationTier, DegradationEntry, DegradationMap, DegradationIssue, DegradationReport, PropDiagnostic, } from './degradation';
|
package/dist/index.js
CHANGED
|
@@ -86,10 +86,14 @@ var SEMANTIC_ENUM = [
|
|
|
86
86
|
"engineering.transition",
|
|
87
87
|
"engineering.animate",
|
|
88
88
|
"engineering.error-boundary",
|
|
89
|
+
"engineering.share-element",
|
|
89
90
|
// —— 能力入口(G-28 组件化:p-* 能力入口组件;useXxx Hook 归 API 层不产生 C-IR 节点)——
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
// ★2026-09-18 语义去重:原列 'capability.scan-qr' / 'capability.pick-photo' 为**重复名**,
|
|
92
|
+
// 已退役改为真实能力(组件的实现分别调用 useQRCode() / useCamera(),即 C42 / C1)——
|
|
93
|
+
// 本枚举收录的是「能作为 C-IR 节点出现的语义」,故此处列真实能力名(E8 双形态先例同源)。
|
|
94
|
+
"capability.location",
|
|
95
|
+
"capability.qr-code",
|
|
96
|
+
"capability.camera"
|
|
93
97
|
];
|
|
94
98
|
var COMPONENT_IR_SCHEMA = {
|
|
95
99
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
@@ -161,11 +165,17 @@ var TAG_SEMANTIC_MAP = {
|
|
|
161
165
|
// ★E18 组件形态(p- 前缀产 C-IR)
|
|
162
166
|
"router-link": "engineering.router-link",
|
|
163
167
|
// ★兼容别名(Vue Router 风格 <router-link> 标签;非 catalog 条目——p-view 先例 G-31 B4)
|
|
168
|
+
"p-share-element": "engineering.share-element",
|
|
169
|
+
// ★批次 8:页面间共享元素转场(≠ p-transition 页内过渡)
|
|
164
170
|
"p-transition": "engineering.transition",
|
|
165
171
|
"p-animate": "engineering.animate",
|
|
166
172
|
// G-31 能力入口
|
|
167
|
-
|
|
168
|
-
|
|
173
|
+
// ★2026-09-18 语义去重:原 capability.scan-qr / capability.pick-photo 是**重复名**——
|
|
174
|
+
// 两个组件的实现分别调用 useQRCode() / useCamera(),对应真实能力即 C42 capability.qr-code /
|
|
175
|
+
// C1 capability.camera(其 mpEquiv 与 hook 完全一致)。故**退役重复名、重指向真实能力**,
|
|
176
|
+
// 并按 E8 双形态在两行补 tag(而非新建语义)。
|
|
177
|
+
"p-scan-qr": "capability.qr-code",
|
|
178
|
+
"p-pick-photo": "capability.camera",
|
|
169
179
|
"p-location": "capability.location",
|
|
170
180
|
// ★G-31 B4 现有组件对齐(src/components 实际标签 → L1 语义)
|
|
171
181
|
"p-view": "layout.box",
|
|
@@ -176,7 +186,9 @@ var TAG_SEMANTIC_MAP = {
|
|
|
176
186
|
"p-safe": "layout.safe",
|
|
177
187
|
"p-sidebar": "layout.sidebar",
|
|
178
188
|
// ★#405 语义登记批:剩余 10 组件全量入图(EXTRA_KIND 文档兑底退役)——
|
|
179
|
-
// 9 个新语义(catalog planned L2:语义层待多端映射)+ p-scroll-view 复用 layout.scroll
|
|
189
|
+
// 9 个新语义(catalog planned L2:语义层待多端映射)+ p-scroll-view 复用 layout.scroll
|
|
190
|
+
// (★2026-09-18 修正原文「p-view 先例」:显式 multi-tag 别名先例是上一行的 `router-link`
|
|
191
|
+
// ——同一语义由两个标签提供;本条与 p-view 是并列的别名情形,非「先例」关系)
|
|
180
192
|
"p-aspect": "layout.aspect",
|
|
181
193
|
"p-zone": "layout.zone",
|
|
182
194
|
"p-loading": "ui.loading",
|
|
@@ -203,6 +215,24 @@ var TAG_SEMANTIC_MAP = {
|
|
|
203
215
|
"p-ad": "shell.ad",
|
|
204
216
|
"p-map": "ui.map"
|
|
205
217
|
};
|
|
218
|
+
var FRAMEWORK_INTERNAL_TAGS = /* @__PURE__ */ new Set(["p-svg-canvas"]);
|
|
219
|
+
var TAG_SEMANTIC_ALIASES = {
|
|
220
|
+
"layout.box": {
|
|
221
|
+
canonical: "p-view",
|
|
222
|
+
aliases: ["p-box"],
|
|
223
|
+
reason: "p-view \u4E3A\u7AEF\u5BF9\u9F50\u4EA7\u7269\uFF08\u5BF9\u9F50\u5B98\u65B9 <view> \u6309\u538B\u53CD\u9988 4 \u5C5E\u6027\u3001\u6A21\u677F\u7528 <view> \u8D70 Web \u6A21\u62DF\u5C42\uFF09\u4E14\u5B9E\u9645\u88AB\u5F15\u7528\uFF1Bp-box \u4E3A G-32 L1 \u8840\u7EDF\uFF08aspectRatio/overflow \u4E24\u4E2A\u8BED\u4E49\u5C5E\u6027\uFF0C\u5F53\u524D\u96F6\u6E90\u7801\u5F15\u7528\uFF09\u2014\u2014\u4FDD\u7559\u4E3A\u517C\u5BB9\u522B\u540D\u4E0D\u5220\u3002\u2605\u6CE8\uFF1Acatalog L1 \u884C\u7684 props \u662F\u4E24\u8005\u7684\u5E76\u96C6\uFF0C\u5C5E\u300C\u610F\u56FE\u58F0\u660E\u300D\uFF0C\u4E0E\u4E24\u5B9E\u73B0\u5747\u4E0D\u5B8C\u5168\u4E00\u81F4\uFF08\u89C1 audit C8 \u62AB\u9732\uFF09\u3002"
|
|
224
|
+
},
|
|
225
|
+
"layout.scroll": {
|
|
226
|
+
canonical: "p-scroll-view",
|
|
227
|
+
aliases: ["p-scroll"],
|
|
228
|
+
reason: "p-scroll-view \u4E3A\u7AEF\u5BF9\u9F50\u4EA7\u7269\uFF08\u900F\u4F20\u5B98\u65B9 <scroll-view> \u5168\u91CF\u5C5E\u6027\uFF1Ascroll-into-view/upper-threshold/refresher \u5168\u5BB6\u6876/enhanced \u7B49\uFF09\uFF1Bp-scroll \u4E3A G-32 L10 \u8840\u7EDF\uFF08axis/paging/refresh/indicator \u8BED\u4E49\u5C5E\u6027\uFF09\u3002\u2605\u6CE8\uFF1Acatalog L10 \u884C props \u540C\u4E3A\u4E24\u8005\u5E76\u96C6\u3002"
|
|
229
|
+
},
|
|
230
|
+
"engineering.router-link": {
|
|
231
|
+
canonical: "p-router-link",
|
|
232
|
+
aliases: ["router-link"],
|
|
233
|
+
reason: "router-link \u662F Vue Router \u98CE\u683C\u6807\u7B7E\u517C\u5BB9\u522B\u540D\uFF08framework \u4FDD\u7559\u5B57\uFF0C\u975E catalog \u6761\u76EE\uFF09\u3002"
|
|
234
|
+
}
|
|
235
|
+
};
|
|
206
236
|
|
|
207
237
|
// src/validate.ts
|
|
208
238
|
var DEFAULT_DESIGN_WIDTH = 375;
|
|
@@ -412,7 +442,9 @@ var SEMANTIC_BACKEND_MAP = {
|
|
|
412
442
|
headless: "nav"
|
|
413
443
|
},
|
|
414
444
|
// —— 能力入口(G-28 组件化)——
|
|
415
|
-
|
|
445
|
+
// ★2026-09-18 语义去重:原键 capability.scan-qr 为重复名(组件的实现调用 useQRCode(),
|
|
446
|
+
// 真实能力即 C42 capability.qr-code)——键已迁移,7 端渲染映射原样保留。
|
|
447
|
+
"capability.qr-code": {
|
|
416
448
|
"vue-dom": "button.proteus-scan-qr",
|
|
417
449
|
"native-ios": "AVCaptureSession",
|
|
418
450
|
"native-android": "CameraX",
|
|
@@ -421,7 +453,9 @@ var SEMANTIC_BACKEND_MAP = {
|
|
|
421
453
|
flutter: "scanQR",
|
|
422
454
|
headless: "scan-qr"
|
|
423
455
|
},
|
|
424
|
-
|
|
456
|
+
// ★2026-09-18 语义去重:原键 capability.pick-photo 为重复名(组件实现调用 useCamera(),
|
|
457
|
+
// 真实能力即 C1 capability.camera)——键已迁移,映射原样保留。
|
|
458
|
+
"capability.camera": {
|
|
425
459
|
"vue-dom": "input.proteus-pick-photo",
|
|
426
460
|
"native-ios": "UIImagePicker",
|
|
427
461
|
"native-android": "PhotoPicker",
|
|
@@ -725,6 +759,44 @@ var SEMANTIC_BACKEND_MAP = {
|
|
|
725
759
|
headless: "form"
|
|
726
760
|
},
|
|
727
761
|
// ★G-32 B4 ④ Gesture 组件形态(drag/scrollable——指令/useGesture 属绑定层不产渲染节点)
|
|
762
|
+
// ★2026-09-18:tap / longpress 补映射——`v-gesture:tap|longpress` 已有**双端原生对等**
|
|
763
|
+
// (MP 映射 bindtap/bindlongpress;Web 走 Pointer 识别器),故由 planned 转 implemented。
|
|
764
|
+
// ★其余手势(swipe/pan/pinch/rotate/press)**刻意不补**:MP 端无事件对等
|
|
765
|
+
// (官方用 worklet 手势处理器<组件>,框架未生成)→ 保持 planned(诚实边界,不凑端数)。
|
|
766
|
+
"gesture.tap": {
|
|
767
|
+
"vue-dom": "v-gesture:tap\uFF08Pointer \u8BC6\u522B\u5668\uFF09",
|
|
768
|
+
"native-ios": "UITapGestureRecognizer",
|
|
769
|
+
"native-android": "GestureDetector.onSingleTapUp",
|
|
770
|
+
"native-harmony": "TapGesture",
|
|
771
|
+
skyline: "bindtap",
|
|
772
|
+
// MP 原生事件(编译器 v-gesture:tap → bindtap,2026-09-18)
|
|
773
|
+
flutter: "GestureDetector.onTap",
|
|
774
|
+
headless: "tap"
|
|
775
|
+
},
|
|
776
|
+
"gesture.longpress": {
|
|
777
|
+
"vue-dom": "v-gesture:longpress\uFF08Pointer \u8BC6\u522B\u5668\uFF09",
|
|
778
|
+
"native-ios": "UILongPressGestureRecognizer",
|
|
779
|
+
"native-android": "GestureDetector.onLongPress",
|
|
780
|
+
"native-harmony": "LongPressGesture",
|
|
781
|
+
skyline: "bindlongpress",
|
|
782
|
+
// MP 原生事件(编译器 v-gesture:longpress → bindlongpress,2026-09-18)
|
|
783
|
+
flutter: "GestureDetector.onLongPress",
|
|
784
|
+
headless: "longpress"
|
|
785
|
+
},
|
|
786
|
+
// ★批次 8:共享元素转场(页面间)——各端系统级转场能力
|
|
787
|
+
"engineering.share-element": {
|
|
788
|
+
"vue-dom": "div.proteus-share-element",
|
|
789
|
+
// Web 无宿主共享元素转场 → 普通容器(降级,不飞)
|
|
790
|
+
"native-ios": "UIView.matchedTransition",
|
|
791
|
+
"native-android": "SharedElementTransition",
|
|
792
|
+
"native-harmony": "geometryTransition",
|
|
793
|
+
// ArkUI 几何过渡
|
|
794
|
+
skyline: "share-element",
|
|
795
|
+
// 微信原生(宿主跨页飞行)
|
|
796
|
+
flutter: "Hero",
|
|
797
|
+
// Flutter Hero 动画即共享元素
|
|
798
|
+
headless: "share-element"
|
|
799
|
+
},
|
|
728
800
|
"gesture.draggable": {
|
|
729
801
|
"vue-dom": "div.proteus-draggable",
|
|
730
802
|
"native-ios": "UIPanGestureRecognizer",
|
|
@@ -922,17 +994,20 @@ var LAYOUT = [
|
|
|
922
994
|
{ id: "L12", kind: "layout", semantic: "layout.masonry", tag: "p-masonry", props: ["colCount", "gap"], mpEquiv: "\u7B2C\u4E09\u65B9\u7011\u5E03\u6D41", tier: "L1", status: "implemented" },
|
|
923
995
|
// ★#405 语义登记批:Fluid 体系剩余组件(语义层待多端映射 → planned L2——G-31.4 不足 3 端降级)
|
|
924
996
|
{ id: "L13", kind: "layout", semantic: "layout.aspect", tag: "p-aspect", props: ["ratio", "maxWidth"], mpEquiv: "\u65E0\uFF08\u7EB5\u6A2A\u6BD4\u5BB9\u5668\uFF09", tier: "L2", status: "planned" },
|
|
925
|
-
{ id: "L14", kind: "layout", semantic: "layout.zone", tag: "p-zone", props: ["designWidth"], mpEquiv: "\u65E0\uFF08\u5BB9\u5668\u65AD\u70B9\u5206\u533A\uFF09", tier: "L2", status: "planned" }
|
|
997
|
+
{ id: "L14", kind: "layout", semantic: "layout.zone", tag: "p-zone", props: ["designWidth"], mpEquiv: "\u65E0\uFF08\u5BB9\u5668\u65AD\u70B9\u5206\u533A\uFF09", tier: "L2", status: "planned" },
|
|
998
|
+
// ★批次 7(2026-09-18)未登记组件补登记:安全区(刘海/折叠屏/降级)+ 侧边导航容器
|
|
999
|
+
{ id: "L15", kind: "layout", semantic: "layout.safe", tag: "p-safe", props: ["area", "fold", "fallback"], mpEquiv: "\u65E0\uFF08\u5B89\u5168\u533A\u8BED\u4E49\u5BB9\u5668\uFF09", tier: "L1", status: "implemented" },
|
|
1000
|
+
{ id: "L16", kind: "layout", semantic: "layout.sidebar", tag: "p-sidebar", props: ["minSidebarWidth", "navWidth", "designWidth", "toggleLabel"], mpEquiv: "\u65E0\uFF08\u5BB9\u5668\u65AD\u70B9\u4FA7\u680F\uFF09", tier: "L1", status: "implemented" }
|
|
926
1001
|
];
|
|
927
1002
|
var UI = [
|
|
928
1003
|
{ id: "U1", kind: "ui", semantic: "ui.text", tag: "p-text", props: ["content", "selectable", "truncate", "align", "userSelect", "overflow", "maxLines", "selectOnGesture", "space", "decode"], mpEquiv: "<text>", tier: "L1", status: "implemented" },
|
|
929
1004
|
{ id: "U2", kind: "ui", semantic: "ui.heading", tag: "p-heading", props: ["level"], mpEquiv: "<h1>-<h6>", tier: "L1", status: "implemented" },
|
|
930
|
-
{ id: "U3", kind: "ui", semantic: "ui.rich-text", tag: "p-rich-text", props: ["
|
|
1005
|
+
{ id: "U3", kind: "ui", semantic: "ui.rich-text", tag: "p-rich-text", props: ["nodes", "space", "userSelect", "mode", "source"], mpEquiv: "<rich-text>", tier: "L1", status: "implemented" },
|
|
931
1006
|
{ id: "U4", kind: "ui", semantic: "ui.icon", tag: "p-icon", props: ["name", "type", "size", "color", "spin"], mpEquiv: "<icon>", tier: "L1", status: "implemented" },
|
|
932
1007
|
{ id: "U5", kind: "ui", semantic: "ui.image", tag: "p-image", props: ["src", "fit", "placeholder", "lazy", "showMenuByLongpress", "fadeIn", "preload", "webp", "referrerPolicy"], mpEquiv: "<image>", tier: "L1", status: "implemented" },
|
|
933
1008
|
{ id: "U6", kind: "ui", semantic: "ui.avatar", tag: "p-avatar", props: ["src", "shape", "size", "fallback"], mpEquiv: "\u7EC4\u5408", tier: "L1", status: "implemented" },
|
|
934
|
-
{ id: "U7", kind: "ui", semantic: "ui.media", tag: "p-media", props: ["kind", "controls", "autoplay", "poster", "
|
|
935
|
-
{ id: "U8", kind: "ui", semantic: "ui.canvas", tag: "p-canvas", props: ["engine", "resolution"], mpEquiv: "<canvas>", tier: "L1", status: "implemented" },
|
|
1009
|
+
{ id: "U7", kind: "ui", semantic: "ui.media", tag: "p-media", props: ["kind", "src", "duration", "controls", "autoplay", "loop", "muted", "initialTime", "poster", "objectFit", "title", "playBtnPosition", "direction", "showProgress", "showFullscreenBtn", "showPlayBtn", "showCenterPlayBtn", "showMuteBtn", "showBottomProgress", "enableProgressGesture", "enablePlayGesture", "pageGesture", "vslideGesture", "vslideGestureInFullscreen", "autoPauseIfNavigate", "autoPauseIfOpenNative", "danmuList", "danmuBtn", "enableDanmu", "pictureInPicture"], mpEquiv: "<video>+<audio>", tier: "L1", status: "implemented" },
|
|
1010
|
+
{ id: "U8", kind: "ui", semantic: "ui.canvas", tag: "p-canvas", props: ["engine", "canvasId", "disableScroll", "resolution"], mpEquiv: "<canvas>", tier: "L1", status: "implemented" },
|
|
936
1011
|
{ id: "U9", kind: "ui", semantic: "ui.svg", tag: "p-svg", props: ["path", "viewbox"], mpEquiv: "\u65E0", tier: "L1", status: "implemented" },
|
|
937
1012
|
{ id: "U10", kind: "ui", semantic: "ui.input", tag: "p-input", props: ["type", "mask", "validation", "clearable"], mpEquiv: "<input>", tier: "L1", status: "implemented" },
|
|
938
1013
|
{ id: "U11", kind: "ui", semantic: "ui.textarea", tag: "p-textarea", props: ["autosize", "maxLength", "count"], mpEquiv: "<textarea>", tier: "L1", status: "implemented" },
|
|
@@ -953,9 +1028,16 @@ var UI = [
|
|
|
953
1028
|
// ★权威标尺批 H:局部文本选区
|
|
954
1029
|
{ id: "U24", kind: "ui", semantic: "ui.selection", tag: "p-selection", props: ["disableContextMenu", "selectable"], mpEquiv: "<selection>", tier: "L1", status: "implemented" },
|
|
955
1030
|
// ★权威标尺批 I:相机
|
|
956
|
-
{ id: "U25", kind: "ui", semantic: "ui.camera", tag: "p-camera", props: ["devicePosition", "flash", "aspectRatio"], mpEquiv: "<camera>", tier: "L1", status: "implemented" },
|
|
1031
|
+
{ id: "U25", kind: "ui", semantic: "ui.camera", tag: "p-camera", props: ["mode", "resolution", "devicePosition", "flash", "frameSize", "aspectRatio"], mpEquiv: "<camera>", tier: "L1", status: "implemented" },
|
|
957
1032
|
// ★权威标尺批 J:地图
|
|
958
|
-
{ id: "U26", kind: "ui", semantic: "ui.map", tag: "p-map", props: ["latitude", "longitude", "scale", "markers"], mpEquiv: "<map>", tier: "L1", status: "implemented" }
|
|
1033
|
+
{ id: "U26", kind: "ui", semantic: "ui.map", tag: "p-map", props: ["latitude", "longitude", "scale", "minScale", "maxScale", "markers", "polyline", "circles", "polygons", "includePoints", "showLocation", "layerStyle", "rotate", "skew", "showCompass", "showScale", "enableZoom", "enableScroll", "enableRotate", "enableSatellite", "enableTraffic", "enablePoi", "enableBuilding", "enableOverlooking", "setting"], mpEquiv: "<map>", tier: "L1", status: "implemented" },
|
|
1034
|
+
// ★批次 7(2026-09-18)未登记组件补登记:这 3 个组件此前**只存在于 TAG_SEMANTIC_MAP**(编译器能解析、
|
|
1035
|
+
// 组件能渲染),但不在 SSOT catalog → C1 不变量(catalog tag ↔ TAG_SEMANTIC_MAP 双向)单向成立、
|
|
1036
|
+
// catalog 查询类 API(primitiveByTag / componentPrimitives)查不到它们。
|
|
1037
|
+
// ★p-button 尤其反讽:它是**端对齐全流程的参考实现**(21/21、SOP v2 范式),却不在语义清单里。
|
|
1038
|
+
{ id: "U27", kind: "ui", semantic: "ui.button", tag: "p-button", props: ["disabled", "loading", "throttle", "size", "type", "plain", "formType", "openType", "hoverClass", "theme", "hoverStopPropagation", "hoverStartTime", "hoverStayTime", "lang", "sessionFrom", "sendMessageTitle", "sendMessagePath", "sendMessageImg", "appParameter", "showMessageCard", "phoneNumberNoQuotaToast", "needShowEntrance", "entrancePath"], mpEquiv: "<button>", tier: "L1", status: "implemented" },
|
|
1039
|
+
{ id: "U28", kind: "ui", semantic: "ui.list", tag: "p-list-view", props: ["items", "itemHeight", "height", "bufferSize", "virtual", "lazy", "padding"], mpEquiv: "<list-view>\uFF08\u865A\u62DF\u6EDA\u52A8\uFF09", tier: "L1", status: "implemented" },
|
|
1040
|
+
{ id: "U29", kind: "ui", semantic: "ui.nav", tag: "p-nav-bar", props: ["title", "back", "fixed", "loading", "frontColor", "backgroundColor", "colorAnimationDuration", "colorAnimationTimingFunc"], mpEquiv: "<navigation-bar>\uFF08\u81EA\u7ED8\uFF09", tier: "L1", status: "implemented" }
|
|
959
1041
|
];
|
|
960
1042
|
var SHELL = [
|
|
961
1043
|
{ id: "S1", kind: "shell", semantic: "shell.page", tag: "p-page", props: ["title", "statusBar", "pullRefresh"], mpEquiv: "<page>", tier: "L1", status: "implemented" },
|
|
@@ -979,17 +1061,21 @@ var SHELL = [
|
|
|
979
1061
|
{ id: "S15", kind: "shell", semantic: "shell.keyboard-accessory", tag: "p-keyboard-accessory", props: ["visible", "maxHeight", "background"], mpEquiv: "<keyboard-accessory>", tier: "L1", status: "implemented" },
|
|
980
1062
|
// ★权威标尺批 J:内嵌网页 / 广告位
|
|
981
1063
|
{ id: "S16", kind: "shell", semantic: "shell.webview", tag: "p-webview", props: ["src", "height", "sandbox"], mpEquiv: "<web-view>", tier: "L1", status: "implemented" },
|
|
982
|
-
{ id: "S17", kind: "shell", semantic: "shell.ad", tag: "p-ad", props: ["unitId", "adIntervals", "adType", "height"], mpEquiv: "<ad>", tier: "L1", status: "implemented" }
|
|
1064
|
+
{ id: "S17", kind: "shell", semantic: "shell.ad", tag: "p-ad", props: ["unitId", "adIntervals", "adType", "adTheme", "height"], mpEquiv: "<ad>", tier: "L1", status: "implemented" }
|
|
983
1065
|
];
|
|
984
1066
|
var GESTURE = [
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
{ id: "
|
|
990
|
-
{ id: "
|
|
991
|
-
{ id: "
|
|
992
|
-
{ id: "
|
|
1067
|
+
// ★2026-09-18:G1/G2 由 planned → implemented——`v-gesture:tap|longpress` 已双端落地
|
|
1068
|
+
// (编译器新增 `directive/v-gesture` 规则:MP 映射 bindtap/bindlongpress;Web 走 Pointer 识别器)。
|
|
1069
|
+
// ★G3–G7 **保持 planned**:MP 端无事件对等(官方用 worklet 手势处理器<组件>,框架未生成),
|
|
1070
|
+
// mpEquiv 已改为**如实描述**(原值 bindswipe/bindtouchmove/组合/3D Touch 多为不准确表述)。
|
|
1071
|
+
{ id: "G1", kind: "gesture", semantic: "gesture.tap", api: "v-gesture:tap", props: ["count"], mpEquiv: "bindtap\uFF08\u539F\u751F\u4E8B\u4EF6\u2014\u2014\u7F16\u8BD1\u5668\u76F4\u6620\u5C04\uFF09", tier: "L1", status: "implemented" },
|
|
1072
|
+
{ id: "G2", kind: "gesture", semantic: "gesture.longpress", api: "v-gesture:longpress", props: ["duration", "onEnd"], mpEquiv: "bindlongpress\uFF08\u539F\u751F\u4E8B\u4EF6\u2014\u2014\u7F16\u8BD1\u5668\u76F4\u6620\u5C04\uFF09", tier: "L1", status: "implemented" },
|
|
1073
|
+
{ id: "G3", kind: "gesture", semantic: "gesture.swipe", api: "v-gesture:swipe", props: ["direction", "threshold"], mpEquiv: "\u65E0\u539F\u751F\u4E8B\u4EF6\u2014\u2014\u9700 @touchstart/@touchend \u81EA\u884C\u5224\u5B9A\u65B9\u5411", tier: "L1", status: "planned" },
|
|
1074
|
+
{ id: "G4", kind: "gesture", semantic: "gesture.pan", api: "v-gesture:pan", props: ["axis", "bounds"], mpEquiv: "\u65E0\u539F\u751F\u4E8B\u4EF6\u2014\u2014\u5B98\u65B9 <pan-gesture-handler>\uFF08worklet\uFF09\u6216 touch \u65CF\u81EA\u884C\u5224\u5B9A", tier: "L1", status: "planned" },
|
|
1075
|
+
{ id: "G5", kind: "gesture", semantic: "gesture.pinch", api: "v-gesture:pinch", props: ["scale", "onChange"], mpEquiv: "\u65E0\u539F\u751F\u4E8B\u4EF6\u2014\u2014\u5B98\u65B9 <scale-gesture-handler>\uFF08worklet\uFF09", tier: "L1", status: "planned" },
|
|
1076
|
+
{ id: "G6", kind: "gesture", semantic: "gesture.rotate", api: "v-gesture:rotate", props: ["angle"], mpEquiv: "\u65E0\u539F\u751F\u4E8B\u4EF6\uFF08MP \u65E0 rotate \u624B\u52BF\u5904\u7406\u5668\uFF09", tier: "L1", status: "planned" },
|
|
1077
|
+
{ id: "G7", kind: "gesture", semantic: "gesture.press", api: "v-gesture:press", props: ["force"], mpEquiv: "\u65E0\u539F\u751F\u4E8B\u4EF6\u2014\u2014\u5B98\u65B9 <force-press-gesture-handler>\uFF08worklet\uFF0C\u9700 3D Touch \u8BBE\u5907\uFF09", tier: "L1", status: "planned" },
|
|
1078
|
+
{ id: "G8", kind: "gesture", semantic: "gesture.draggable", tag: "p-draggable", props: ["direction", "inertia", "outOfBounds", "x", "y", "damping", "friction", "disabled", "scaleEnabled", "scaleMin", "scaleMax", "scaleValue", "animation", "scaleArea", "ghost", "snapToGrid", "onDrop"], mpEquiv: "movable-view", tier: "L1", status: "implemented" },
|
|
993
1079
|
{ id: "G9", kind: "gesture", semantic: "gesture.scrollable", tag: "p-scrollable", props: ["bounce", "refresh", "loadMore"], mpEquiv: "<scroll-view>", tier: "L1", status: "implemented" },
|
|
994
1080
|
{ id: "G10", kind: "gesture", semantic: "gesture.use-gesture", api: "useGesture()", props: ["recognizers", "simultaneous"], mpEquiv: "\u65E0", tier: "L1", status: "planned" }
|
|
995
1081
|
];
|
|
@@ -998,9 +1084,10 @@ function cap(id, name, api, mpEquiv, returnType) {
|
|
|
998
1084
|
}
|
|
999
1085
|
var CAPABILITY = [
|
|
1000
1086
|
// 7.1 设备/硬件(15)
|
|
1001
|
-
|
|
1087
|
+
// ★2026-09-18 双形态(E8 先例):能力入口组件 p-pick-photo 即 useCamera() 的声明式入口
|
|
1088
|
+
{ id: "C1", kind: "capability", semantic: "capability.camera", api: "useCamera()", tag: "p-pick-photo", props: ["Result<Media>"], mpEquiv: "wx.createCameraContext", tier: "L1", status: "implemented" },
|
|
1002
1089
|
cap("C2", "microphone", "useMicrophone()", "RecorderManager", "Result<AudioBuffer>"),
|
|
1003
|
-
{ id: "C3", kind: "capability", semantic: "capability.location", api: "useLocation()", props: ["Result<Coords>"], mpEquiv: "wx.getLocation", tier: "L1", status: "implemented" },
|
|
1090
|
+
{ id: "C3", kind: "capability", semantic: "capability.location", api: "useLocation()", tag: "p-location", props: ["Result<Coords>"], mpEquiv: "wx.getLocation", tier: "L1", status: "implemented" },
|
|
1004
1091
|
cap("C4", "map", "useMap()", "wx.createMapContext", "MapController"),
|
|
1005
1092
|
cap("C5", "sensor", "useSensor()", "onAccelerometer/onCompass/onGyroscope", "SensorStream"),
|
|
1006
1093
|
cap("C6", "vibrate", "useVibrate()", "wx.vibrateShort/Long", "void"),
|
|
@@ -1042,7 +1129,8 @@ var CAPABILITY = [
|
|
|
1042
1129
|
cap("C39", "face-id", "useFaceID()", "\u7EC4\u5408", "Result<boolean>"),
|
|
1043
1130
|
cap("C40", "payment", "usePayment()", "wx.requestPayment", "Result<PayResult>"),
|
|
1044
1131
|
cap("C41", "login", "useLogin()", "wx.login", "Result<Token>"),
|
|
1045
|
-
|
|
1132
|
+
// ★2026-09-18 双形态(E8 先例):能力入口组件 p-scan-qr 即 useQRCode() 的声明式入口
|
|
1133
|
+
{ id: "C42", kind: "capability", semantic: "capability.qr-code", api: "useQRCode()", tag: "p-scan-qr", props: ["Result<string>"], mpEquiv: "wx.scanCode + canvas", tier: "L1", status: "implemented" },
|
|
1046
1134
|
cap("C43", "file-system", "useFileSystem()", "wx.getFileSystemManager", "FSAdapter"),
|
|
1047
1135
|
cap("C44", "archive", "useArchive()", "wx.compressFile", "Result<void>"),
|
|
1048
1136
|
cap("C45", "shortcut", "useShortcut()", "wx.addToDesktop", "Result<void>"),
|
|
@@ -1124,7 +1212,18 @@ var ENGINEERING = [
|
|
|
1124
1212
|
{ id: "E25", kind: "engineering", semantic: "engineering.inspector", api: "useInspector()", mpEquiv: "\u65E0", tier: "L1", status: "planned" },
|
|
1125
1213
|
{ id: "E26", kind: "engineering", semantic: "engineering.performance", api: "usePerformance()", mpEquiv: "wx.reportPerformance", tier: "L1", status: "planned" },
|
|
1126
1214
|
{ id: "E27", kind: "engineering", semantic: "engineering.define-component", api: "defineComponent()", mpEquiv: "Component()", tier: "L1", status: "planned" },
|
|
1127
|
-
{ id: "E28", kind: "engineering", semantic: "engineering.define-capability", api: "defineCapability()", mpEquiv: "\u65E0", tier: "L1", status: "planned" }
|
|
1215
|
+
{ id: "E28", kind: "engineering", semantic: "engineering.define-capability", api: "defineCapability()", mpEquiv: "\u65E0", tier: "L1", status: "planned" },
|
|
1216
|
+
// ★批次 8(2026-09-18):共享元素转场(官方 <share-element> 对齐)——能力由宿主提供,框架只声明语义
|
|
1217
|
+
{ id: "E29", kind: "engineering", semantic: "engineering.share-element", tag: "p-share-element", props: ["shuttleKey", "animate", "duration", "easingFunction", "transitionOnGesture", "shuttleOnPush", "shuttleOnPop", "rectTweenType"], mpEquiv: "<share-element>\uFF08\u5BBF\u4E3B\u8DE8\u9875\u98DE\u884C\u52A8\u753B\uFF09", tier: "L1", status: "implemented" },
|
|
1218
|
+
// ★E30(2026-09-19):WebMCP 接入——把**能力面**暴露为浏览器内 agent 可调用工具。
|
|
1219
|
+
// 规范面:`document.modelContext.registerTool(...)`(★非 navigator)+ signal 注销(规范无 unregisterTool);
|
|
1220
|
+
// 灵感来自 VueUse v15 `useWebMCP`(document 命名空间 / signal 注销 / 方法可调用探测 / 三元组返回)。
|
|
1221
|
+
// 框架差异化:能力面统一 `CapResult<T>` 契约(G-32.4)→ 能力可**自动**派生为工具、响应归一不必逐个手写。
|
|
1222
|
+
// ★状态口径(与 E1-E28 / C15 / C26 等同款,非本轮新引入):hook 形态原语无**渲染后端映射**,
|
|
1223
|
+
// 故按 catalog 既有约定记 `planned`——该字段反映「已在 SEMANTIC_BACKEND_MAP 登记 ≥3 端」,
|
|
1224
|
+
// 不等于「代码未实现」(本原语已实现于 `packages/api/src/mcp.ts`)。
|
|
1225
|
+
// `mpEquiv: 无`:WebMCP 是浏览器侧 agent 通道,小程序无 `document.modelContext` → 如实降级(isSupported=false)。
|
|
1226
|
+
{ id: "E30", kind: "engineering", semantic: "engineering.mcp", api: "useMCP()", props: ["tools", "capabilities", "prefix"], mpEquiv: "\u65E0\uFF08\u6D4F\u89C8\u5668\u4FA7 agent \u901A\u9053\uFF1BMP \u7AEF isSupported=false \u8BDA\u5B9E\u964D\u7EA7\uFF09", tier: "L1", status: "planned" }
|
|
1128
1227
|
];
|
|
1129
1228
|
var PRIMITIVE_CATALOG = [...LAYOUT, ...UI, ...SHELL, ...GESTURE, ...CAPABILITY, ...ENGINEERING];
|
|
1130
1229
|
function componentPrimitives(kinds = ["layout", "ui", "shell", "gesture", "engineering"]) {
|
|
@@ -1233,7 +1332,7 @@ var MP_COMPONENTS = [
|
|
|
1233
1332
|
{ mp: "<ad>", proteus: "shell.ad\uFF08p-ad\uFF09", status: "ok", group: "component" },
|
|
1234
1333
|
{ mp: "<official-account>", proteus: "useMiniProgram\uFF08\u5FAE\u4FE1\u79C1\u6709\uFF09", status: "private", group: "component" },
|
|
1235
1334
|
{ mp: "<open-data>", proteus: "useMiniProgram\uFF08\u5FAE\u4FE1\u79C1\u6709\uFF09", status: "private", group: "component" },
|
|
1236
|
-
{ mp: "<share-element>", proteus: "
|
|
1335
|
+
{ mp: "<share-element>", proteus: "engineering.share-element\uFF08p-share-element\uFF09", status: "ok", group: "component" },
|
|
1237
1336
|
{ mp: "<aria-component>", proteus: "aria-* \u5C5E\u6027\uFF08\u5404\u7EC4\u4EF6 ariaLabel\uFF1B\u4E24\u7AEF\u539F\u751F\u652F\u6301\uFF09", status: "ok", group: "component" },
|
|
1238
1337
|
{ mp: "<page-container>", proteus: "shell.page-container", status: "ok", group: "component" },
|
|
1239
1338
|
{ mp: "<voip-room>", proteus: "useMiniProgram\uFF08\u5FAE\u4FE1 VOIP\uFF09", status: "private", group: "component" },
|
|
@@ -1399,6 +1498,45 @@ function auditCatalogConsistency() {
|
|
|
1399
1498
|
}
|
|
1400
1499
|
}
|
|
1401
1500
|
}
|
|
1501
|
+
{
|
|
1502
|
+
const bySemantic = /* @__PURE__ */ new Map();
|
|
1503
|
+
for (const [tag, sem] of Object.entries(TAG_SEMANTIC_MAP)) {
|
|
1504
|
+
const list = bySemantic.get(sem) ?? [];
|
|
1505
|
+
list.push(tag);
|
|
1506
|
+
bySemantic.set(sem, list);
|
|
1507
|
+
}
|
|
1508
|
+
const aliasKeys = Object.keys(TAG_SEMANTIC_ALIASES);
|
|
1509
|
+
for (const [sem, tags] of bySemantic) {
|
|
1510
|
+
if (tags.length <= 1) continue;
|
|
1511
|
+
const decl = TAG_SEMANTIC_ALIASES[sem];
|
|
1512
|
+
if (!decl) {
|
|
1513
|
+
issues.push({
|
|
1514
|
+
rule: "C6",
|
|
1515
|
+
detail: `\u8BED\u4E49 ${sem} \u88AB ${tags.length} \u4E2A\u6807\u7B7E\u5171\u4EAB\uFF08${tags.join(", ")}\uFF09\u4F46\u672A\u5728 TAG_SEMANTIC_ALIASES \u767B\u8BB0\uFF08\u987B\u7ED9\u7406\u7531 + \u6307\u660E\u89C4\u8303\u6807\u7B7E\uFF09`
|
|
1516
|
+
});
|
|
1517
|
+
continue;
|
|
1518
|
+
}
|
|
1519
|
+
const declared = /* @__PURE__ */ new Set([decl.canonical, ...decl.aliases]);
|
|
1520
|
+
const undeclared = tags.filter((t) => !declared.has(t));
|
|
1521
|
+
if (undeclared.length) {
|
|
1522
|
+
issues.push({ rule: "C6", detail: `${sem} \u7684\u5171\u4EAB\u6807\u7B7E ${undeclared.join(", ")} \u672A\u5728 TAG_SEMANTIC_ALIASES \u4E2D\u58F0\u660E` });
|
|
1523
|
+
}
|
|
1524
|
+
if (!tags.includes(decl.canonical)) {
|
|
1525
|
+
issues.push({ rule: "C6", detail: `${sem} \u7684 canonical\u300C${decl.canonical}\u300D\u4E0D\u5728\u5B9E\u9645\u5171\u4EAB\u6807\u7B7E\u4E2D` });
|
|
1526
|
+
}
|
|
1527
|
+
if (!decl.reason?.trim()) issues.push({ rule: "C6", detail: `${sem} \u7684\u522B\u540D\u767B\u8BB0\u7F3A\u7406\u7531` });
|
|
1528
|
+
}
|
|
1529
|
+
for (const sem of aliasKeys) {
|
|
1530
|
+
if ((bySemantic.get(sem)?.length ?? 0) <= 1) {
|
|
1531
|
+
issues.push({ rule: "C6", detail: `TAG_SEMANTIC_ALIASES \u767B\u8BB0\u4E86 ${sem}\uFF0C\u4F46\u5B83\u5E76\u672A\u88AB\u591A\u6807\u7B7E\u5171\u4EAB\uFF08\u9648\u65E7\u767B\u8BB0\uFF09` });
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
for (const sem of Object.keys(SEMANTIC_BACKEND_MAP)) {
|
|
1536
|
+
if (!PRIMITIVE_CATALOG.some((p) => p.semantic === sem)) {
|
|
1537
|
+
issues.push({ rule: "C7", detail: `${sem} \u5728 SEMANTIC_BACKEND_MAP \u4F46 catalog \u65E0\u627F\u63A5\u884C\uFF08\u5B64\u513F\u8BED\u4E49\uFF09` });
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1402
1540
|
return issues;
|
|
1403
1541
|
}
|
|
1404
1542
|
function formatCoverageReport(report) {
|
|
@@ -1829,12 +1967,30 @@ var SPEC_COMPONENT_OVERRIDE = {
|
|
|
1829
1967
|
"radio-group": { status: "na", proteus: "p-radio \u5206\u7EC4\uFF08\u6D88\u706D\u4E3A\u5B50\u9879\uFF09" },
|
|
1830
1968
|
"picker-view-column": { status: "na", proteus: "p-picker \u591A\u5217\uFF08\u6D88\u706D\u4E3A\u5C5E\u6027\uFF09" },
|
|
1831
1969
|
// Skyline 手势处理器 → 手势原语(gesture.* / v-gesture)
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1970
|
+
// ★2026-09-18 诚实性修正:以下 5 条原标 covered,但其指向的 gesture 原语在 PRIMITIVE_CATALOG 中
|
|
1971
|
+
// 为 **planned**(`gesture.long-press` 更是拼写错误——实际名为 `gesture.longpress`)。
|
|
1972
|
+
// covered 的定义是「有可运行等价」,而统一手势 API(`v-gesture:*`)尚未落地 →
|
|
1973
|
+
// 按实测状态改标 planned(与 2026-09-16 属性标尺虚高同类修正:数字不粉饰)。
|
|
1974
|
+
// ★2026-09-18 复评:tap/longpress **已据实转 covered**(编译器新增 directive/v-gesture 规则,
|
|
1975
|
+
// 双端原生事件对等:MP bindtap/bindlongpress、Web Pointer 识别器);pan/pinch/press 仍 planned
|
|
1976
|
+
// (MP 无事件对等——官方是 worklet 手势处理器<组件>,非事件属性)。
|
|
1977
|
+
// ★同时新增 auditSpecOverrideRefs 门禁——此前 override 表**完全不在引用校验范围内**,
|
|
1978
|
+
// 故这 5 条虚高 + 1 处悬空引用长期未被发现。
|
|
1979
|
+
"tap-gesture-handler": { status: "covered", proteus: "gesture.tap\uFF08\u26052026-09-18 \u843D\u5730\uFF1A\u7F16\u8BD1\u5668 v-gesture:tap \u2192 bindtap\u2014\u2014MP \u539F\u751F\u4E8B\u4EF6\u5BF9\u7B49\uFF09" },
|
|
1980
|
+
// ★2026-09-19 诚实性修正(与本文件 `gesture.long-press` 拼写错误同类):本节原标 `covered`
|
|
1981
|
+
// 且引用 `gesture.draggable`(**拖拽**语义)——两处都不成立:
|
|
1982
|
+
// ① 引用错位:双击识别在 `gesture.tap` 的 `count`/`dblTapWindow`(recognizers.ts:229),
|
|
1983
|
+
// 与 draggable(G8 = movable-view 拖拽)无关;
|
|
1984
|
+
// ② MP 无对等:`v-gesture:tap` 在 MP 只映射 bindtap(原生事件不带 count),编译器亦无 count
|
|
1985
|
+
// 处理 → 双击语义仅 Web 成立。
|
|
1986
|
+
// ⇒ 按 pan/scale/force-press 同一标准(MP 无原生对等即 planned)据实改标 `planned`。
|
|
1987
|
+
// ★该错位长期漏网,因 auditSpecOverrideRefs 当时只校验「引用存在且已落地」——
|
|
1988
|
+
// 本轮补 GESTURE_HANDLER_EXPECTED 语义配对门禁(同类错位今后必红)。
|
|
1989
|
+
"double-tap-gesture-handler": { status: "planned", proteus: "gesture.tap \u7684 count \u53D8\u4F53\uFF08Web \u8BC6\u522B\u5668\u652F\u6301\u53CC\u51FB\uFF1BMP \u539F\u751F bindtap \u4E0D\u5E26 count\uFF0C\u65E0\u5BF9\u7B49\uFF09" },
|
|
1990
|
+
"long-press-gesture-handler": { status: "covered", proteus: "gesture.longpress\uFF08\u26052026-09-18 \u843D\u5730\uFF1A\u7F16\u8BD1\u5668 v-gesture:longpress \u2192 bindlongpress\u2014\u2014MP \u539F\u751F\u4E8B\u4EF6\u5BF9\u7B49\uFF09" },
|
|
1991
|
+
"pan-gesture-handler": { status: "planned", proteus: "gesture.pan\uFF08v-gesture:pan \u89C4\u5212\u4E2D\uFF09" },
|
|
1992
|
+
"scale-gesture-handler": { status: "planned", proteus: "gesture.pinch\uFF08v-gesture:pinch \u89C4\u5212\u4E2D\uFF09" },
|
|
1993
|
+
"force-press-gesture-handler": { status: "planned", proteus: "gesture.press\uFF08v-gesture:press \u89C4\u5212\u4E2D\uFF09" },
|
|
1838
1994
|
"horizontal-drag-gesture-handler": { status: "covered", proteus: "gesture.draggable" },
|
|
1839
1995
|
"vertical-drag-gesture-handler": { status: "covered", proteus: "gesture.draggable" },
|
|
1840
1996
|
// Skyline 布局构建器 → 布局语义
|
|
@@ -1867,6 +2023,47 @@ var SPEC_COMPONENT_OVERRIDE = {
|
|
|
1867
2023
|
"store-home": { status: "private", proteus: "\uFF08\u5FAE\u4FE1\u5C0F\u5E97\u4E3B\u9875\uFF0C\u79C1\u6709\uFF09" },
|
|
1868
2024
|
"store-product": { status: "private", proteus: "\uFF08\u5FAE\u4FE1\u5C0F\u5E97\u5546\u54C1\uFF0C\u79C1\u6709\uFF09" }
|
|
1869
2025
|
};
|
|
2026
|
+
var GESTURE_HANDLER_EXPECTED = {
|
|
2027
|
+
"tap-gesture-handler": "gesture.tap",
|
|
2028
|
+
"double-tap-gesture-handler": "gesture.tap",
|
|
2029
|
+
// 语义上属 tap 的 count 变体(当前 planned——MP 无对等)
|
|
2030
|
+
"long-press-gesture-handler": "gesture.longpress",
|
|
2031
|
+
"pan-gesture-handler": "gesture.pan",
|
|
2032
|
+
"scale-gesture-handler": "gesture.pinch",
|
|
2033
|
+
"force-press-gesture-handler": "gesture.press",
|
|
2034
|
+
"horizontal-drag-gesture-handler": "gesture.draggable",
|
|
2035
|
+
"vertical-drag-gesture-handler": "gesture.draggable"
|
|
2036
|
+
};
|
|
2037
|
+
function auditSpecOverrideRefs(catalog, overrideTable = SPEC_COMPONENT_OVERRIDE) {
|
|
2038
|
+
const bySemantic = new Map(catalog.map((p) => [p.semantic, p]));
|
|
2039
|
+
const byTag = new Map(catalog.filter((p) => p.tag).map((p) => [p.tag, p]));
|
|
2040
|
+
const issues = [];
|
|
2041
|
+
let coveredRefs = 0;
|
|
2042
|
+
for (const [tag, cls] of Object.entries(overrideTable)) {
|
|
2043
|
+
if (cls.status !== "covered") continue;
|
|
2044
|
+
const text = cls.proteus ?? "";
|
|
2045
|
+
const expected = GESTURE_HANDLER_EXPECTED[tag];
|
|
2046
|
+
if (expected) {
|
|
2047
|
+
const gestureRefs = [...text.matchAll(/\bgesture\.[a-z][\w-]*/g)].map((m) => m[0]).filter((r) => bySemantic.has(r));
|
|
2048
|
+
if (gestureRefs.length > 0 && !gestureRefs.includes(expected)) {
|
|
2049
|
+
issues.push({ mp: tag, ref: expected, kind: "mismatched" });
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
for (const m of text.matchAll(/\b(?:layout|ui|shell|gesture|capability|engineering)\.[a-z][\w-]*/g)) {
|
|
2053
|
+
coveredRefs++;
|
|
2054
|
+
const p = bySemantic.get(m[0]);
|
|
2055
|
+
if (!p) issues.push({ mp: tag, ref: m[0], kind: "semantic" });
|
|
2056
|
+
else if (p.status === "planned") issues.push({ mp: tag, ref: m[0], kind: "unimplemented" });
|
|
2057
|
+
}
|
|
2058
|
+
for (const m of text.matchAll(/\bp-[a-z][\w-]*/g)) {
|
|
2059
|
+
coveredRefs++;
|
|
2060
|
+
const p = byTag.get(m[0]);
|
|
2061
|
+
if (!p) issues.push({ mp: tag, ref: m[0], kind: "component" });
|
|
2062
|
+
else if (p.status === "planned") issues.push({ mp: tag, ref: m[0], kind: "unimplemented" });
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
return { issues, coveredRefs };
|
|
2066
|
+
}
|
|
1870
2067
|
function auditSpecCoverage(spec, matrix) {
|
|
1871
2068
|
const counts = { covered: 0, planned: 0, private: 0, na: 0, gap: 0 };
|
|
1872
2069
|
const gaps = [];
|
|
@@ -1902,16 +2099,236 @@ function auditSpecCoverage(spec, matrix) {
|
|
|
1902
2099
|
};
|
|
1903
2100
|
}
|
|
1904
2101
|
var SPEC_RATCHET = {
|
|
1905
|
-
|
|
1906
|
-
//
|
|
2102
|
+
// ★2026-09-18 水位修正(非回退):255 → 250(含 5 条手势虚高);同日后又 **250 → 252**
|
|
2103
|
+
// (其中 tap/longpress 经编译器落地,见下)。原 255 基线**含 5 条虚高**
|
|
2104
|
+
// (tap/long-press/pan/scale/force-press-gesture-handler 标 covered,但其手势原语为 planned)。
|
|
2105
|
+
// 修正后实测 covered 250 / planned 6(share-element + 5 手势)/ private 106 / na 20 / gap 0。
|
|
2106
|
+
// 这与 2026-09-16「属性总数 793→771」属同类修正——**修的是标尺,不是能力**:
|
|
2107
|
+
// 真实可运行的等价物一件没少,只是不再把「规划中」记成「已落地」。
|
|
2108
|
+
// ★今后此值只增不减;若因**同类诚实性修正**需下调,必须在本注释写明被修正的具体条目。
|
|
2109
|
+
// ★2026-09-18 上调 250 → 252:tap/longpress 手势**真实落地**(编译器 directive/v-gesture 规则
|
|
2110
|
+
// → MP bindtap/bindlongpress + Web Pointer 识别器),spec 分类据实由 planned 转 covered。
|
|
2111
|
+
// 与上次的「下调」性质相反——这次是**能力真实增长**(真·落地率 97%→98%)。
|
|
2112
|
+
// ★2026-09-19 诚实性修正(下行 253→252,属「修标尺不是修能力」):`double-tap-gesture-handler`
|
|
2113
|
+
// 原标 covered 且引用 `gesture.draggable`(拖拽)——引用错位 + MP 无对等(bindtap 不带 count)
|
|
2114
|
+
// ⇒ 据实转 planned。**真实可运行的等价物一件没少**,只是不再把「仅 Web 成立」记成「两端已落地」。
|
|
2115
|
+
// 该条即上次注释要求「因诚实性修正需下调时,必须写明被修正的具体条目」所指的条目。
|
|
2116
|
+
coveredMin: 252,
|
|
2117
|
+
// 2026-09-18 上行基线(250 修正基线 + tap/longpress 落地);2026-09-19 因 double-tap 修正后实测亦为 252
|
|
1907
2118
|
gapMax: 0
|
|
1908
2119
|
// 全部官方项必须归类
|
|
1909
2120
|
};
|
|
2121
|
+
|
|
2122
|
+
// src/degradation.ts
|
|
2123
|
+
var PRIVATE_WEB_FALLBACK = {
|
|
2124
|
+
openType: "Web \u89E6\u53D1\u540C\u540D\u4E8B\u4EF6\uFF08\u5982 @contact\uFF09+ console \u63D0\u793A\u81EA\u5B9A\u4E49\u5BF9\u5E94\u4EA4\u4E92\uFF08\u53C2\u8003 Web p-button open-type\uFF09",
|
|
2125
|
+
appId: "Web \u65E0\u8DE8\u5E94\u7528\u8DF3\u8F6C\u80FD\u529B\uFF0C\u4EC5\u56DE\u663E\u53C2\u6570\u5E76\u63D0\u793A",
|
|
2126
|
+
shortLink: "Web \u65E0\u77ED\u94FE\u8DF3\u8F6C\u80FD\u529B\uFF0C\u4EC5\u56DE\u663E\u53C2\u6570\u5E76\u63D0\u793A",
|
|
2127
|
+
unitId: "Web \u65E0\u5BBF\u4E3B\u5E7F\u544A\u4F4D\uFF0C\u6E32\u67D3\u5360\u4F4D\u5E76\u63D0\u793A\uFF08\u4E0D\u9759\u9ED8\uFF09",
|
|
2128
|
+
adType: "Web \u65E0\u5BBF\u4E3B\u5E7F\u544A\u7C7B\u578B\uFF0C\u5FFD\u7565\u5E76\u63D0\u793A",
|
|
2129
|
+
adTheme: "Web \u65E0\u5BBF\u4E3B\u5E7F\u544A\u4E3B\u9898\uFF0C\u5FFD\u7565\u5E76\u63D0\u793A",
|
|
2130
|
+
adIntervals: "Web \u65E0\u5BBF\u4E3B\u5E7F\u544A\u81EA\u52A8\u5237\u65B0\uFF0C\u5FFD\u7565\u5E76\u63D0\u793A"
|
|
2131
|
+
};
|
|
2132
|
+
var STYLE_WEB_FALLBACK = {
|
|
2133
|
+
hoverClass: "\u6620\u5C04 CSS :active/:hover \u7C7B\uFF08\u6846\u67B6\u9ED8\u8BA4\u7C7B\u6052\u5728\uFF0C\u81EA\u5B9A\u4E49\u7C7B\u4F5C\u9644\u52A0\u2014\u2014T3\uFF09",
|
|
2134
|
+
hoverStartTime: "Web \u7528 CSS transition-delay \u8FD1\u4F3C\u6309\u538B\u8D77\u59CB\u5EF6\u65F6",
|
|
2135
|
+
hoverStayTime: "Web \u7528 CSS transition-duration \u8FD1\u4F3C\u6309\u538B\u505C\u7559\u65F6\u957F",
|
|
2136
|
+
hoverStopPropagation: "Web \u7528 CSS pointer-events \u963B\u65AD\u5B50\u7EA7\u53CD\u9988\u8FD1\u4F3C"
|
|
2137
|
+
};
|
|
2138
|
+
var MP_HOST_WEB_FALLBACK = {
|
|
2139
|
+
alwaysEmbed: "Web \u65E0\u540C\u5C42/\u975E\u540C\u5C42\u5207\u6362\u6982\u5FF5\uFF08input \u59CB\u7EC8\u5728\u6587\u6863\u6D41\uFF09\uFF0C\u5FFD\u7565\u5E76\u63D0\u793A",
|
|
2140
|
+
confirmHold: "Web \u65E0\u6CD5\u63A7\u5236\u8F6F\u952E\u76D8\u6536\u8D77\u65F6\u673A\uFF08\u7531\u6D4F\u89C8\u5668/\u7CFB\u7EDF\u51B3\u5B9A\uFF09\uFF0C\u5FFD\u7565\u5E76\u63D0\u793A",
|
|
2141
|
+
adjustPosition: "Web \u7531\u6D4F\u89C8\u5668\u81EA\u8EAB\u5904\u7406\u952E\u76D8\u5F39\u8D77\u65F6\u7684\u89C6\u53E3\u6EDA\u52A8\uFF0C\u5FFD\u7565\u5E76\u63D0\u793A",
|
|
2142
|
+
holdKeyboard: "Web \u65E0\u6CD5\u5728\u5931\u7126\u70B9\u51FB\u65F6\u4FDD\u6301\u952E\u76D8\uFF08\u6D4F\u89C8\u5668\u884C\u4E3A\uFF09\uFF0C\u5FFD\u7565\u5E76\u63D0\u793A",
|
|
2143
|
+
cursorColor: "Web \u8FD1\u4F3C\uFF1ACSS caret-color\uFF08\u5F53\u524D\u672A\u63A5\u7EBF\uFF0C\u5FFD\u7565\u5E76\u63D0\u793A\uFF09",
|
|
2144
|
+
selectionStart: "Web \u8FD1\u4F3C\uFF1AsetSelectionRange\uFF08\u5F53\u524D\u672A\u63A5\u7EBF\uFF0C\u5FFD\u7565\u5E76\u63D0\u793A\uFF09",
|
|
2145
|
+
selectionEnd: "Web \u8FD1\u4F3C\uFF1AsetSelectionRange\uFF08\u5F53\u524D\u672A\u63A5\u7EBF\uFF0C\u5FFD\u7565\u5E76\u63D0\u793A\uFF09",
|
|
2146
|
+
placeholderClass: "Web \u65E0\u300C\u5360\u4F4D\u7B26\u4E13\u7528\u7C7B\u540D\u300D\u901A\u9053\uFF08::placeholder \u4E0D\u53EF\u627F\u63A5\u4EFB\u610F\u7C7B\uFF09\uFF0C\u5FFD\u7565\u5E76\u63D0\u793A",
|
|
2147
|
+
safePasswordCertPath: "Web \u65E0\u5C0F\u7A0B\u5E8F\u5B89\u5168\u952E\u76D8\uFF08\u5BBF\u4E3B\u7EA7\u52A0\u5BC6\u952E\u76D8\uFF09\uFF0C\u5FFD\u7565\u5E76\u63D0\u793A",
|
|
2148
|
+
safePasswordLength: "Web \u65E0\u5C0F\u7A0B\u5E8F\u5B89\u5168\u952E\u76D8\uFF0C\u5FFD\u7565\u5E76\u63D0\u793A",
|
|
2149
|
+
safePasswordTimeStamp: "Web \u65E0\u5C0F\u7A0B\u5E8F\u5B89\u5168\u952E\u76D8\uFF0C\u5FFD\u7565\u5E76\u63D0\u793A",
|
|
2150
|
+
safePasswordNonce: "Web \u65E0\u5C0F\u7A0B\u5E8F\u5B89\u5168\u952E\u76D8\uFF0C\u5FFD\u7565\u5E76\u63D0\u793A",
|
|
2151
|
+
safePasswordSalt: "Web \u65E0\u5C0F\u7A0B\u5E8F\u5B89\u5168\u952E\u76D8\uFF0C\u5FFD\u7565\u5E76\u63D0\u793A",
|
|
2152
|
+
safePasswordCustomHash: "Web \u65E0\u5C0F\u7A0B\u5E8F\u5B89\u5168\u952E\u76D8\uFF0C\u5FFD\u7565\u5E76\u63D0\u793A",
|
|
2153
|
+
reportSubmit: "formId \u662F\u5C0F\u7A0B\u5E8F\u6A21\u677F\u6D88\u606F\u5BBF\u4E3B\u80FD\u529B\uFF0CWeb \u65E0\u5BF9\u5E94 \u2192 \u5FFD\u7565\u5E76\u63D0\u793A",
|
|
2154
|
+
reportSubmitTimeout: "formId \u662F\u5C0F\u7A0B\u5E8F\u6A21\u677F\u6D88\u606F\u5BBF\u4E3B\u80FD\u529B\uFF0CWeb \u65E0\u5BF9\u5E94 \u2192 \u5FFD\u7565\u5E76\u63D0\u793A"
|
|
2155
|
+
};
|
|
2156
|
+
var HOST_TAGS = {
|
|
2157
|
+
"p-map": "Web \u6A21\u62DF\u5C42\u6E32\u67D3\u9759\u6001\u5360\u4F4D\u5BB9\u5668\uFF0C\u5FFD\u7565\u8BE5\u5730\u56FE\u663E\u793A\u53C2\u6570\u5E76\u63D0\u793A",
|
|
2158
|
+
"p-media": "Web \u7528\u539F\u751F <video>\uFF0C\u5FFD\u7565\u8BE5\u64AD\u653E\u5668\u5916\u89C2/\u4E13\u6709\u53C2\u6570\u5E76\u63D0\u793A",
|
|
2159
|
+
"p-camera": "Web \u65E0\u6444\u50CF\u5934\u5BBF\u4E3B\uFF0C\u5FFD\u7565\u8BE5\u91C7\u96C6\u53C2\u6570\u5E76\u63D0\u793A\uFF08\u53EF\u9000\u5316 getUserMedia \u65F6\u53E6\u884C\u5B9E\u73B0\uFF09",
|
|
2160
|
+
"p-ad": "Web \u65E0\u5BBF\u4E3B\u5E7F\u544A\uFF0C\u5FFD\u7565\u8BE5\u5E7F\u544A\u53C2\u6570\u5E76\u63D0\u793A",
|
|
2161
|
+
"p-canvas": "Web \u7528 HTMLCanvasElement\uFF0C\u5FFD\u7565\u8BE5\u5C0F\u7A0B\u5E8F\u6E32\u67D3\u4E0A\u4E0B\u6587\u4E13\u6709\u53C2\u6570\u5E76\u63D0\u793A",
|
|
2162
|
+
"p-webview": "Web \u7528 <iframe>\uFF0C\u5FFD\u7565\u8BE5\u5C0F\u7A0B\u5E8F web-view \u4E13\u6709\u53C2\u6570\u5E76\u63D0\u793A"
|
|
2163
|
+
};
|
|
2164
|
+
var WEB_EXTENSION_MP_FALLBACK = {
|
|
2165
|
+
density: "\u5C0F\u7A0B\u5E8F\u65E0 DPI \u5BC6\u5EA6\u6982\u5FF5\uFF0C\u5FFD\u7565\u5E76\u63D0\u793A\uFF08rpx \u5DF2\u6309\u8BBE\u8BA1\u7A3F\u5BBD\u5EA6\u6362\u7B97\uFF09",
|
|
2166
|
+
designWidth: "\u5C0F\u7A0B\u5E8F\u7528 rpx \u57FA\u51C6\uFF08750\uFF09\uFF0C\u8BE5 Web \u4E13\u7528\u57FA\u51C6\u5FFD\u7565\u5E76\u63D0\u793A",
|
|
2167
|
+
searchable: "\u5C0F\u7A0B\u5E8F\u65E0\u5185\u5EFA\u641C\u7D22\u8BED\u4E49\uFF0C\u5FFD\u7565\u5E76\u63D0\u793A\uFF08\u7531\u4E1A\u52A1\u4FA7\u81EA\u7ED8\uFF09"
|
|
2168
|
+
};
|
|
2169
|
+
var TAG_PROP_MP_FALLBACK = {
|
|
2170
|
+
"p-stack": {
|
|
2171
|
+
snap: "\u5C0F\u7A0B\u5E8F view \u4E0D\u6EDA\u52A8\uFF08Skyline \u65E0 CSS overflow \u6EDA\u52A8\uFF0CS14 \u5B9E\u6D4B\uFF09\u2192 \u5438\u9644\u65E0\u8F7D\u4F53\uFF0C\u964D\u7EA7\u4E3A\u666E\u901A\u6392\u5217 + \u53EF\u89C2\u5BDF\u63D0\u793A\uFF1B\u9700\u7FFB\u9875\u8BF7\u7528 p-scroll-view \u7684 paging-enabled",
|
|
2172
|
+
loop: "\u56DE\u73AF\u4F9D\u8D56\u5438\u9644\u5BB9\u5668\uFF0C\u800C\u5C0F\u7A0B\u5E8F view \u4E0D\u6EDA\u52A8\uFF08S14\uFF09\u2192 \u4E00\u5E76\u964D\u7EA7\u4E3A\u666E\u901A\u6392\u5217 + \u53EF\u89C2\u5BDF\u63D0\u793A"
|
|
2173
|
+
}
|
|
2174
|
+
};
|
|
2175
|
+
var RULE_REGISTERED_PROPS = [
|
|
2176
|
+
...Object.keys(PRIVATE_WEB_FALLBACK),
|
|
2177
|
+
...Object.keys(STYLE_WEB_FALLBACK),
|
|
2178
|
+
...Object.keys(WEB_EXTENSION_MP_FALLBACK),
|
|
2179
|
+
...Object.keys(MP_HOST_WEB_FALLBACK),
|
|
2180
|
+
...Object.values(TAG_PROP_MP_FALLBACK).flatMap((m) => Object.keys(m))
|
|
2181
|
+
];
|
|
2182
|
+
var HOST_FALLBACK_TAGS = Object.keys(HOST_TAGS);
|
|
2183
|
+
function degradeProp(tag, prop) {
|
|
2184
|
+
const style = STYLE_WEB_FALLBACK[prop];
|
|
2185
|
+
if (style) return { mp: "supported", web: "fallback", behavior: style };
|
|
2186
|
+
const priv = PRIVATE_WEB_FALLBACK[prop];
|
|
2187
|
+
if (priv) return { mp: "supported", web: "fallback", behavior: priv };
|
|
2188
|
+
const mpHost = MP_HOST_WEB_FALLBACK[prop];
|
|
2189
|
+
if (mpHost) return { mp: "supported", web: "fallback", behavior: mpHost };
|
|
2190
|
+
if (tag && HOST_TAGS[tag]) {
|
|
2191
|
+
return { mp: "supported", web: "fallback", behavior: HOST_TAGS[tag] };
|
|
2192
|
+
}
|
|
2193
|
+
const tagProp = tag ? TAG_PROP_MP_FALLBACK[tag] : void 0;
|
|
2194
|
+
const tagPropBehavior = tagProp ? tagProp[prop] : void 0;
|
|
2195
|
+
if (tagPropBehavior) return { mp: "fallback", web: "supported", behavior: tagPropBehavior };
|
|
2196
|
+
const webExt = WEB_EXTENSION_MP_FALLBACK[prop];
|
|
2197
|
+
if (webExt) return { mp: "fallback", web: "supported", behavior: webExt };
|
|
2198
|
+
return { mp: "supported", web: "supported" };
|
|
2199
|
+
}
|
|
2200
|
+
function degradationOf(prim) {
|
|
2201
|
+
const out = {};
|
|
2202
|
+
for (const p of prim.props ?? []) out[p] = degradeProp(prim.tag, p);
|
|
2203
|
+
return out;
|
|
2204
|
+
}
|
|
2205
|
+
var DEGRADATION_TABLE = (() => {
|
|
2206
|
+
const out = {};
|
|
2207
|
+
for (const prim of PRIMITIVE_CATALOG) {
|
|
2208
|
+
if (!prim.tag) continue;
|
|
2209
|
+
out[prim.tag] = degradationOf(prim);
|
|
2210
|
+
}
|
|
2211
|
+
return out;
|
|
2212
|
+
})();
|
|
2213
|
+
var MP_UNSUPPORTED_PROPS = (() => {
|
|
2214
|
+
const kebab = (s) => s.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
2215
|
+
const out = {};
|
|
2216
|
+
for (const [tag, table] of Object.entries(DEGRADATION_TABLE)) {
|
|
2217
|
+
const set = /* @__PURE__ */ new Set();
|
|
2218
|
+
for (const [prop, entry] of Object.entries(table)) {
|
|
2219
|
+
if (entry.mp === "unsupported") set.add(kebab(prop));
|
|
2220
|
+
}
|
|
2221
|
+
if (set.size) out[tag] = set;
|
|
2222
|
+
}
|
|
2223
|
+
return out;
|
|
2224
|
+
})();
|
|
2225
|
+
function auditDegradation(extraSpecs) {
|
|
2226
|
+
const issues = [];
|
|
2227
|
+
const counts = {
|
|
2228
|
+
mp: { supported: 0, fallback: 0, unsupported: 0 },
|
|
2229
|
+
web: { supported: 0, fallback: 0, unsupported: 0 }
|
|
2230
|
+
};
|
|
2231
|
+
let props = 0;
|
|
2232
|
+
let components = 0;
|
|
2233
|
+
let behaviorDeclared = 0;
|
|
2234
|
+
let behaviorNeeded = 0;
|
|
2235
|
+
const specs = [];
|
|
2236
|
+
const sourceByTag = new Map((extraSpecs ?? []).map((s) => [s.tag, s.props]));
|
|
2237
|
+
const emitted = /* @__PURE__ */ new Set();
|
|
2238
|
+
for (const prim of PRIMITIVE_CATALOG) {
|
|
2239
|
+
if (!prim.tag) continue;
|
|
2240
|
+
specs.push({ tag: prim.tag, props: sourceByTag.get(prim.tag) ?? prim.props ?? [] });
|
|
2241
|
+
emitted.add(prim.tag);
|
|
2242
|
+
}
|
|
2243
|
+
for (const s of extraSpecs ?? []) {
|
|
2244
|
+
if (emitted.has(s.tag)) continue;
|
|
2245
|
+
specs.push({ tag: s.tag, props: s.props });
|
|
2246
|
+
emitted.add(s.tag);
|
|
2247
|
+
}
|
|
2248
|
+
for (const { tag, props: propList } of specs) {
|
|
2249
|
+
components++;
|
|
2250
|
+
for (const p of propList) {
|
|
2251
|
+
props++;
|
|
2252
|
+
const entry = degradeProp(tag, p);
|
|
2253
|
+
counts.mp[entry.mp]++;
|
|
2254
|
+
counts.web[entry.web]++;
|
|
2255
|
+
const needsBehavior = entry.mp !== "supported" || entry.web !== "supported";
|
|
2256
|
+
if (needsBehavior) {
|
|
2257
|
+
behaviorNeeded++;
|
|
2258
|
+
if (entry.behavior && entry.behavior.trim().length > 0) behaviorDeclared++;
|
|
2259
|
+
else {
|
|
2260
|
+
issues.push({
|
|
2261
|
+
tag,
|
|
2262
|
+
prop: p,
|
|
2263
|
+
kind: "behavior-gap",
|
|
2264
|
+
detail: `\u58F0\u660E\u4E86 ${entry.mp === "supported" ? "" : `mp:${entry.mp} `}${entry.web === "supported" ? "" : `web:${entry.web}`} \u4F46\u65E0 behavior\uFF08\u964D\u7EA7\u987B\u53EF\u89C2\u5BDF\uFF0C\xA74 \u53CD\u9ED1\u76D2\uFF09`
|
|
2265
|
+
});
|
|
2266
|
+
}
|
|
2267
|
+
}
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
for (const [tag, table] of Object.entries(DEGRADATION_TABLE)) {
|
|
2271
|
+
for (const [prop, entry] of Object.entries(table)) {
|
|
2272
|
+
const fresh = degradeProp(tag, prop);
|
|
2273
|
+
if (fresh.mp !== entry.mp || fresh.web !== entry.web) {
|
|
2274
|
+
issues.push({
|
|
2275
|
+
tag,
|
|
2276
|
+
prop,
|
|
2277
|
+
kind: "table-divergence",
|
|
2278
|
+
detail: `DEGRADATION_TABLE=${entry.mp}/${entry.web} \u4E0E\u89C4\u5219\u5224\u5B9A=${fresh.mp}/${fresh.web} \u4E0D\u4E00\u81F4\uFF08\u8868\u987B\u7531\u89C4\u5219\u6784\u5EFA\uFF09`
|
|
2279
|
+
});
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
}
|
|
2283
|
+
return { components, props, counts, behaviorDeclared, behaviorNeeded, issues, ok: issues.length === 0 };
|
|
2284
|
+
}
|
|
2285
|
+
function formatDegradationReport(r) {
|
|
2286
|
+
const lines = [
|
|
2287
|
+
`\u964D\u7EA7\u58F0\u660E\u8986\u76D6\uFF1A${r.components} \u7EC4\u4EF6 / ${r.props} \u5C5E\u6027`,
|
|
2288
|
+
` mp \u7AEF\uFF1Asupported ${r.counts.mp.supported} \xB7 fallback ${r.counts.mp.fallback} \xB7 unsupported ${r.counts.mp.unsupported}`,
|
|
2289
|
+
` web \u7AEF\uFF1Asupported ${r.counts.web.supported} \xB7 fallback ${r.counts.web.fallback} \xB7 unsupported ${r.counts.web.unsupported}`,
|
|
2290
|
+
` fallback \u53EF\u89C2\u5BDF\u884C\u4E3A\uFF1A${r.behaviorDeclared}/${r.behaviorNeeded}`
|
|
2291
|
+
];
|
|
2292
|
+
if (r.issues.length) {
|
|
2293
|
+
lines.push(` \u274C ${r.issues.length} \u9879\u95EE\u9898\uFF1A`);
|
|
2294
|
+
for (const i of r.issues.slice(0, 20)) lines.push(` - [${i.tag}] ${i.prop}\uFF1A${i.detail}`);
|
|
2295
|
+
if (r.issues.length > 20) lines.push(` \u2026\u53E6\u6709 ${r.issues.length - 20} \u9879`);
|
|
2296
|
+
}
|
|
2297
|
+
return lines.join("\n");
|
|
2298
|
+
}
|
|
2299
|
+
var PROP_NO_DEGRADATION = "PROP_NO_DEGRADATION";
|
|
2300
|
+
function checkPropDegradation(tag, prop, end, table = DEGRADATION_TABLE) {
|
|
2301
|
+
const entry = table[tag]?.[prop];
|
|
2302
|
+
if (!entry) return null;
|
|
2303
|
+
if (entry[end] !== "unsupported") return null;
|
|
2304
|
+
return {
|
|
2305
|
+
code: PROP_NO_DEGRADATION,
|
|
2306
|
+
tag,
|
|
2307
|
+
prop,
|
|
2308
|
+
end,
|
|
2309
|
+
message: `[${PROP_NO_DEGRADATION}] <${tag}> \u7684\u5C5E\u6027 "${prop}" \u5728 ${end} \u7AEF\u4E3A unsupported\uFF08\u672C\u7AEF\u65E0\u6CD5\u5B9E\u73B0\uFF09\u2014\u2014\u7EE7\u7EED\u4F7F\u7528\u4F1A\u9759\u9ED8\u5931\u8D25\uFF08\u8FDD\u53CD G-31.2\uFF09\u3002\u8BF7\u6539\u7528 @conditional \u663E\u5F0F\u5206\u652F\u6216\u79FB\u9664\u8BE5\u5C5E\u6027\u3002`
|
|
2310
|
+
};
|
|
2311
|
+
}
|
|
2312
|
+
function collectPropDiagnostics(tag, props, end, table = DEGRADATION_TABLE) {
|
|
2313
|
+
const out = [];
|
|
2314
|
+
for (const p of props) {
|
|
2315
|
+
const d = checkPropDegradation(tag, p, end, table);
|
|
2316
|
+
if (d) out.push(d);
|
|
2317
|
+
}
|
|
2318
|
+
return out;
|
|
2319
|
+
}
|
|
1910
2320
|
export {
|
|
1911
2321
|
COMPONENT_IR_SCHEMA,
|
|
1912
2322
|
DEFAULT_DESIGN_WIDTH,
|
|
2323
|
+
DEGRADATION_TABLE,
|
|
2324
|
+
FRAMEWORK_INTERNAL_TAGS,
|
|
2325
|
+
GESTURE_HANDLER_EXPECTED,
|
|
2326
|
+
HOST_FALLBACK_TAGS,
|
|
1913
2327
|
MP_MAPPING_MATRIX,
|
|
2328
|
+
MP_UNSUPPORTED_PROPS,
|
|
1914
2329
|
PRIMITIVE_CATALOG,
|
|
2330
|
+
PROP_NO_DEGRADATION,
|
|
2331
|
+
RULE_REGISTERED_PROPS,
|
|
1915
2332
|
SEMANTIC_BACKEND_MAP,
|
|
1916
2333
|
SEMANTIC_ENUM,
|
|
1917
2334
|
SPEC_COMPONENT_OVERRIDE,
|
|
@@ -1920,19 +2337,27 @@ export {
|
|
|
1920
2337
|
SPEC_PLANNED,
|
|
1921
2338
|
SPEC_PRIVATE,
|
|
1922
2339
|
SPEC_RATCHET,
|
|
2340
|
+
TAG_SEMANTIC_ALIASES,
|
|
1923
2341
|
TAG_SEMANTIC_MAP,
|
|
1924
2342
|
auditCatalogConsistency,
|
|
2343
|
+
auditDegradation,
|
|
1925
2344
|
auditMatrixReferences,
|
|
1926
2345
|
auditMiniprogramCoverage,
|
|
1927
2346
|
auditSpecCoverage,
|
|
2347
|
+
auditSpecOverrideRefs,
|
|
1928
2348
|
checkComponentSnapshot,
|
|
1929
2349
|
checkPrimitiveCatalog,
|
|
2350
|
+
checkPropDegradation,
|
|
1930
2351
|
checkSemanticCoverage,
|
|
1931
2352
|
classifySpecApi,
|
|
1932
2353
|
classifySpecComponent,
|
|
2354
|
+
collectPropDiagnostics,
|
|
1933
2355
|
componentPrimitives,
|
|
2356
|
+
degradationOf,
|
|
2357
|
+
degradeProp,
|
|
1934
2358
|
extractSemanticTree,
|
|
1935
2359
|
formatCoverageReport,
|
|
2360
|
+
formatDegradationReport,
|
|
1936
2361
|
implementedPrimitives,
|
|
1937
2362
|
mapSemanticToBackend,
|
|
1938
2363
|
primitiveById,
|
|
@@ -23,6 +23,43 @@ export declare function classifySpecComponent(tag: string, matrix: ReadonlyArray
|
|
|
23
23
|
}>): MpSpecClass;
|
|
24
24
|
/** 组件级显式分类(名不对齐/语义消灭/Skyline 专属——矩阵未逐条登记时的权威声明) */
|
|
25
25
|
export declare const SPEC_COMPONENT_OVERRIDE: Record<string, MpSpecClass>;
|
|
26
|
+
/**
|
|
27
|
+
* ★2026-09-18 补门禁:`SPEC_COMPONENT_OVERRIDE` 的引用一致性。
|
|
28
|
+
*
|
|
29
|
+
* 背景(本轮实测发现):`auditMatrixReferences` 只遍历 `MP_MAPPING_MATRIX`,
|
|
30
|
+
* override 表**完全不在引用校验范围内** → 表里长期存在两类未被发现的缺陷:
|
|
31
|
+
* ① **虚高覆盖**:5 个手势处理器标 `covered`,但指向的 `gesture.tap/pan/pinch/press`
|
|
32
|
+
* 在 PRIMITIVE_CATALOG 中是 `planned`(`covered` 的定义是「有可运行等价」);
|
|
33
|
+
* ② **悬空引用**:`gesture.long-press` 拼写错误(实际名为 `gesture.longpress`)。
|
|
34
|
+
* 两者都让「真·落地率」虚高且无人察觉。本函数把 override 表纳入同一校验。
|
|
35
|
+
*
|
|
36
|
+
* @param catalog 原语清单(SSOT,判定 covered 的引用是否真已落地)
|
|
37
|
+
* @param overrideTable 可选注入(测试用;缺省 = SPEC_COMPONENT_OVERRIDE)
|
|
38
|
+
* @returns issues 结构同矩阵引用检查;`dangling` 为未登记原语,`unimplemented` 为指向 planned 原语。
|
|
39
|
+
*/
|
|
40
|
+
/**
|
|
41
|
+
* ★gesture-handler 组件 → **期望承载语义**(2026-09-19)。
|
|
42
|
+
*
|
|
43
|
+
* 背景(本轮实测发现):`double-tap-gesture-handler` 长期标 `covered` 并引用
|
|
44
|
+
* `gesture.draggable`(拖拽)——语义完全对不上,却因 `auditSpecOverrideRefs` 当时只校验
|
|
45
|
+
* 「引用存在且**已落地**」而无人发现(draggable 确实存在且已落地)。
|
|
46
|
+
* 「引用存在」≠「引用对得上」——本表把后者机器化:**covered 声明的引用必须包含期望语义**。
|
|
47
|
+
*
|
|
48
|
+
* 维护:新增 *-gesture-handler 类组件时同步本表;covered 行若换了承载语义须同步此处(否则 FAIL)。
|
|
49
|
+
*/
|
|
50
|
+
export declare const GESTURE_HANDLER_EXPECTED: Record<string, string>;
|
|
51
|
+
export declare function auditSpecOverrideRefs(catalog: ReadonlyArray<{
|
|
52
|
+
semantic: string;
|
|
53
|
+
tag?: string;
|
|
54
|
+
status: string;
|
|
55
|
+
}>, overrideTable?: Record<string, MpSpecClass>): {
|
|
56
|
+
issues: Array<{
|
|
57
|
+
mp: string;
|
|
58
|
+
ref: string;
|
|
59
|
+
kind: 'semantic' | 'component' | 'unimplemented' | 'mismatched';
|
|
60
|
+
}>;
|
|
61
|
+
coveredRefs: number;
|
|
62
|
+
};
|
|
26
63
|
export interface SpecCoverageReport {
|
|
27
64
|
total: number;
|
|
28
65
|
covered: number;
|
package/dist/primitives.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export type PrimitiveKind = 'layout' | 'ui' | 'shell' | 'gesture' | 'capability'
|
|
|
2
2
|
/** 状态:implemented = 已在 SEMANTIC_BACKEND_MAP 登记 ≥3 端(G-31.4/G-32.3 门禁适用);planned = 已冻结待实现(L2 生态) */
|
|
3
3
|
export type PrimitiveStatus = 'implemented' | 'planned';
|
|
4
4
|
export interface PrimitiveDef {
|
|
5
|
-
/** 清单编号(L1..
|
|
5
|
+
/** 清单编号(L1..L16 / U1..U29 / S1..S17 / G1..G10 / C1..C81 / E1..E29) */
|
|
6
6
|
id: string;
|
|
7
7
|
kind: PrimitiveKind;
|
|
8
8
|
/** 语义标识(<domain>.<kind>——C-IR semantic / 能力属性) */
|
package/dist/schema.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export interface ComponentIR {
|
|
|
15
15
|
degradation?: Record<string, 'supported' | 'fallback' | 'unsupported'>;
|
|
16
16
|
}
|
|
17
17
|
/** 语义枚举(与 plan component-ir.schema.json 一致)——<domain>.<kind> */
|
|
18
|
-
export declare const SEMANTIC_ENUM: readonly ["layout.box", "layout.inline", "layout.stack", "layout.grid", "layout.fluid", "layout.adaptive", "layout.fit", "layout.spacer", "layout.divider", "layout.scroll", "layout.virtual-list", "layout.masonry", "layout.split", "layout.safe", "layout.sidebar", "layout.aspect", "layout.zone", "ui.text", "ui.heading", "ui.rich-text", "ui.icon", "ui.image", "ui.avatar", "ui.media", "ui.canvas", "ui.svg", "ui.input", "ui.textarea", "ui.select", "ui.checkbox", "ui.radio", "ui.switch", "ui.slider", "ui.picker", "ui.form", "ui.button", "ui.list", "ui.nav", "ui.loading", "ui.scale", "ui.skeleton", "ui.progress", "ui.label", "ui.selection", "ui.camera", "shell.page", "shell.nav", "shell.tabbar", "shell.segment", "shell.drawer", "shell.modal", "shell.popover", "shell.toast", "shell.action-sheet", "shell.mask", "shell.popup", "shell.toolbar", "shell.page-container", "shell.keyboard-accessory", "shell.webview", "shell.ad", "ui.map", "gesture.draggable", "gesture.scrollable", "engineering.router-link", "engineering.transition", "engineering.animate", "engineering.error-boundary", "
|
|
18
|
+
export declare const SEMANTIC_ENUM: readonly ["layout.box", "layout.inline", "layout.stack", "layout.grid", "layout.fluid", "layout.adaptive", "layout.fit", "layout.spacer", "layout.divider", "layout.scroll", "layout.virtual-list", "layout.masonry", "layout.split", "layout.safe", "layout.sidebar", "layout.aspect", "layout.zone", "ui.text", "ui.heading", "ui.rich-text", "ui.icon", "ui.image", "ui.avatar", "ui.media", "ui.canvas", "ui.svg", "ui.input", "ui.textarea", "ui.select", "ui.checkbox", "ui.radio", "ui.switch", "ui.slider", "ui.picker", "ui.form", "ui.button", "ui.list", "ui.nav", "ui.loading", "ui.scale", "ui.skeleton", "ui.progress", "ui.label", "ui.selection", "ui.camera", "shell.page", "shell.nav", "shell.tabbar", "shell.segment", "shell.drawer", "shell.modal", "shell.popover", "shell.toast", "shell.action-sheet", "shell.mask", "shell.popup", "shell.toolbar", "shell.page-container", "shell.keyboard-accessory", "shell.webview", "shell.ad", "ui.map", "gesture.draggable", "gesture.scrollable", "engineering.router-link", "engineering.transition", "engineering.animate", "engineering.error-boundary", "engineering.share-element", "capability.location", "capability.qr-code", "capability.camera"];
|
|
19
19
|
/** C-IR JSON Schema(等价于 plan component-ir.schema.json——TS 内嵌供校验/工具消费) */
|
|
20
20
|
export declare const COMPONENT_IR_SCHEMA: {
|
|
21
21
|
readonly $schema: "https://json-schema.org/draft/2020-12/schema";
|
|
@@ -30,7 +30,7 @@ export declare const COMPONENT_IR_SCHEMA: {
|
|
|
30
30
|
};
|
|
31
31
|
readonly semantic: {
|
|
32
32
|
readonly type: "string";
|
|
33
|
-
readonly enum: readonly ["layout.box", "layout.inline", "layout.stack", "layout.grid", "layout.fluid", "layout.adaptive", "layout.fit", "layout.spacer", "layout.divider", "layout.scroll", "layout.virtual-list", "layout.masonry", "layout.split", "layout.safe", "layout.sidebar", "layout.aspect", "layout.zone", "ui.text", "ui.heading", "ui.rich-text", "ui.icon", "ui.image", "ui.avatar", "ui.media", "ui.canvas", "ui.svg", "ui.input", "ui.textarea", "ui.select", "ui.checkbox", "ui.radio", "ui.switch", "ui.slider", "ui.picker", "ui.form", "ui.button", "ui.list", "ui.nav", "ui.loading", "ui.scale", "ui.skeleton", "ui.progress", "ui.label", "ui.selection", "ui.camera", "shell.page", "shell.nav", "shell.tabbar", "shell.segment", "shell.drawer", "shell.modal", "shell.popover", "shell.toast", "shell.action-sheet", "shell.mask", "shell.popup", "shell.toolbar", "shell.page-container", "shell.keyboard-accessory", "shell.webview", "shell.ad", "ui.map", "gesture.draggable", "gesture.scrollable", "engineering.router-link", "engineering.transition", "engineering.animate", "engineering.error-boundary", "
|
|
33
|
+
readonly enum: readonly ["layout.box", "layout.inline", "layout.stack", "layout.grid", "layout.fluid", "layout.adaptive", "layout.fit", "layout.spacer", "layout.divider", "layout.scroll", "layout.virtual-list", "layout.masonry", "layout.split", "layout.safe", "layout.sidebar", "layout.aspect", "layout.zone", "ui.text", "ui.heading", "ui.rich-text", "ui.icon", "ui.image", "ui.avatar", "ui.media", "ui.canvas", "ui.svg", "ui.input", "ui.textarea", "ui.select", "ui.checkbox", "ui.radio", "ui.switch", "ui.slider", "ui.picker", "ui.form", "ui.button", "ui.list", "ui.nav", "ui.loading", "ui.scale", "ui.skeleton", "ui.progress", "ui.label", "ui.selection", "ui.camera", "shell.page", "shell.nav", "shell.tabbar", "shell.segment", "shell.drawer", "shell.modal", "shell.popover", "shell.toast", "shell.action-sheet", "shell.mask", "shell.popup", "shell.toolbar", "shell.page-container", "shell.keyboard-accessory", "shell.webview", "shell.ad", "ui.map", "gesture.draggable", "gesture.scrollable", "engineering.router-link", "engineering.transition", "engineering.animate", "engineering.error-boundary", "engineering.share-element", "capability.location", "capability.qr-code", "capability.camera"];
|
|
34
34
|
};
|
|
35
35
|
readonly props: {
|
|
36
36
|
readonly type: "object";
|
|
@@ -60,3 +60,44 @@ export declare const COMPONENT_IR_SCHEMA: {
|
|
|
60
60
|
};
|
|
61
61
|
/** C-IR 组件标签 → 语义类型(组件清单映射——G-32 ①-③ 组件原语 + G-31 L1 布局/UI 原语 + ★B4 现有组件对齐) */
|
|
62
62
|
export declare const TAG_SEMANTIC_MAP: Record<string, string>;
|
|
63
|
+
/**
|
|
64
|
+
* ★框架**内部运行时标签**(2026-09-18 批次 7):由编译器产出或仅供内部运行的组件标签——
|
|
65
|
+
* 它们**刻意不获得跨端语义**(不登记进 TAG_SEMANTIC_MAP / PRIMITIVE_CATALOG),
|
|
66
|
+
* 但必须在「未登记 p-*」反黑盒检查中被识别,否则产生**错误告警**。
|
|
67
|
+
*
|
|
68
|
+
* 判定依据(与 G-31 一致:不把平台私有的实现载体上升为框架语义):
|
|
69
|
+
* - `p-svg-canvas`:G-62 SVG 动画降级的 **MP 运行时载体**(`template/svg-canvas` 规则从 `<svg>`
|
|
70
|
+
* 形状变化动画产出,见 packages/compiler/src/template.ts)。组件自身文档写明
|
|
71
|
+
* 「MP-only 运行时组件——离屏 canvas 逐帧绘制依赖 wx.createOffscreenCanvas;Web 端不走此组件,
|
|
72
|
+
* 故无 L2 等价物」→ 它是 `ui.svg` 语义在 MP 端的**实现形态**,不是独立跨端语义。
|
|
73
|
+
* ★修复背景:此前它被 `tag/unknown-p-star` 检查命中,编译期报
|
|
74
|
+
* 「未入库组件?产物将按未注册自定义组件输出(MP 端不渲染)」——**该断言是错的**
|
|
75
|
+
* (组件确实在库中、产物亦由 gen-routes 正确注册),属误导告警(实测 155 个文件受影响面)。
|
|
76
|
+
*
|
|
77
|
+
* 新增条目须给理由;**不得**用它绕过「新组件必须登记语义」的要求——仅限「实现载体」类。
|
|
78
|
+
*/
|
|
79
|
+
export declare const FRAMEWORK_INTERNAL_TAGS: ReadonlySet<string>;
|
|
80
|
+
/**
|
|
81
|
+
* ★多标签共享语义的**显式别名登记**(2026-09-18)。
|
|
82
|
+
*
|
|
83
|
+
* 背景:`checkPrimitiveCatalog` 只校验 **catalog** 的 semantic 唯一,不校验 `TAG_SEMANTIC_MAP`
|
|
84
|
+
* 的**值**唯一——于是「两个标签指向同一语义」这一情形长期**无人校验**,实测存在 3 处:
|
|
85
|
+
* `layout.box`(p-box / p-view)、`layout.scroll`(p-scroll / p-scroll-view)、
|
|
86
|
+
* `engineering.router-link`(p-router-link / router-link)。
|
|
87
|
+
*
|
|
88
|
+
* 语义:这些**不是缺陷**(同一语义可由多个标签提供,如 `router-link` 是 Vue Router 风格兼容别名),
|
|
89
|
+
* 但**必须显式登记 + 给理由 + 指明规范标签**,否则属「悄悄重复」(新增重复值不再可能漏检——
|
|
90
|
+
* 由 auditCatalogConsistency 的 **C6** 强制)。
|
|
91
|
+
*
|
|
92
|
+
* ★规范标签(canonical)判定依据:**端对齐产物且实际被源码引用者**为规范;
|
|
93
|
+
* 另一者为 G-32 血统的兼容别名(历史组件,保留不删——删除属更大决策)。
|
|
94
|
+
* ★实测源码引用(排除构建产物/缓存):p-view 22 · p-scroll-view 2 · p-box 0 · p-scroll 1。
|
|
95
|
+
*/
|
|
96
|
+
export interface TagAliasDecl {
|
|
97
|
+
/** 规范标签(端对齐产物 + 实际使用;catalog 应登记此 tag) */
|
|
98
|
+
canonical: string;
|
|
99
|
+
/** 兼容别名标签 */
|
|
100
|
+
aliases: string[];
|
|
101
|
+
reason: string;
|
|
102
|
+
}
|
|
103
|
+
export declare const TAG_SEMANTIC_ALIASES: Record<string, TagAliasDecl>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@proteus-vue/component-ir",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0-beta.10",
|
|
4
4
|
"description": "G-31 组件与 API 语义化(component-semantics-plan B1):Component IR(C-IR)schema + 属性约束校验 + semantic 映射——组件=语义、属性=约束、Backend 消费 semantic 而非 tag(零依赖纯逻辑)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"README.md"
|
|
20
20
|
],
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@proteus-vue/contracts": "0.
|
|
22
|
+
"@proteus-vue/contracts": "0.3.0-beta.10"
|
|
23
23
|
},
|
|
24
24
|
"scripts": {
|
|
25
25
|
"build": "tsc -p tsconfig.build.json --emitDeclarationOnly && esbuild src/index.ts --bundle --format=esm --platform=neutral --outfile=dist/index.js"
|