@proteus-vue/component-ir 0.3.0-beta.11 → 0.3.0-beta.23
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/dist/capability-domains.d.ts +13 -0
- package/dist/color.d.ts +18 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +1484 -21
- package/dist/paint-hint.d.ts +40 -0
- package/dist/pnode-analyze.d.ts +42 -0
- package/dist/pnode-build.d.ts +59 -0
- package/dist/pnode-style.d.ts +50 -0
- package/dist/pnode-trace.d.ts +21 -0
- package/dist/pnode.d.ts +216 -0
- package/dist/schema.d.ts +2 -2
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -20,6 +20,8 @@ var SEMANTIC_ENUM = [
|
|
|
20
20
|
// ★#405 语义登记批:组件库扩展组件(Fluid S1-S4 + 工程兜底,语义层待 L2 落地——G-31.4 降级 planned)
|
|
21
21
|
"layout.aspect",
|
|
22
22
|
"layout.zone",
|
|
23
|
+
"layout.formfactor",
|
|
24
|
+
// ★★Fluid System v2:柔性形态容器(形态编排)
|
|
23
25
|
// —— 基础 UI 原语(G-32 ② UI 18 + 既有按钮)——
|
|
24
26
|
"ui.text",
|
|
25
27
|
"ui.heading",
|
|
@@ -152,8 +154,17 @@ var TAG_SEMANTIC_MAP = {
|
|
|
152
154
|
"p-segment": "shell.segment",
|
|
153
155
|
"p-drawer": "shell.drawer",
|
|
154
156
|
"p-modal": "shell.modal",
|
|
157
|
+
// ★★★GP4-c(2026-10-03):登录失效拦截弹窗——与 p-modal **同语义**(模态弹窗)、
|
|
158
|
+
// **不同可取消性**(本组件不可取消:无关闭按钮/点遮罩不关;唯一出口是登录态恢复)。
|
|
159
|
+
// ★不登记 ⇒ 编译期按"未注册自定义组件"输出 ⇒ MP 端不渲染(M3 门禁会当场红)
|
|
160
|
+
"p-auth-gate": "shell.modal",
|
|
155
161
|
"p-popover": "shell.popover",
|
|
156
162
|
"p-toast": "shell.toast",
|
|
163
|
+
// ★★★GP4-a/b(2026-10-03):两个**浮层宿主**同属其能力的语义(multi-tag 别名——同 ui.loading 的先例)。
|
|
164
|
+
// · p-toast-host:Toast 队列的渲染端(构建期按需注入)
|
|
165
|
+
// · p-loading-host:Loading 多实例的渲染端(同上)
|
|
166
|
+
// ★不登记 ⇒ 编译期按"未注册自定义组件"输出 ⇒ **MP 端不渲染**(M3 门禁当场抓出)
|
|
167
|
+
"p-toast-host": "shell.toast",
|
|
157
168
|
"p-action-sheet": "shell.action-sheet",
|
|
158
169
|
"p-split": "layout.split",
|
|
159
170
|
// ★已落地绑定(G-32 S10 分栏语义——layout.split 承载)
|
|
@@ -191,7 +202,16 @@ var TAG_SEMANTIC_MAP = {
|
|
|
191
202
|
// ——同一语义由两个标签提供;本条与 p-view 是并列的别名情形,非「先例」关系)
|
|
192
203
|
"p-aspect": "layout.aspect",
|
|
193
204
|
"p-zone": "layout.zone",
|
|
205
|
+
"p-formfactor": "layout.formfactor",
|
|
206
|
+
// ★★Fluid System v2:柔性形态容器
|
|
194
207
|
"p-loading": "ui.loading",
|
|
208
|
+
// ★★★GP4-b(2026-10-03):Loading **多实例宿主**与**区域遮罩**同属 ui.loading 语义(multi-tag 别名,
|
|
209
|
+
// 与上一行 `router-link`/`p-view` 的"同一语义多标签"同款)。
|
|
210
|
+
// · p-loading-host:命令式多实例的渲染端(由构建期按需注入——用户一般不手写)
|
|
211
|
+
// · p-loading-region:区域遮罩(就地包裹,用户手写)
|
|
212
|
+
// ★不登记 ⇒ 编译期按"未注册自定义组件"输出 ⇒ MP 端不渲染(M3 门禁当场抓出——它是对的)
|
|
213
|
+
"p-loading-host": "ui.loading",
|
|
214
|
+
"p-loading-region": "ui.loading",
|
|
195
215
|
"p-scale": "ui.scale",
|
|
196
216
|
"p-skeleton": "ui.skeleton",
|
|
197
217
|
"p-mask": "shell.mask",
|
|
@@ -231,6 +251,28 @@ var TAG_SEMANTIC_ALIASES = {
|
|
|
231
251
|
canonical: "p-router-link",
|
|
232
252
|
aliases: ["router-link"],
|
|
233
253
|
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"
|
|
254
|
+
},
|
|
255
|
+
// ★★★GP4-a/b(2026-10-03):两个浮层能力的**宿主标签**与其声明式组件共享语义——
|
|
256
|
+
// 与上两条"实现替换/血统并存"的别名不同,这里是**职责分工**(声明式 vs 命令式渲染端):
|
|
257
|
+
// · `p-toast` = 声明式单条提示(用户摆位置)|`p-toast-host` = Toast 队列的**渲染端**
|
|
258
|
+
// (命令式 `showToast` 的宿主;构建期按需注入,**用户一般不手写**)
|
|
259
|
+
// · `p-loading` = 声明式遮罩(单实例) |`p-loading-host` = Loading **多实例**宿主(同上)
|
|
260
|
+
// · `p-loading-region` = 区域遮罩(就地包裹;用户手写,与 p-loading 是不同用法而非替代)
|
|
261
|
+
// ★语义相同(都是"提示/加载态")故共享 primitive;**保留两标签**因为用法与生命周期都不同。
|
|
262
|
+
"shell.modal": {
|
|
263
|
+
canonical: "p-modal",
|
|
264
|
+
aliases: ["p-auth-gate"],
|
|
265
|
+
reason: 'p-modal \u662F\u53EF\u53D6\u6D88\u5F39\u5C42\uFF08\u6709\u5173\u95ED\u6309\u94AE/\u70B9\u906E\u7F69\u53EF\u5173\uFF09\uFF1Bp-auth-gate \u662F**\u4E0D\u53EF\u53D6\u6D88**\u7684\u767B\u5F55\u5931\u6548\u62E6\u622A\u5F39\u7A97\uFF08\u552F\u4E00\u51FA\u53E3\u662F\u767B\u5F55\u6001\u6062\u590D\uFF09\u3002\u540C\u8BED\u4E49\uFF08\u6A21\u6001\u5F39\u7A97\uFF09\u4E0D\u540C\u53EF\u53D6\u6D88\u6027 \u21D2 \u4FDD\u7559\u4E24\u6807\u7B7E\u800C\u975E\u5408\u5E76\uFF08\u628A"\u4E0D\u53EF\u53D6\u6D88"\u505A\u6210 p-modal \u7684\u5F00\u5173\uFF0C\u4F1A\u8BA9"\u53EF\u5173"\u7684\u9ED8\u8BA4\u914D\u7F6E\u6709\u673A\u4F1A\u88AB\u8BEF\u7528\u5230\u62E6\u622A\u573A\u666F\uFF09\u3002'
|
|
266
|
+
},
|
|
267
|
+
"shell.toast": {
|
|
268
|
+
canonical: "p-toast",
|
|
269
|
+
aliases: ["p-toast-host"],
|
|
270
|
+
reason: "p-toast \u662F\u58F0\u660E\u5F0F\u5355\u6761\u63D0\u793A\uFF08\u7236\u7EA7\u7ED9 visible/text\uFF09\uFF1Bp-toast-host \u662F\u540C\u8BED\u4E49\u7684**\u547D\u4EE4\u5F0F\u6E32\u67D3\u7AEF**\uFF08\u8BA2\u9605 runtime \u961F\u5217\uFF0C\u7531\u6784\u5EFA\u671F\u6309\u9700\u6CE8\u5165\u6BCF\u9875\uFF09\u2014\u2014\u4E24\u8005\u7528\u9014\u4E0D\u540C\uFF0C\u6545\u5E76\u5B58\u800C\u975E\u66FF\u4EE3\u3002"
|
|
271
|
+
},
|
|
272
|
+
"ui.loading": {
|
|
273
|
+
canonical: "p-loading",
|
|
274
|
+
aliases: ["p-loading-host", "p-loading-region"],
|
|
275
|
+
reason: "p-loading \u662F\u58F0\u660E\u5F0F\u906E\u7F69\uFF08\u5355\u5B9E\u4F8B\uFF09\uFF1Bp-loading-host \u662F\u540C\u8BED\u4E49\u7684**\u547D\u4EE4\u5F0F\u591A\u5B9E\u4F8B\u6E32\u67D3\u7AEF**\uFF08\u6784\u5EFA\u671F\u6CE8\u5165\uFF09\uFF1Bp-loading-region \u662F**\u533A\u57DF\u906E\u7F69**\uFF08\u5C31\u5730\u5305\u88F9\u3001\u8D34\u5408\u76D2\u5B50\u5C3A\u5BF8\uFF09\u3002\u4E09\u8005\u662F\u540C\u4E00\u8BED\u4E49\u7684\u4E09\u79CD\u7528\u6CD5\uFF08\u5168\u5C4F\u58F0\u660E\u5F0F/\u5168\u5C4F\u547D\u4EE4\u5F0F/\u533A\u57DF\u5185\uFF09\uFF0C\u6545\u5171\u4EAB primitive \u800C\u4FDD\u7559\u5404\u81EA\u6807\u7B7E\u3002"
|
|
234
276
|
}
|
|
235
277
|
};
|
|
236
278
|
|
|
@@ -386,6 +428,16 @@ var SEMANTIC_BACKEND_MAP = {
|
|
|
386
428
|
flutter: "NavigationRail",
|
|
387
429
|
headless: "sidebar"
|
|
388
430
|
},
|
|
431
|
+
// ★★Fluid System v2(2026-09-26):柔性形态容器——各端按形态画像编排拓扑/能力/密度/缩放
|
|
432
|
+
"layout.formfactor": {
|
|
433
|
+
"vue-dom": "div.proteus-formfactor",
|
|
434
|
+
"native-ios": "UIViewController.formFactor",
|
|
435
|
+
"native-android": "FormFactorLayout",
|
|
436
|
+
"native-harmony": "GridRow.formFactor",
|
|
437
|
+
skyline: "view.formfactor",
|
|
438
|
+
flutter: "LayoutBuilder.formFactor",
|
|
439
|
+
headless: "formfactor"
|
|
440
|
+
},
|
|
389
441
|
// —— 基础 UI 原语 ——
|
|
390
442
|
"ui.text": {
|
|
391
443
|
"vue-dom": "span",
|
|
@@ -453,8 +505,183 @@ var SEMANTIC_BACKEND_MAP = {
|
|
|
453
505
|
flutter: "scanQR",
|
|
454
506
|
headless: "scan-qr"
|
|
455
507
|
},
|
|
508
|
+
// ★★NC1 首个声明式能力(2026-09-30,C83):屏幕常亮。
|
|
509
|
+
// ★落地状态如实标注(两类):**已接线** = vue-dom(`navigator.wakeLock`,生成桥真跑)
|
|
510
|
+
// + skyline(`wx.setKeepScreenOn`,生成桥真跑);**声明** = native-ios/android/harmony
|
|
511
|
+
// (平台真实 API,按本表既有口径登记——与 capability.qr-code 的 AVCaptureSession 同性质,
|
|
512
|
+
// 属设计声明层;native 宿主接线是后续批次)。
|
|
513
|
+
"capability.keep-screen-on": {
|
|
514
|
+
"vue-dom": "navigator.wakeLock",
|
|
515
|
+
"native-ios": "UIApplication.isIdleTimerDisabled",
|
|
516
|
+
"native-android": "Window.FLAG_KEEP_SCREEN_ON",
|
|
517
|
+
"native-harmony": "window.setWindowKeepScreenOn",
|
|
518
|
+
skyline: "wx.setKeepScreenOn",
|
|
519
|
+
flutter: "keepScreenOn",
|
|
520
|
+
headless: "keep-screen-on"
|
|
521
|
+
},
|
|
522
|
+
// ★★NC1 第二个声明式能力(2026-09-30,C84):文档预览。
|
|
523
|
+
// ★落地状态如实分两类:**已接线** = vue-dom(`window.open`)/ skyline(`wx.openDocument`,生成桥真跑);
|
|
524
|
+
// **声明** = native 两端(平台真实 API:iOS QLPreviewController / Android ACTION_VIEW,
|
|
525
|
+
// 与既有 capability.qr-code 的 AVCaptureSession 同性质,属设计声明层)。
|
|
526
|
+
// ★**native-harmony 有意省略**:鸿蒙文档预览 API 名未经取证,**不编造**(≥3 端映射判据不受影响)。
|
|
527
|
+
"capability.document-preview": {
|
|
528
|
+
"vue-dom": "window.open",
|
|
529
|
+
"native-ios": "QLPreviewController",
|
|
530
|
+
"native-android": "Intent.ACTION_VIEW",
|
|
531
|
+
skyline: "wx.openDocument",
|
|
532
|
+
flutter: "open_file",
|
|
533
|
+
headless: "open-document"
|
|
534
|
+
},
|
|
535
|
+
// ★★2026-09-30 反馈组件补齐(U19 shell.toast / S8 ui.loading):
|
|
536
|
+
// 组件本体(`p-toast` / `p-loading`)**早已存在且已注册**(`packages/components/index.ts`)
|
|
537
|
+
// 并有 MP 产物级测试(`tests/component-b5.test.ts`:wx:if/定时器/A@keyframes 断言)——
|
|
538
|
+
// 缺的只是**本表的后端映射登记**(G-31.4 门禁要求 ≥3 端才可标 implemented,
|
|
539
|
+
// 而 catalog 的 status 因此一直停在 planned ⇒ 与"组件已能用"的事实不符)。
|
|
540
|
+
// ⇒ 本批补齐映射后,catalog status 据实转 implemented(**修的是记账,不是能力**)。
|
|
541
|
+
// ★对照:`wx.showToast` / `wx.showLoading`(命令式全局提示)是**另一条路**——
|
|
542
|
+
// 前者由本组件承接(声明式、可跨端、样式可控),后者仍属 MP 官方 API 表(见 SPEC_PLANNED,
|
|
543
|
+
// 归"全局命令式提示"待补)。两者不冲突:组件是推荐路径,命令式是兼容路径。
|
|
544
|
+
"shell.toast": {
|
|
545
|
+
"vue-dom": "div.proteus-toast",
|
|
546
|
+
"native-ios": "UIView.toast",
|
|
547
|
+
"native-android": "Toast",
|
|
548
|
+
"native-harmony": "promptAction.showToast",
|
|
549
|
+
skyline: "view.toast",
|
|
550
|
+
// Skyline 无原生 toast 组件 → 用 view 自绘(组件本体即此策略)
|
|
551
|
+
flutter: "ScaffoldMessenger.showSnackBar",
|
|
552
|
+
headless: "toast"
|
|
553
|
+
},
|
|
554
|
+
"ui.loading": {
|
|
555
|
+
"vue-dom": "div.proteus-loading",
|
|
556
|
+
"native-ios": "UIActivityIndicatorView",
|
|
557
|
+
"native-android": "ProgressBar",
|
|
558
|
+
"native-harmony": "LoadingProgress",
|
|
559
|
+
skyline: "view.loading",
|
|
560
|
+
// 同上:自绘 spinner(组件内 @keyframes 动画)
|
|
561
|
+
flutter: "CircularProgressIndicator",
|
|
562
|
+
headless: "loading"
|
|
563
|
+
},
|
|
456
564
|
// ★2026-09-18 语义去重:原键 capability.pick-photo 为重复名(组件实现调用 useCamera(),
|
|
457
565
|
// 真实能力即 C1 capability.camera)——键已迁移,映射原样保留。
|
|
566
|
+
// ★★2026-09-30:生命周期三能力的后端映射(C23/C24/C25 随 App 腿落地转为 implemented)。
|
|
567
|
+
// 各行含义:**平台真实触发源**(不是渲染类型——这三个是"事件源"类能力)。
|
|
568
|
+
// · native-ios/android:系统生命周期回调(iOS `willResignActive`/Android `onPause`——本轮真机实证)
|
|
569
|
+
// · vue-dom / skyline:既有实现(`visibilitychange` / `wx.onAppShow`)
|
|
570
|
+
// · headless:确定性注入(测试夹具)
|
|
571
|
+
"capability.app-lifecycle": {
|
|
572
|
+
"vue-dom": "document.visibilitychange",
|
|
573
|
+
"native-ios": "UIApplication.willResignActive",
|
|
574
|
+
"native-android": "Activity.onPause",
|
|
575
|
+
skyline: "wx.onAppShow",
|
|
576
|
+
flutter: "AppLifecycleState",
|
|
577
|
+
headless: "app-lifecycle-bus"
|
|
578
|
+
},
|
|
579
|
+
"capability.page-lifecycle": {
|
|
580
|
+
"vue-dom": "visibilitychange+history",
|
|
581
|
+
"native-ios": "UINavigationController.didShowViewController",
|
|
582
|
+
"native-android": "Fragment.onResume",
|
|
583
|
+
skyline: "Page.onShow",
|
|
584
|
+
flutter: "RouteAware.didPush",
|
|
585
|
+
headless: "page-lifecycle-bus"
|
|
586
|
+
},
|
|
587
|
+
"capability.background": {
|
|
588
|
+
"vue-dom": "document.hidden",
|
|
589
|
+
"native-ios": "UIApplication.didEnterBackground",
|
|
590
|
+
"native-android": "Activity.onStop",
|
|
591
|
+
skyline: "wx.onBackgroundAudioInterruptionBegin",
|
|
592
|
+
flutter: "AppLifecycleState.paused",
|
|
593
|
+
headless: "background-bus"
|
|
594
|
+
},
|
|
595
|
+
// ★2026-09-30 应用与生命周期域收口(能力已落地)
|
|
596
|
+
"capability.mini-program": {
|
|
597
|
+
"vue-dom": "window.open (cross-app n/a)",
|
|
598
|
+
"native-ios": "UIApplication.open",
|
|
599
|
+
"native-android": "Intent.ACTION_VIEW",
|
|
600
|
+
"skyline": "wx.navigateToMiniProgram",
|
|
601
|
+
"flutter": "url_launcher",
|
|
602
|
+
"headless": "mini-program-nav"
|
|
603
|
+
},
|
|
604
|
+
// ★2026-09-30 应用与生命周期域收口(能力已落地)
|
|
605
|
+
"capability.embedded": {
|
|
606
|
+
"vue-dom": "window.parent (preset)",
|
|
607
|
+
"native-ios": "UIApplicationDelegate.host",
|
|
608
|
+
"native-android": "Activity.getIntent",
|
|
609
|
+
"skyline": "wx.getEnterOptionsSync",
|
|
610
|
+
"flutter": "PlatformDispatcher.host",
|
|
611
|
+
"headless": "host-context"
|
|
612
|
+
},
|
|
613
|
+
// ★2026-09-30 应用与生命周期域收口(能力已落地)
|
|
614
|
+
"capability.extension": {
|
|
615
|
+
"vue-dom": "dynamic import()",
|
|
616
|
+
"native-ios": "NSBundle.load",
|
|
617
|
+
"native-android": "DexClassLoader",
|
|
618
|
+
"skyline": "plugin shell (host)",
|
|
619
|
+
"flutter": "dynamic library load",
|
|
620
|
+
"headless": "extension-loader"
|
|
621
|
+
},
|
|
622
|
+
// ★2026-09-30 应用与生命周期域收口(能力已落地)
|
|
623
|
+
"capability.update": {
|
|
624
|
+
"vue-dom": "ServiceWorkerRegistration",
|
|
625
|
+
"native-ios": "Bundle version check",
|
|
626
|
+
"native-android": "Play Core In-App Updates",
|
|
627
|
+
"skyline": "wx.getUpdateManager",
|
|
628
|
+
"flutter": "upgrader",
|
|
629
|
+
"headless": "update-manager"
|
|
630
|
+
},
|
|
631
|
+
// ★2026-09-30 应用与生命周期域收口(能力已落地)
|
|
632
|
+
"capability.worker": {
|
|
633
|
+
"vue-dom": "new Worker()",
|
|
634
|
+
"native-ios": "DispatchQueue.global",
|
|
635
|
+
"native-android": "ThreadPoolExecutor",
|
|
636
|
+
"skyline": "wx.createWorker",
|
|
637
|
+
"flutter": "Isolate.spawn",
|
|
638
|
+
"headless": "worker-thread"
|
|
639
|
+
},
|
|
640
|
+
// ★2026-09-30 应用与生命周期域收口(能力已落地)
|
|
641
|
+
"capability.preload": {
|
|
642
|
+
"vue-dom": "link rel=preload",
|
|
643
|
+
"native-ios": "NSBundle preload",
|
|
644
|
+
"native-android": "Resources preload",
|
|
645
|
+
"skyline": "wx.preloadAssets",
|
|
646
|
+
"flutter": "precacheImage",
|
|
647
|
+
"headless": "preload"
|
|
648
|
+
},
|
|
649
|
+
// ★2026-09-30 应用与生命周期域收口(能力已落地)
|
|
650
|
+
"capability.idle": {
|
|
651
|
+
"vue-dom": "requestIdleCallback",
|
|
652
|
+
"native-ios": "CADisplayLink idle",
|
|
653
|
+
"native-android": "Choreographer idle",
|
|
654
|
+
"skyline": "wx.requestIdleCallback",
|
|
655
|
+
"flutter": "SchedulerBinding.idle",
|
|
656
|
+
"headless": "idle-callback"
|
|
657
|
+
},
|
|
658
|
+
// ★2026-09-30 应用与生命周期域收口(能力已落地)
|
|
659
|
+
"capability.window": {
|
|
660
|
+
"vue-dom": "window.resizeTo (limited)",
|
|
661
|
+
"native-ios": "UIWindowScene.requestGeometryUpdate",
|
|
662
|
+
"native-android": "WindowManager.LayoutParams",
|
|
663
|
+
"skyline": "wx.setWindowSize",
|
|
664
|
+
"flutter": "WindowManager",
|
|
665
|
+
"headless": "window-manager"
|
|
666
|
+
},
|
|
667
|
+
// ★2026-09-30 应用与生命周期域收口(能力已落地)
|
|
668
|
+
"capability.navigation-guard": {
|
|
669
|
+
"vue-dom": "beforeunload",
|
|
670
|
+
"native-ios": "UINavigationController.delegate",
|
|
671
|
+
"native-android": "OnBackPressedDispatcher",
|
|
672
|
+
"skyline": "wx.enableAlertBeforeUnload",
|
|
673
|
+
"flutter": "PopScope",
|
|
674
|
+
"headless": "nav-guard"
|
|
675
|
+
},
|
|
676
|
+
// ★2026-09-30 应用与生命周期域收口(能力已落地)
|
|
677
|
+
"capability.webassembly": {
|
|
678
|
+
"vue-dom": "WebAssembly.instantiate",
|
|
679
|
+
"native-ios": "JavaScriptCore WebAssembly",
|
|
680
|
+
"native-android": "QuickJS WebAssembly",
|
|
681
|
+
"skyline": "WXWebAssembly.instantiate",
|
|
682
|
+
"flutter": "wasm bindings",
|
|
683
|
+
"headless": "wasm"
|
|
684
|
+
},
|
|
458
685
|
"capability.camera": {
|
|
459
686
|
"vue-dom": "input.proteus-pick-photo",
|
|
460
687
|
"native-ios": "UIImagePicker",
|
|
@@ -995,6 +1222,8 @@ var LAYOUT = [
|
|
|
995
1222
|
// ★#405 语义登记批:Fluid 体系剩余组件(语义层待多端映射 → planned L2——G-31.4 不足 3 端降级)
|
|
996
1223
|
{ 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" },
|
|
997
1224
|
{ 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" },
|
|
1225
|
+
// ★★Fluid System v2(2026-09-26):柔性形态容器——按设备形态自动编排布局拓扑/能力/密度/缩放
|
|
1226
|
+
{ id: "L26", kind: "layout", semantic: "layout.formfactor", tag: "p-formfactor", props: ["declared", "width", "height"], mpEquiv: "\u65E0\uFF08\u5F62\u6001\u7F16\u6392\u5BB9\u5668\uFF1Aglance/stack/duo/rail-split/rail-grid/dashboard/hero-focus-row\uFF09", tier: "L2", status: "implemented" },
|
|
998
1227
|
// ★批次 7(2026-09-18)未登记组件补登记:安全区(刘海/折叠屏/降级)+ 侧边导航容器
|
|
999
1228
|
{ 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
1229
|
{ 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" }
|
|
@@ -1019,7 +1248,9 @@ var UI = [
|
|
|
1019
1248
|
{ id: "U17", kind: "ui", semantic: "ui.picker", tag: "p-picker", props: ["mode", "start", "end", "indicatorStyle", "indicatorClass", "maskClass", "maskStyle", "immediateChange"], mpEquiv: "<picker>", tier: "L1", status: "implemented" },
|
|
1020
1249
|
{ id: "U18", kind: "ui", semantic: "ui.form", tag: "p-form", props: ["model", "rules", "layout"], mpEquiv: "\u7EC4\u5408", tier: "L1", status: "implemented" },
|
|
1021
1250
|
// ★#405 语义登记批:反馈/状态类组件
|
|
1022
|
-
|
|
1251
|
+
// ★2026-09-30 反馈组件补齐:组件本体(p-loading)与 MP 产物测试早已存在,
|
|
1252
|
+
// 缺的只是 SEMANTIC_BACKEND_MAP 的后端映射登记 ⇒ 补齐后据实转 implemented(**修记账非修能力**)
|
|
1253
|
+
{ id: "U19", kind: "ui", semantic: "ui.loading", tag: "p-loading", props: ["size", "text"], mpEquiv: "wx.showLoading \u90E8\u5206", tier: "L1", status: "implemented" },
|
|
1023
1254
|
{ id: "U20", kind: "ui", semantic: "ui.scale", tag: "p-scale", props: ["level", "density", "baseSize"], mpEquiv: "\u65E0\uFF08\u65E0\u969C\u788D\u6863\u4F4D\uFF09", tier: "L2", status: "planned" },
|
|
1024
1255
|
{ id: "U21", kind: "ui", semantic: "ui.skeleton", tag: "p-skeleton", props: ["rows", "avatar", "animated"], mpEquiv: "\u65E0", tier: "L2", status: "planned" },
|
|
1025
1256
|
// ★能力颗粒度对齐 C2:进度条 / 表单标签
|
|
@@ -1047,7 +1278,8 @@ var SHELL = [
|
|
|
1047
1278
|
{ id: "S5", kind: "shell", semantic: "shell.drawer", tag: "p-drawer", props: ["side", "width", "overlay"], mpEquiv: "\u7EC4\u5408", tier: "L1", status: "implemented" },
|
|
1048
1279
|
{ id: "S6", kind: "shell", semantic: "shell.modal", tag: "p-modal", props: ["open", "dismissible", "sheet", "dialog", "alert"], mpEquiv: "<modal>+wx.showModal", tier: "L1", status: "implemented" },
|
|
1049
1280
|
{ id: "S7", kind: "shell", semantic: "shell.popover", tag: "p-popover", props: ["trigger", "placement"], mpEquiv: "\u7EC4\u5408", tier: "L1", status: "implemented" },
|
|
1050
|
-
|
|
1281
|
+
// ★2026-09-30 同上(p-toast 已有 MP 产物测试 + 7 端映射 ⇒ 据实转 implemented)
|
|
1282
|
+
{ id: "S8", kind: "shell", semantic: "shell.toast", tag: "p-toast", props: ["message", "duration", "type"], mpEquiv: "wx.showToast", tier: "L1", status: "implemented" },
|
|
1051
1283
|
{ id: "S9", kind: "shell", semantic: "shell.action-sheet", tag: "p-action-sheet", props: ["actions", "cancel"], mpEquiv: "wx.showActionSheet", tier: "L1", status: "implemented" },
|
|
1052
1284
|
{ 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" },
|
|
1053
1285
|
// ★已落地绑定:分栏语义由 layout.split 承载(G-32 文档为 shell.split——机器事实以实现为准)
|
|
@@ -1108,9 +1340,13 @@ var CAPABILITY = [
|
|
|
1108
1340
|
cap("C20", "calendar", "useCalendar()", "wx.addPhoneCalendar", "CalendarAPI"),
|
|
1109
1341
|
cap("C21", "phone-call", "usePhoneCall()", "wx.makePhoneCall", "Result<void>"),
|
|
1110
1342
|
cap("C22", "sms", "useSMS()", "wx.??\uFF08\u53D7\u9650\uFF09", "Result<void>"),
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1343
|
+
// ★★2026-09-30:C23/C24/C25 App 宿主腿落地(此前仅 wx/web 两桥 ⇒ App 端形同虚设)——
|
|
1344
|
+
// App 桥 `packages/api/src/capability-app.ts`(总线 + 三能力 + 虚拟栈页面事件翻译器);
|
|
1345
|
+
// 真机判据 `check:host-runtime` ①~⑤(冷启动补 launch / 真实栈驱动页面生命周期 /
|
|
1346
|
+
// 壳真实事件逐条驱动总线 / 启动参数经 job 泵 / 阶段快照)。
|
|
1347
|
+
{ id: "C23", kind: "capability", semantic: "capability.app-lifecycle", api: "useAppLifecycle()", props: ["LifecycleHooks"], mpEquiv: "App.onLaunch/onShow", tier: "L1", status: "implemented" },
|
|
1348
|
+
{ id: "C24", kind: "capability", semantic: "capability.page-lifecycle", api: "usePageLifecycle()", props: ["LifecycleHooks"], mpEquiv: "Page.onLoad/onShow", tier: "L1", status: "implemented" },
|
|
1349
|
+
{ id: "C25", kind: "capability", semantic: "capability.background", api: "useBackground()", props: ["BackgroundAPI"], mpEquiv: "wx.onBackground", tier: "L1", status: "implemented" },
|
|
1114
1350
|
// 7.3 通信/数据(10)
|
|
1115
1351
|
cap("C26", "fetch", "useFetch()", "wx.request", "Promise<T>"),
|
|
1116
1352
|
cap("C27", "websocket", "useWebSocket()", "wx.connectSocket", "WSConnection"),
|
|
@@ -1136,15 +1372,15 @@ var CAPABILITY = [
|
|
|
1136
1372
|
cap("C45", "shortcut", "useShortcut()", "wx.addToDesktop", "Result<void>"),
|
|
1137
1373
|
cap("C46", "in-app-purchase", "useInAppPurchase()", "wx.requestPayment \u6269\u5C55", "Result<Receipt>"),
|
|
1138
1374
|
// 7.5 平台特有(4——C47-C50,此前 7.4 末 C46,总 50)
|
|
1139
|
-
{ id: "C47", kind: "capability", semantic: "capability.mini-program", api: "useMiniProgram()", props: ["MPContext"], mpEquiv: "wx.navigateToMiniProgram", tier: "L1", status: "
|
|
1140
|
-
{ id: "C48", kind: "capability", semantic: "capability.embedded", api: "useEmbedded()", props: ["HostContext"], mpEquiv: "\u65E0\uFF08\u88AB\u5BBF\u4E3B\u5D4C\u5165\uFF09", tier: "L1", status: "
|
|
1375
|
+
{ id: "C47", kind: "capability", semantic: "capability.mini-program", api: "useMiniProgram()", props: ["MPContext"], mpEquiv: "wx.navigateToMiniProgram", tier: "L1", status: "implemented" },
|
|
1376
|
+
{ id: "C48", kind: "capability", semantic: "capability.embedded", api: "useEmbedded()", props: ["HostContext"], mpEquiv: "\u65E0\uFF08\u88AB\u5BBF\u4E3B\u5D4C\u5165\uFF09", tier: "L1", status: "implemented" },
|
|
1141
1377
|
{ id: "C49", kind: "capability", semantic: "capability.live", api: "useLive()", props: ["LiveRoom"], mpEquiv: "wx...\uFF08\u76F4\u64AD\u7EC4\u4EF6\uFF09", tier: "L1", status: "planned" },
|
|
1142
|
-
{ 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: "
|
|
1378
|
+
{ 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: "implemented" },
|
|
1143
1379
|
// ★颗粒度对齐 C(2026-09-11):C51 小程序热更新(wx.getUpdateManager)
|
|
1144
|
-
{ id: "C51", kind: "capability", semantic: "capability.update", api: "useUpdate()", props: ["UpdateManagerAPI"], mpEquiv: "wx.getUpdateManager", tier: "L1", status: "
|
|
1380
|
+
{ id: "C51", kind: "capability", semantic: "capability.update", api: "useUpdate()", props: ["UpdateManagerAPI"], mpEquiv: "wx.getUpdateManager", tier: "L1", status: "implemented" },
|
|
1145
1381
|
// ★颗粒度对齐 C3(2026-09-11):相册 / Worker(对齐小程序媒体与多线程 API——纯 Hook,无 C-IR 节点 → planned)
|
|
1146
1382
|
{ id: "C52", kind: "capability", semantic: "capability.album", api: "useAlbum()", props: ["AlbumAPI"], mpEquiv: "wx.chooseMedia/saveImageToPhotosAlbum/previewMedia", tier: "L1", status: "planned" },
|
|
1147
|
-
{ id: "C53", kind: "capability", semantic: "capability.worker", api: "useWorker()", props: ["WorkerHandle"], mpEquiv: "wx.createWorker", tier: "L1", status: "
|
|
1383
|
+
{ id: "C53", kind: "capability", semantic: "capability.worker", api: "useWorker()", props: ["WorkerHandle"], mpEquiv: "wx.createWorker", tier: "L1", status: "implemented" },
|
|
1148
1384
|
// ★颗粒度对齐 C3 批 2:收货地址 / WiFi / 微信运动(对齐小程序系统 API——纯 Hook → planned)
|
|
1149
1385
|
{ id: "C54", kind: "capability", semantic: "capability.address", api: "useAddress()", props: ["ShippingAddress"], mpEquiv: "wx.chooseAddress", tier: "L1", status: "planned" },
|
|
1150
1386
|
{ id: "C55", kind: "capability", semantic: "capability.wifi", api: "useWifi()", props: ["WifiAPI"], mpEquiv: "wx.getConnectedWifi/getWifiList/connectWifi", tier: "L1", status: "planned" },
|
|
@@ -1164,7 +1400,7 @@ var CAPABILITY = [
|
|
|
1164
1400
|
{ id: "C65", kind: "capability", semantic: "capability.privacy", api: "usePrivacy()", props: ["PrivacyAPI"], mpEquiv: "wx.getPrivacySetting/openPrivacyContract/requirePrivacyAuthorize", tier: "L1", status: "planned" },
|
|
1165
1401
|
// ★权威标尺缺口补齐批 D(2026-09-12):性能 / 预加载 / 图像编辑(通用能力,无资质门槛)
|
|
1166
1402
|
{ id: "C66", kind: "capability", semantic: "capability.performance", api: "usePerformance()", props: ["PerformanceAPI"], mpEquiv: "wx.getPerformance/reportPerformance", tier: "L1", status: "planned" },
|
|
1167
|
-
{ id: "C67", kind: "capability", semantic: "capability.preload", api: "usePreload()", props: ["PreloadAPI"], mpEquiv: "wx.preloadAssets/preloadSkylineView/preloadWebview/preDownloadSubpackage", tier: "L1", status: "
|
|
1403
|
+
{ id: "C67", kind: "capability", semantic: "capability.preload", api: "usePreload()", props: ["PreloadAPI"], mpEquiv: "wx.preloadAssets/preloadSkylineView/preloadWebview/preDownloadSubpackage", tier: "L1", status: "implemented" },
|
|
1168
1404
|
{ id: "C68", kind: "capability", semantic: "capability.image-edit", api: "useImageEdit()", props: ["ImageEditAPI"], mpEquiv: "wx.cropImage/editImage", tier: "L1", status: "planned" },
|
|
1169
1405
|
// ★权威标尺缺口补齐批 E(2026-09-12):网络底层 / 媒体高级
|
|
1170
1406
|
{ id: "C69", kind: "capability", semantic: "capability.socket", api: "useSocket()", props: ["UDPSocketHandle / TCPSocketHandle"], mpEquiv: "wx.createUDPSocket/createTCPSocket", tier: "L1", status: "planned" },
|
|
@@ -1172,16 +1408,37 @@ var CAPABILITY = [
|
|
|
1172
1408
|
// ★权威标尺缺口补齐批 F(2026-09-12):录屏/缓存/空闲/窗口/导航拦截
|
|
1173
1409
|
{ id: "C71", kind: "capability", semantic: "capability.screen-capture", api: "useScreenCapture()", props: ["ScreenCaptureAPI"], mpEquiv: "wx.getScreenRecordingState/onScreenRecordingStateChanged/onUserCaptureScreen/checkIsPictureInPictureActive", tier: "L1", status: "planned" },
|
|
1174
1410
|
{ id: "C72", kind: "capability", semantic: "capability.cache-manager", api: "useCacheManager()", props: ["CacheManagerHandle"], mpEquiv: "wx.createCacheManager", tier: "L1", status: "planned" },
|
|
1175
|
-
{ id: "C73", kind: "capability", semantic: "capability.idle", api: "useIdle()", props: ["IdleAPI"], mpEquiv: "wx.requestIdleCallback/cancelIdleCallback", tier: "L1", status: "
|
|
1176
|
-
{ id: "C74", kind: "capability", semantic: "capability.window", api: "useWindow()", props: ["WindowAPI"], mpEquiv: "wx.setWindowSize", tier: "L1", status: "
|
|
1177
|
-
{ id: "C75", kind: "capability", semantic: "capability.navigation-guard", api: "useNavigationGuard()", props: ["NavigationGuardAPI"], mpEquiv: "wx.enableAlertBeforeUnload/disableAlertBeforeUnload", tier: "L1", status: "
|
|
1411
|
+
{ id: "C73", kind: "capability", semantic: "capability.idle", api: "useIdle()", props: ["IdleAPI"], mpEquiv: "wx.requestIdleCallback/cancelIdleCallback", tier: "L1", status: "implemented" },
|
|
1412
|
+
{ id: "C74", kind: "capability", semantic: "capability.window", api: "useWindow()", props: ["WindowAPI"], mpEquiv: "wx.setWindowSize", tier: "L1", status: "implemented" },
|
|
1413
|
+
{ id: "C75", kind: "capability", semantic: "capability.navigation-guard", api: "useNavigationGuard()", props: ["NavigationGuardAPI"], mpEquiv: "wx.enableAlertBeforeUnload/disableAlertBeforeUnload", tier: "L1", status: "implemented" },
|
|
1178
1414
|
// ★权威标尺缺口补齐批 G(2026-09-12):AR/XR / iBeacon / 局域网 / 翻译 / 海报 / 设备探测
|
|
1179
1415
|
{ id: "C76", kind: "capability", semantic: "capability.ar", api: "useAR()", props: ["ARAPI"], mpEquiv: "wx.createVKSession/isVKSupport", tier: "L1", status: "planned" },
|
|
1180
1416
|
{ id: "C77", kind: "capability", semantic: "capability.beacon", api: "useBeacon()", props: ["BeaconAPI"], mpEquiv: "wx.onBeaconServiceChange/onBeaconUpdate", tier: "L1", status: "planned" },
|
|
1181
1417
|
{ id: "C78", kind: "capability", semantic: "capability.local-service", api: "useLocalService()", props: ["LocalServiceAPI"], mpEquiv: "wx.onLocalServiceFound/Lost/ResolveFail/DiscoveryStop", tier: "L1", status: "planned" },
|
|
1182
1418
|
{ id: "C79", kind: "capability", semantic: "capability.translation", api: "useTranslation()", props: ["TranslationAPI"], mpEquiv: "wx.onUserTriggerTranslation/onUserOffTranslation", tier: "L1", status: "planned" },
|
|
1183
1419
|
{ id: "C80", kind: "capability", semantic: "capability.poster", api: "usePoster()", props: ["PosterAPI"], mpEquiv: "wx.onGeneratePoster", tier: "L1", status: "planned" },
|
|
1184
|
-
{ id: "C81", kind: "capability", semantic: "capability.device-capability", api: "useDeviceCapability()", props: ["DeviceCapabilityAPI"], mpEquiv: "wx.checkDeviceSupportHevc", tier: "L1", status: "planned" }
|
|
1420
|
+
{ id: "C81", kind: "capability", semantic: "capability.device-capability", api: "useDeviceCapability()", props: ["DeviceCapabilityAPI"], mpEquiv: "wx.checkDeviceSupportHevc", tier: "L1", status: "planned" },
|
|
1421
|
+
// ★C82(2026-09-29):WebAssembly 跨平台实现——**超官方清单**能力(官方 301 API 里无 wasm)。
|
|
1422
|
+
// 三端形态有实质差异(双源取证:微信官方文档 /framework/performance/wasm.html +
|
|
1423
|
+
// miniprogram-api-typings/lib.wx.wasm.d.ts):
|
|
1424
|
+
// MP `WXWebAssembly.instantiate(path)` 只收**代码包路径**(.wasm/.wasm.br)· 无 compile/validate ·
|
|
1425
|
+
// v2.13.0+ 全局 / v2.15.0+ Worker;Web 与 App-iOS(JSC) 走标准 `WebAssembly`(收字节 · 有 compile/validate);
|
|
1426
|
+
// App-Android 由**宿主 wasm3** 执行(QuickJS 内建无 WASM——双端真机验证 add(2,40)=42)。
|
|
1427
|
+
// ⇒ 归一入口 `instantiate({bytes}|{path})` + 能力位 `supportsStreaming`/`supportsPathLoad`。
|
|
1428
|
+
{ id: "C82", kind: "capability", semantic: "capability.webassembly", api: "useWebAssembly()", props: ["WebAssemblyAPI"], mpEquiv: "WXWebAssembly.instantiate\uFF08\u5B98\u65B9\u6587\u6863 performance/wasm\uFF09", tier: "L1", status: "implemented" },
|
|
1429
|
+
// ★★NC1 首个声明式能力(2026-09-30):来源 = 权威标尺修复后浮现的真实缺口之一
|
|
1430
|
+
// (快照抽取器修复前 `wx.setKeepScreenOn` 结构性不可见,见 mp-spec-coverage 的 SPEC_PLANNED)。
|
|
1431
|
+
// 落地方式 = **声明式**(`packages/api/src/bridge-decls/keep-screen-on.ts` → 生成 → 合并进 bridge):
|
|
1432
|
+
// MP `wx.setKeepScreenOn` / Web **Screen Wake Lock**(`navigator.wakeLock.request('screen')`)——
|
|
1433
|
+
// 双端都有真实对等(非降级对等)。status 标 implemented:桥与 Hook 已落地且测试覆盖
|
|
1434
|
+
// (含双端真跑 + 幂等 + 缺 API 降级)。
|
|
1435
|
+
{ id: "C83", kind: "capability", semantic: "capability.keep-screen-on", api: "useKeepScreenOn()", props: ["KeepScreenOnState"], mpEquiv: "wx.setKeepScreenOn\uFF08Web \u5BF9\u7B49\uFF1AScreen Wake Lock API\uFF09", tier: "L1", status: "implemented" },
|
|
1436
|
+
// ★★NC1 第二个声明式能力(2026-09-30):**用户裁定**「openDocument 属原生能力落地范畴,
|
|
1437
|
+
// 目标就是 99% 的业务代码不需要写原生代码」⇒ 落地而非搁置。
|
|
1438
|
+
// 形态 = 声明式(bridge-decls/open-document.ts → 生成 → 合并);MP `wx.openDocument` /
|
|
1439
|
+
// Web `window.open`(浏览器查看器);★Web 诚实边界:office 格式通常转下载而非预览。
|
|
1440
|
+
// status = implemented:桥与 Hook 已落地 + 测试覆盖(含"弹窗被拦截 ⇒ Err"的防静默判据)。
|
|
1441
|
+
{ id: "C84", kind: "capability", semantic: "capability.document-preview", api: "useOpenDocument()", props: ["DocumentPath"], mpEquiv: "wx.openDocument\uFF08Web \u5BF9\u7B49\uFF1Awindow.open \u2192 \u6D4F\u89C8\u5668\u67E5\u770B\u5668\uFF09", tier: "L1", status: "implemented" }
|
|
1185
1442
|
];
|
|
1186
1443
|
var ENGINEERING = [
|
|
1187
1444
|
{ id: "E1", kind: "engineering", semantic: "engineering.state", api: "useState()", mpEquiv: "data", tier: "L1", status: "planned" },
|
|
@@ -1293,6 +1550,105 @@ function checkSemanticCoverage(minBackends = 3) {
|
|
|
1293
1550
|
return gaps;
|
|
1294
1551
|
}
|
|
1295
1552
|
|
|
1553
|
+
// src/capability-domains.ts
|
|
1554
|
+
var CAPABILITY_CATEGORY = {
|
|
1555
|
+
fetch: "\u7F51\u7EDC\u4E0E\u901A\u4FE1",
|
|
1556
|
+
websocket: "\u7F51\u7EDC\u4E0E\u901A\u4FE1",
|
|
1557
|
+
"socket-task": "\u7F51\u7EDC\u4E0E\u901A\u4FE1",
|
|
1558
|
+
socket: "\u7F51\u7EDC\u4E0E\u901A\u4FE1",
|
|
1559
|
+
"local-service": "\u7F51\u7EDC\u4E0E\u901A\u4FE1",
|
|
1560
|
+
upload: "\u7F51\u7EDC\u4E0E\u901A\u4FE1",
|
|
1561
|
+
download: "\u7F51\u7EDC\u4E0E\u901A\u4FE1",
|
|
1562
|
+
"data-channel": "\u7F51\u7EDC\u4E0E\u901A\u4FE1",
|
|
1563
|
+
bluetooth: "\u7F51\u7EDC\u4E0E\u901A\u4FE1",
|
|
1564
|
+
nfc: "\u7F51\u7EDC\u4E0E\u901A\u4FE1",
|
|
1565
|
+
device: "\u8BBE\u5907\u4E0E\u7CFB\u7EDF",
|
|
1566
|
+
screen: "\u8BBE\u5907\u4E0E\u7CFB\u7EDF",
|
|
1567
|
+
battery: "\u8BBE\u5907\u4E0E\u7CFB\u7EDF",
|
|
1568
|
+
orientation: "\u8BBE\u5907\u4E0E\u7CFB\u7EDF",
|
|
1569
|
+
brightness: "\u8BBE\u5907\u4E0E\u7CFB\u7EDF",
|
|
1570
|
+
sensor: "\u8BBE\u5907\u4E0E\u7CFB\u7EDF",
|
|
1571
|
+
vibrate: "\u8BBE\u5907\u4E0E\u7CFB\u7EDF",
|
|
1572
|
+
network: "\u8BBE\u5907\u4E0E\u7CFB\u7EDF",
|
|
1573
|
+
keyboard: "\u8BBE\u5907\u4E0E\u7CFB\u7EDF",
|
|
1574
|
+
clipboard: "\u8BBE\u5907\u4E0E\u7CFB\u7EDF",
|
|
1575
|
+
"element-query": "\u8BBE\u5907\u4E0E\u7CFB\u7EDF",
|
|
1576
|
+
intersection: "\u8BBE\u5907\u4E0E\u7CFB\u7EDF",
|
|
1577
|
+
"media-query": "\u8BBE\u5907\u4E0E\u7CFB\u7EDF",
|
|
1578
|
+
"screen-capture": "\u8BBE\u5907\u4E0E\u7CFB\u7EDF",
|
|
1579
|
+
"cache-manager": "\u8BBE\u5907\u4E0E\u7CFB\u7EDF",
|
|
1580
|
+
ar: "\u8BBE\u5907\u4E0E\u7CFB\u7EDF",
|
|
1581
|
+
beacon: "\u8BBE\u5907\u4E0E\u7CFB\u7EDF",
|
|
1582
|
+
"device-capability": "\u8BBE\u5907\u4E0E\u7CFB\u7EDF",
|
|
1583
|
+
storage: "\u5B58\u50A8\u4E0E\u6587\u4EF6",
|
|
1584
|
+
cookie: "\u5B58\u50A8\u4E0E\u6587\u4EF6",
|
|
1585
|
+
"file-system": "\u5B58\u50A8\u4E0E\u6587\u4EF6",
|
|
1586
|
+
archive: "\u5B58\u50A8\u4E0E\u6587\u4EF6",
|
|
1587
|
+
location: "\u4F4D\u7F6E\u4E0E\u5730\u56FE",
|
|
1588
|
+
map: "\u4F4D\u7F6E\u4E0E\u5730\u56FE",
|
|
1589
|
+
camera: "\u5A92\u4F53\u4E0E\u626B\u7801",
|
|
1590
|
+
microphone: "\u5A92\u4F53\u4E0E\u626B\u7801",
|
|
1591
|
+
live: "\u5A92\u4F53\u4E0E\u626B\u7801",
|
|
1592
|
+
"qr-code": "\u5A92\u4F53\u4E0E\u626B\u7801",
|
|
1593
|
+
canvas: "\u5A92\u4F53\u4E0E\u626B\u7801",
|
|
1594
|
+
video: "\u5A92\u4F53\u4E0E\u626B\u7801",
|
|
1595
|
+
audio: "\u5A92\u4F53\u4E0E\u626B\u7801",
|
|
1596
|
+
"live-pusher": "\u5A92\u4F53\u4E0E\u626B\u7801",
|
|
1597
|
+
"image-edit": "\u5A92\u4F53\u4E0E\u626B\u7801",
|
|
1598
|
+
"media-processing": "\u5A92\u4F53\u4E0E\u626B\u7801",
|
|
1599
|
+
login: "\u8D26\u53F7\u4E0E\u652F\u4ED8",
|
|
1600
|
+
auth: "\u8D26\u53F7\u4E0E\u652F\u4ED8",
|
|
1601
|
+
biometric: "\u8D26\u53F7\u4E0E\u652F\u4ED8",
|
|
1602
|
+
"face-id": "\u8D26\u53F7\u4E0E\u652F\u4ED8",
|
|
1603
|
+
permission: "\u8D26\u53F7\u4E0E\u652F\u4ED8",
|
|
1604
|
+
payment: "\u8D26\u53F7\u4E0E\u652F\u4ED8",
|
|
1605
|
+
"in-app-purchase": "\u8D26\u53F7\u4E0E\u652F\u4ED8",
|
|
1606
|
+
privacy: "\u8D26\u53F7\u4E0E\u652F\u4ED8",
|
|
1607
|
+
notification: "\u901A\u77E5\u4E0E\u5206\u4EAB",
|
|
1608
|
+
share: "\u901A\u77E5\u4E0E\u5206\u4EAB",
|
|
1609
|
+
shortcut: "\u901A\u77E5\u4E0E\u5206\u4EAB",
|
|
1610
|
+
sms: "\u901A\u77E5\u4E0E\u5206\u4EAB",
|
|
1611
|
+
contact: "\u901A\u77E5\u4E0E\u5206\u4EAB",
|
|
1612
|
+
"phone-call": "\u901A\u77E5\u4E0E\u5206\u4EAB",
|
|
1613
|
+
calendar: "\u901A\u77E5\u4E0E\u5206\u4EAB",
|
|
1614
|
+
ad: "\u901A\u77E5\u4E0E\u5206\u4EAB",
|
|
1615
|
+
poster: "\u901A\u77E5\u4E0E\u5206\u4EAB",
|
|
1616
|
+
translation: "\u901A\u77E5\u4E0E\u5206\u4EAB",
|
|
1617
|
+
"app-lifecycle": "\u5E94\u7528\u4E0E\u751F\u547D\u5468\u671F",
|
|
1618
|
+
"page-lifecycle": "\u5E94\u7528\u4E0E\u751F\u547D\u5468\u671F",
|
|
1619
|
+
background: "\u5E94\u7528\u4E0E\u751F\u547D\u5468\u671F",
|
|
1620
|
+
"mini-program": "\u5E94\u7528\u4E0E\u751F\u547D\u5468\u671F",
|
|
1621
|
+
embedded: "\u5E94\u7528\u4E0E\u751F\u547D\u5468\u671F",
|
|
1622
|
+
extension: "\u5E94\u7528\u4E0E\u751F\u547D\u5468\u671F",
|
|
1623
|
+
preload: "\u5E94\u7528\u4E0E\u751F\u547D\u5468\u671F",
|
|
1624
|
+
idle: "\u5E94\u7528\u4E0E\u751F\u547D\u5468\u671F",
|
|
1625
|
+
window: "\u5E94\u7528\u4E0E\u751F\u547D\u5468\u671F",
|
|
1626
|
+
"navigation-guard": "\u5E94\u7528\u4E0E\u751F\u547D\u5468\u671F",
|
|
1627
|
+
analytics: "\u53EF\u89C2\u6D4B\u4E0E\u8C03\u8BD5",
|
|
1628
|
+
log: "\u53EF\u89C2\u6D4B\u4E0E\u8C03\u8BD5",
|
|
1629
|
+
performance: "\u53EF\u89C2\u6D4B\u4E0E\u8C03\u8BD5",
|
|
1630
|
+
// ★2026-09-30 补齐(原 75 条 vs 82 能力 ⇒ 7 个落「其他」;归类依据见文件头注释)
|
|
1631
|
+
update: "\u5E94\u7528\u4E0E\u751F\u547D\u5468\u671F",
|
|
1632
|
+
worker: "\u5E94\u7528\u4E0E\u751F\u547D\u5468\u671F",
|
|
1633
|
+
webassembly: "\u5E94\u7528\u4E0E\u751F\u547D\u5468\u671F",
|
|
1634
|
+
album: "\u5A92\u4F53\u4E0E\u626B\u7801",
|
|
1635
|
+
address: "\u8D26\u53F7\u4E0E\u652F\u4ED8",
|
|
1636
|
+
wifi: "\u7F51\u7EDC\u4E0E\u901A\u4FE1",
|
|
1637
|
+
"we-run": "\u8BBE\u5907\u4E0E\u7CFB\u7EDF",
|
|
1638
|
+
// ★2026-09-30 NC1 首个声明式能力(C83——与设备/屏幕同域)
|
|
1639
|
+
"keep-screen-on": "\u8BBE\u5907\u4E0E\u7CFB\u7EDF",
|
|
1640
|
+
// ★2026-09-30 NC1:C84 文档预览(文件域——预览的是文件)
|
|
1641
|
+
"document-preview": "\u5B58\u50A8\u4E0E\u6587\u4EF6"
|
|
1642
|
+
};
|
|
1643
|
+
var CAPABILITY_CATEGORY_ORDER = ["\u7F51\u7EDC\u4E0E\u901A\u4FE1", "\u8BBE\u5907\u4E0E\u7CFB\u7EDF", "\u5B58\u50A8\u4E0E\u6587\u4EF6", "\u4F4D\u7F6E\u4E0E\u5730\u56FE", "\u5A92\u4F53\u4E0E\u626B\u7801", "\u8D26\u53F7\u4E0E\u652F\u4ED8", "\u901A\u77E5\u4E0E\u5206\u4EAB", "\u5E94\u7528\u4E0E\u751F\u547D\u5468\u671F", "\u53EF\u89C2\u6D4B\u4E0E\u8C03\u8BD5", "\u5176\u4ED6"];
|
|
1644
|
+
function capabilityDomainOf(semanticOrSlug) {
|
|
1645
|
+
const slug = semanticOrSlug.replace(/^capability\./, "");
|
|
1646
|
+
return CAPABILITY_CATEGORY[slug] ?? "\u5176\u4ED6";
|
|
1647
|
+
}
|
|
1648
|
+
function auditCapabilityDomains(slugs) {
|
|
1649
|
+
return slugs.filter((s) => !(s in CAPABILITY_CATEGORY));
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1296
1652
|
// src/audit.ts
|
|
1297
1653
|
var MP_COMPONENTS = [
|
|
1298
1654
|
{ mp: "<view>", proteus: "layout.box / layout.stack", status: "ok", group: "component" },
|
|
@@ -1393,6 +1749,10 @@ var MP_API_GROUPS = [
|
|
|
1393
1749
|
{ mp: "wx.onUserTriggerTranslation/onUserOffTranslation\uFF08\u7FFB\u8BD1\uFF09", proteus: "useTranslation", status: "ok", group: "api" },
|
|
1394
1750
|
{ mp: "wx.onGeneratePoster\uFF08\u5206\u4EAB\u6D77\u62A5\uFF09", proteus: "usePoster", status: "ok", group: "api" },
|
|
1395
1751
|
{ mp: "wx.checkDeviceSupportHevc\uFF08\u8BBE\u5907\u80FD\u529B\u63A2\u6D4B\uFF09", proteus: "useDeviceCapability", status: "ok", group: "api" },
|
|
1752
|
+
// ★C82:**超官方清单**能力(官方 301 API 列表里无 wasm)——`WXWebAssembly` 属官方「小程序运行时」
|
|
1753
|
+
// 文档(performance/wasm)确认的运行时能力 ⇒ 矩阵按 mpEquiv 如实登记;
|
|
1754
|
+
// 不进 spec 覆盖度分母(否则分母不变而 covered 虚增,覆盖率失真——见 mp-spec-coverage 注释)
|
|
1755
|
+
{ mp: "WXWebAssembly.instantiate\uFF08WebAssembly \u8DE8\u5E73\u53F0\uFF0C\u5B98\u65B9\u6587\u6863 performance/wasm\uFF09", proteus: "useWebAssembly", status: "ok", group: "api" },
|
|
1396
1756
|
{ mp: "wx.getFileSystemManager/*\uFF08\u6587\u4EF6 30+\uFF09", proteus: "useFileSystem", status: "ok", group: "api" },
|
|
1397
1757
|
{ mp: "wx.compressFile/unzip", proteus: "useArchive", status: "ok", group: "api" },
|
|
1398
1758
|
{ mp: "wx.set/get/remove/clearStorage(+Sync)", proteus: "useStorage", status: "ok", group: "api" },
|
|
@@ -1757,7 +2117,126 @@ var SPEC_COVERED = {
|
|
|
1757
2117
|
onUserOffTranslation: "useTranslation",
|
|
1758
2118
|
offUserOffTranslation: "useTranslation",
|
|
1759
2119
|
onGeneratePoster: "usePoster",
|
|
1760
|
-
offGeneratePoster: "usePoster"
|
|
2120
|
+
offGeneratePoster: "usePoster",
|
|
2121
|
+
// ★★2026-09-30 补齐(快照抽取器修泛型缺陷后新可见的 102 个官方 API——
|
|
2122
|
+
// 旧快照结构性漏抽它们 ⇒ 此前从未被归类。逐条证据见 tools 归类记录)
|
|
2123
|
+
authorize: "usePermission",
|
|
2124
|
+
canvasToTempFilePath: "useCanvas",
|
|
2125
|
+
chooseAddress: "useAddress",
|
|
2126
|
+
chooseMedia: "useAlbum",
|
|
2127
|
+
connectWifi: "useWifi",
|
|
2128
|
+
getBatteryInfo: "useBattery",
|
|
2129
|
+
getClipboardData: "useClipboard",
|
|
2130
|
+
getConnectedWifi: "useWifi",
|
|
2131
|
+
getHCEState: "useNFC",
|
|
2132
|
+
getLocation: "useLocation",
|
|
2133
|
+
getNetworkType: "useNetwork",
|
|
2134
|
+
getScreenBrightness: "useBrightness",
|
|
2135
|
+
getSetting: "usePermission",
|
|
2136
|
+
getStorageSync: "useStorage",
|
|
2137
|
+
getWeRunData: "useWeRun",
|
|
2138
|
+
getWifiList: "useWifi",
|
|
2139
|
+
login: "useLogin",
|
|
2140
|
+
makePhoneCall: "usePhoneCall",
|
|
2141
|
+
navigateToMiniProgram: "useMiniProgram",
|
|
2142
|
+
previewMedia: "useAlbum",
|
|
2143
|
+
request: "useFetch",
|
|
2144
|
+
requestPayment: "usePayment",
|
|
2145
|
+
saveImageToPhotosAlbum: "useAlbum",
|
|
2146
|
+
saveVideoToPhotosAlbum: "useAlbum",
|
|
2147
|
+
scanCode: "useQRCode",
|
|
2148
|
+
setClipboardData: "useClipboard",
|
|
2149
|
+
setScreenBrightness: "useBrightness",
|
|
2150
|
+
setStorageSync: "useStorage",
|
|
2151
|
+
startSoterAuthentication: "useBiometric",
|
|
2152
|
+
startWifi: "useWifi",
|
|
2153
|
+
vibrateShort: "useVibrate",
|
|
2154
|
+
requestSubscribeMessage: "useNotification",
|
|
2155
|
+
setWifiList: "useWifi",
|
|
2156
|
+
stopWifi: "useWifi",
|
|
2157
|
+
// ★2026-09-30 NC1:setKeepScreenOn 已由声明式能力 C83 承接(落地后据实由 planned 转 covered)
|
|
2158
|
+
setKeepScreenOn: "useKeepScreenOn",
|
|
2159
|
+
// ★2026-09-30 记账修正:命令式全局提示**早已实现**(`packages/api/src/platform.ts` 的 createUIAPI:
|
|
2160
|
+
// MP 走 wx.showToast/showLoading;Web 走 DOM 自绘 + SSR console 降级),且有测试
|
|
2161
|
+
// (tests/platform-api.test.ts「stub wx 时 showToast/showLoading/hideLoading/showModal/
|
|
2162
|
+
// showActionSheet 转发」)。同一份实现里的 showModal/showActionSheet **早已标 covered**
|
|
2163
|
+
// ⇒ 这 4 条此前记 planned 属**记账不一致**(非能力缺口)。
|
|
2164
|
+
// ★形态说明:命令式(走 PlatformAPI.ui)与声明式(p-toast / p-loading 组件)是**两条并存路径**,
|
|
2165
|
+
// 与 showModal(shell.modal 组件)↔ wx.showModal 的关系同构。
|
|
2166
|
+
showToast: "PlatformAPI.ui.showToast\uFF08+ shell.toast / p-toast \u7EC4\u4EF6\uFF09",
|
|
2167
|
+
hideToast: "PlatformAPI.ui\uFF08DOM/wx \u63D0\u793A\u968F showToast \u751F\u547D\u5468\u671F\uFF1B\u7EC4\u4EF6\u8DEF\u5F84\u89C1 p-toast\uFF09",
|
|
2168
|
+
showLoading: "PlatformAPI.ui.showLoading\uFF08+ ui.loading / p-loading \u7EC4\u4EF6\uFF09",
|
|
2169
|
+
// ★2026-09-30 NC1:C84 文档预览落地(声明式桥;MP wx.openDocument / Web window.open)
|
|
2170
|
+
openDocument: "useOpenDocument",
|
|
2171
|
+
hideLoading: "PlatformAPI.ui.hideLoading",
|
|
2172
|
+
batchGetStorage: "useStorage",
|
|
2173
|
+
batchSetStorage: "useStorage",
|
|
2174
|
+
clearStorage: "useStorage",
|
|
2175
|
+
getStorage: "useStorage",
|
|
2176
|
+
setStorage: "useStorage",
|
|
2177
|
+
removeStorage: "useStorage",
|
|
2178
|
+
getStorageInfo: "useStorage",
|
|
2179
|
+
startAccelerometer: "useSensorStream",
|
|
2180
|
+
stopAccelerometer: "useSensorStream",
|
|
2181
|
+
startCompass: "useSensorStream",
|
|
2182
|
+
stopCompass: "useSensorStream",
|
|
2183
|
+
startGyroscope: "useSensorStream",
|
|
2184
|
+
stopGyroscope: "useSensorStream",
|
|
2185
|
+
startLocationUpdate: "useLocation",
|
|
2186
|
+
stopLocationUpdate: "useLocation",
|
|
2187
|
+
startLocationUpdateBackground: "useLocation",
|
|
2188
|
+
openBluetoothAdapter: "useBluetooth",
|
|
2189
|
+
closeBluetoothAdapter: "useBluetooth",
|
|
2190
|
+
getBluetoothAdapterState: "useBluetooth",
|
|
2191
|
+
getBluetoothDevices: "useBluetooth",
|
|
2192
|
+
getConnectedBluetoothDevices: "useBluetooth",
|
|
2193
|
+
createBLEConnection: "useBluetooth",
|
|
2194
|
+
closeBLEConnection: "useBluetooth",
|
|
2195
|
+
getBLEDeviceCharacteristics: "useBluetooth",
|
|
2196
|
+
getBLEDeviceServices: "useBluetooth",
|
|
2197
|
+
getBLEDeviceRSSI: "useBluetooth",
|
|
2198
|
+
getBLEMTU: "useBluetooth",
|
|
2199
|
+
setBLEMTU: "useBluetooth",
|
|
2200
|
+
isBluetoothDevicePaired: "useBluetooth",
|
|
2201
|
+
makeBluetoothPair: "useBluetooth",
|
|
2202
|
+
notifyBLECharacteristicValueChange: "useBluetooth",
|
|
2203
|
+
readBLECharacteristicValue: "useBluetooth",
|
|
2204
|
+
writeBLECharacteristicValue: "useBluetooth",
|
|
2205
|
+
startBluetoothDevicesDiscovery: "useBluetooth",
|
|
2206
|
+
stopBluetoothDevicesDiscovery: "useBluetooth",
|
|
2207
|
+
openSystemBluetoothSetting: "useBluetooth",
|
|
2208
|
+
closeSocket: "useWebSocket",
|
|
2209
|
+
sendSocketMessage: "useWebSocket",
|
|
2210
|
+
getBeacons: "useBeacon",
|
|
2211
|
+
startBeaconDiscovery: "useBeacon",
|
|
2212
|
+
stopBeaconDiscovery: "useBeacon",
|
|
2213
|
+
startLocalServiceDiscovery: "useLocalService",
|
|
2214
|
+
stopLocalServiceDiscovery: "useLocalService",
|
|
2215
|
+
startHCE: "useNFC",
|
|
2216
|
+
stopHCE: "useNFC",
|
|
2217
|
+
sendHCEMessage: "useNFC",
|
|
2218
|
+
checkIsSupportSoterAuthentication: "useBiometric",
|
|
2219
|
+
checkIsSoterEnrolledInDevice: "useBiometric",
|
|
2220
|
+
chooseImage: "useAlbum",
|
|
2221
|
+
chooseVideo: "useAlbum",
|
|
2222
|
+
previewImage: "useAlbum",
|
|
2223
|
+
compressImage: "useMediaProcessing",
|
|
2224
|
+
compressVideo: "useMediaProcessing",
|
|
2225
|
+
getAvailableAudioSources: "useAudio",
|
|
2226
|
+
setInnerAudioOption: "useAudio",
|
|
2227
|
+
vibrateLong: "useVibrate",
|
|
2228
|
+
hideKeyboard: "useKeyboard",
|
|
2229
|
+
openSetting: "usePermission",
|
|
2230
|
+
openAppAuthorizeSetting: "usePermission",
|
|
2231
|
+
addPhoneRepeatCalendar: "useCalendarAPI",
|
|
2232
|
+
navigateTo: "useRouter/useRoute\uFF08packages/router\uFF09",
|
|
2233
|
+
navigateBack: "useRouter/useRoute\uFF08packages/router\uFF09",
|
|
2234
|
+
redirectTo: "useRouter/useRoute\uFF08packages/router\uFF09",
|
|
2235
|
+
reLaunch: "useRouter/useRoute\uFF08packages/router\uFF09",
|
|
2236
|
+
switchTab: "useRouter/useRoute\uFF08packages/router\uFF09",
|
|
2237
|
+
showModal: "shell.modal\uFF08p-modal \u7EC4\u4EF6\uFF09",
|
|
2238
|
+
showActionSheet: "shell.action-sheet\uFF08p-action-sheet \u7EC4\u4EF6\uFF09",
|
|
2239
|
+
getSelectedTextRange: "ui.selection\uFF08p-selection \u7EC4\u4EF6\uFF09"
|
|
1761
2240
|
};
|
|
1762
2241
|
var SPEC_PRIVATE = /* @__PURE__ */ new Set([
|
|
1763
2242
|
// 支付 / 交通卡 / 离线支付
|
|
@@ -1865,7 +2344,50 @@ var SPEC_PRIVATE = /* @__PURE__ */ new Set([
|
|
|
1865
2344
|
// 小程序归属 / 开屏广告 / XR 系统(微信私有)
|
|
1866
2345
|
"checkIsAddedToMyMiniProgram",
|
|
1867
2346
|
"getShowSplashAdStatus",
|
|
1868
|
-
"getXrFrameSystem"
|
|
2347
|
+
"getXrFrameSystem",
|
|
2348
|
+
// ★★2026-09-30 补齐(新可见 36 条——微信生态独占:卡券/红包/订单支付/发票/车牌/POI/
|
|
2349
|
+
// 群工具与群分享/VoIP/收藏/后台周期拉取/分享菜单控制/小程序返回链/提示更新微信)
|
|
2350
|
+
"addCard",
|
|
2351
|
+
"openCard",
|
|
2352
|
+
"showRedPackage",
|
|
2353
|
+
"requestOrderPayment",
|
|
2354
|
+
"chooseInvoice",
|
|
2355
|
+
"chooseInvoiceTitle",
|
|
2356
|
+
"chooseLicensePlate",
|
|
2357
|
+
"chooseLocation",
|
|
2358
|
+
"choosePoi",
|
|
2359
|
+
"getChatToolInfo",
|
|
2360
|
+
"openChatTool",
|
|
2361
|
+
"notifyGroupMembers",
|
|
2362
|
+
"selectGroupMembers",
|
|
2363
|
+
"shareAppMessageToGroup",
|
|
2364
|
+
"shareEmojiToGroup",
|
|
2365
|
+
"shareFileMessage",
|
|
2366
|
+
"shareFileToGroup",
|
|
2367
|
+
"shareImageToGroup",
|
|
2368
|
+
"shareVideoMessage",
|
|
2369
|
+
"shareVideoToGroup",
|
|
2370
|
+
"shareToWeRun",
|
|
2371
|
+
"subscribeVoIPVideoMembers",
|
|
2372
|
+
"updateVoIPChatMuteConfig",
|
|
2373
|
+
"exitVoIPChat",
|
|
2374
|
+
"addFileToFavorites",
|
|
2375
|
+
"addVideoToFavorites",
|
|
2376
|
+
"hideShareMenu",
|
|
2377
|
+
"showShareMenu",
|
|
2378
|
+
"updateShareMenu",
|
|
2379
|
+
"showShareImageMenu",
|
|
2380
|
+
"navigateBackMiniProgram",
|
|
2381
|
+
"updateWeChatApp",
|
|
2382
|
+
"openLocation",
|
|
2383
|
+
"getBackgroundFetchData",
|
|
2384
|
+
"getBackgroundFetchToken",
|
|
2385
|
+
"setBackgroundFetchToken",
|
|
2386
|
+
// ★2026-09-30 归类修正:这两条依赖**微信生态上下文**(聊天会话选文件 / 微信授权头像昵称),
|
|
2387
|
+
// 与 Web/App 无共同语义 ⇒ 属「微信独占」而非「通用缺口」(重分类前误记 planned——那会虚增
|
|
2388
|
+
// 缺口数并误导 NC2 排期去做"跨端对等",而它本质上没有对等物)
|
|
2389
|
+
"chooseMessageFile",
|
|
2390
|
+
"getUserProfile"
|
|
1869
2391
|
]);
|
|
1870
2392
|
var SPEC_NA = /* @__PURE__ */ new Set([
|
|
1871
2393
|
// 废弃路由事件(wx.router 前身)
|
|
@@ -1887,7 +2409,41 @@ var SPEC_NA = /* @__PURE__ */ new Set([
|
|
|
1887
2409
|
// 系统信息旧版(新工程用 getWindowInfo/getDeviceInfo/getAppBaseInfo)
|
|
1888
2410
|
"getSystemInfoAsync",
|
|
1889
2411
|
// 调度原语(Vue nextTick / 渲染器调度等价,非平台能力)
|
|
1890
|
-
"nextTick"
|
|
2412
|
+
"nextTick",
|
|
2413
|
+
// ★★2026-09-30 补齐(新可见 33 条——已废弃/构建期/被语义原语消灭/宿主层;逐组见注释)
|
|
2414
|
+
"checkSession",
|
|
2415
|
+
"getShareInfo",
|
|
2416
|
+
"getUserInfo",
|
|
2417
|
+
"getSystemInfo",
|
|
2418
|
+
"setEnableDebug",
|
|
2419
|
+
"playVoice",
|
|
2420
|
+
"pauseVoice",
|
|
2421
|
+
"stopVoice",
|
|
2422
|
+
"startRecord",
|
|
2423
|
+
"stopRecord",
|
|
2424
|
+
"playBackgroundAudio",
|
|
2425
|
+
"pauseBackgroundAudio",
|
|
2426
|
+
"stopBackgroundAudio",
|
|
2427
|
+
"seekBackgroundAudio",
|
|
2428
|
+
"getBackgroundAudioPlayerState",
|
|
2429
|
+
"getExtConfig",
|
|
2430
|
+
"loadFontFace",
|
|
2431
|
+
"setNavigationBarTitle",
|
|
2432
|
+
"setNavigationBarColor",
|
|
2433
|
+
"showNavigationBarLoading",
|
|
2434
|
+
"hideNavigationBarLoading",
|
|
2435
|
+
"setTopBarText",
|
|
2436
|
+
"hideHomeButton",
|
|
2437
|
+
"showTabBar",
|
|
2438
|
+
"hideTabBar",
|
|
2439
|
+
"setTabBarBadge",
|
|
2440
|
+
"removeTabBarBadge",
|
|
2441
|
+
"setTabBarItem",
|
|
2442
|
+
"setTabBarStyle",
|
|
2443
|
+
"showTabBarRedDot",
|
|
2444
|
+
"hideTabBarRedDot",
|
|
2445
|
+
"setBackgroundColor",
|
|
2446
|
+
"setBackgroundTextStyle"
|
|
1891
2447
|
]);
|
|
1892
2448
|
var SPEC_PLANNED = {
|
|
1893
2449
|
// 网络底层(已由 C69 useSocket 覆盖——见 SPEC_COVERED)
|
|
@@ -1900,6 +2456,24 @@ var SPEC_PLANNED = {
|
|
|
1900
2456
|
// 缓存管理 / 窗口 / 卸载拦截(已由 C72/C74/C75 覆盖——见 SPEC_COVERED)
|
|
1901
2457
|
// 调度(已由 C73 useIdle 覆盖——见 SPEC_COVERED)
|
|
1902
2458
|
// 设备能力探测(已由 C81 useDeviceCapability 覆盖——见 SPEC_COVERED)
|
|
2459
|
+
// ★C82 WebAssembly:**不在官方 301 API 清单内**(`WXWebAssembly` 属「小程序运行时」文档章节,
|
|
2460
|
+
// 非「API」章节)⇒ **不进 SPEC_COVERED**:那会让 covered 分子虚增而分母不变 ⇒ 覆盖率虚高。
|
|
2461
|
+
// 也不进 SPEC_PRIVATE(它是**公开能力**,不是微信私有 API)
|
|
2462
|
+
// ⇒ 归类为「超清单能力」,由 primitives C82 直接承载(矩阵见 audit.ts)
|
|
2463
|
+
// ★★2026-09-30 补齐(新可见 21 条——真实缺口,NC2「内置能力扩充」候选输入)
|
|
2464
|
+
addPhoneContact: "\u901A\u8BAF\u5F55\u5199\u5165\uFF08\u65E0\u5BF9\u7B49 Hook\uFF09",
|
|
2465
|
+
canvasGetImageData: "\u753B\u5E03\u50CF\u7D20\u8BFB\u53D6\uFF08CanvasContext \u65E0\u6B64\u65B9\u6CD5\u2014\u2014\u89C1 capability.ts CanvasContext\uFF09",
|
|
2466
|
+
canvasPutImageData: "\u753B\u5E03\u50CF\u7D20\u5199\u5165\uFF08\u540C\u4E0A\uFF09",
|
|
2467
|
+
checkIsOpenAccessibility: "\u65E0\u969C\u788D\u5F00\u5173\u63A2\u6D4B\uFF08\u65E0\u5BF9\u7B49\uFF09",
|
|
2468
|
+
createBLEPeripheralServer: "\u84DD\u7259\u5916\u8BBE\u6A21\u5F0F\uFF08\u65E0\u5BF9\u7B49\uFF09",
|
|
2469
|
+
getImageInfo: "\u56FE\u7247\u5143\u4FE1\u606F\uFF08\u5BBD\u9AD8/\u65B9\u5411\u2014\u2014useFileSystem \u53EA\u7ED9 size/digest\uFF09",
|
|
2470
|
+
getVideoInfo: "\u89C6\u9891\u5143\u4FE1\u606F\uFF08\u540C\u4E0A\u65CF\uFF09",
|
|
2471
|
+
getRandomValues: "\u5BC6\u7801\u5B66\u968F\u673A\u6570\uFF08Web \u7AEF\u6709 crypto.getRandomValues\u2014\u2014\u5BBF\u4E3B\u672A\u63A5\u7EBF\uFF09",
|
|
2472
|
+
pageScrollTo: "\u9875\u9762\u7EA7\u6EDA\u52A8\uFF08p-scroll \u53EA\u7BA1\u7EC4\u4EF6\u5185\u6EDA\u52A8\uFF09",
|
|
2473
|
+
startPullDownRefresh: "\u9875\u9762\u7EA7\u4E0B\u62C9\u5237\u65B0\uFF08p-scroll refresher \u53EA\u7BA1 scroll-view\uFF09",
|
|
2474
|
+
stopPullDownRefresh: "\u9875\u9762\u7EA7\u4E0B\u62C9\u5237\u65B0\uFF08\u540C\u4E0A\uFF09",
|
|
2475
|
+
startDeviceMotionListening: "\u8BBE\u5907\u8FD0\u52A8\u76D1\u542C\uFF08\u878D\u5408\u4F20\u611F\u5668\u2014\u2014\u5F53\u524D\u4EC5\u4E09\u8F74\u5355\u9879\uFF09",
|
|
2476
|
+
stopDeviceMotionListening: "\u8BBE\u5907\u8FD0\u52A8\u76D1\u542C\uFF08\u540C\u4E0A\uFF09"
|
|
1903
2477
|
};
|
|
1904
2478
|
var EVENT_BASE = {
|
|
1905
2479
|
// 映射既有 Hook(covered)
|
|
@@ -2113,10 +2687,26 @@ var SPEC_RATCHET = {
|
|
|
2113
2687
|
// 原标 covered 且引用 `gesture.draggable`(拖拽)——引用错位 + MP 无对等(bindtap 不带 count)
|
|
2114
2688
|
// ⇒ 据实转 planned。**真实可运行的等价物一件没少**,只是不再把「仅 Web 成立」记成「两端已落地」。
|
|
2115
2689
|
// 该条即上次注释要求「因诚实性修正需下调时,必须写明被修正的具体条目」所指的条目。
|
|
2116
|
-
coveredMin:
|
|
2117
|
-
// 2026-09-
|
|
2690
|
+
coveredMin: 362,
|
|
2691
|
+
// ★2026-09-30 上调 252 → 356(修标尺)→ 357(C83)→ 361(4 条命令式提示:记账修正)→ **362**(C84 文档预览落地)
|
|
2118
2692
|
gapMax: 0
|
|
2119
2693
|
// 全部官方项必须归类
|
|
2694
|
+
// ★★2026-09-30:抽标尺修复后的水位重锁(**性质:修的是尺子,不是能力**——必须写清楚)
|
|
2695
|
+
// 起因:快照抽取器(scripts/gen-mp-spec.mjs)的 API 正则不认**泛型方法签名** `name<T>(...)`
|
|
2696
|
+
// ⇒ 197 个真实官方 API(含 `wx.request` / `wx.login` / `wx.authorize` / `wx.getStorageSync` /
|
|
2697
|
+
// `wx.chooseMedia` / `wx.setKeepScreenOn` 等**最核心的一批**)**从未进入权威标尺**:
|
|
2698
|
+
// 分母被截短 40%(298 vs 实际 495)。
|
|
2699
|
+
// 后果有两面:① 这些 API 的缺口**结构性不可见**(gap=0 是假的——根本没在表里);
|
|
2700
|
+
// ② 报表里的「官方 N 项」本身是错的。
|
|
2701
|
+
// ✅ 修复后:官方 579 项(组件 84 · API 495);covered 356 / planned 25 / private 145 / na 53 / gap 0。
|
|
2702
|
+
// ★为什么 covered **大幅上涨**(252→356)却不是"能力增长":新增可见的 197 项里,
|
|
2703
|
+
// **104 项**(102 新归类 + 2 由既有规则命中)经逐条取证确认**已有承接**——证据见下方各表
|
|
2704
|
+
// 分组注释(严格口径 = 剥注释后匹配 `wx.<name>(` 或同族动态派发,非"看起来像");
|
|
2705
|
+
// private +39 · na +33 · planned +21 —— 合计 197。它们是**此前没被记账的既有覆盖**。
|
|
2706
|
+
// 这是"修尺子"带来的**账面**变化:真实可运行的等价物一件没多也没少。
|
|
2707
|
+
// ★planned 在后续批次中逐条转出(C83 落地 + 命令式提示 4 条记账修正)——当前值见棘轮注释
|
|
2708
|
+
// —— 它们是 NC2「内置能力扩充」的候选输入(**保守归类**:拿不准一律 planned,不虚标 covered)。
|
|
2709
|
+
// ★附加修复:抽取范围原为「`interface Wx {` 起 → 文件尾」(未设边界,靠缩进巧合)⇒ 改为括号配对。
|
|
2120
2710
|
};
|
|
2121
2711
|
|
|
2122
2712
|
// src/degradation.ts
|
|
@@ -2317,7 +2907,853 @@ function collectPropDiagnostics(tag, props, end, table = DEGRADATION_TABLE) {
|
|
|
2317
2907
|
}
|
|
2318
2908
|
return out;
|
|
2319
2909
|
}
|
|
2910
|
+
|
|
2911
|
+
// src/color.ts
|
|
2912
|
+
function rgbOfColor(color) {
|
|
2913
|
+
if (color === void 0 || color === null) return null;
|
|
2914
|
+
const c = color.trim().toLowerCase();
|
|
2915
|
+
const h3 = /^#([0-9a-f])([0-9a-f])([0-9a-f])[0-9a-f]?$/.exec(c);
|
|
2916
|
+
if (h3 !== null) {
|
|
2917
|
+
return [parseInt(h3[1] + h3[1], 16), parseInt(h3[2] + h3[2], 16), parseInt(h3[3] + h3[3], 16)];
|
|
2918
|
+
}
|
|
2919
|
+
const h6 = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})?$/.exec(c);
|
|
2920
|
+
if (h6 !== null) {
|
|
2921
|
+
return [parseInt(h6[1], 16), parseInt(h6[2], 16), parseInt(h6[3], 16)];
|
|
2922
|
+
}
|
|
2923
|
+
const m = /^rgba?\(([^)]*)\)$/.exec(c);
|
|
2924
|
+
if (m !== null) {
|
|
2925
|
+
const parts = m[1].split(",").map((t) => Number(t.trim()));
|
|
2926
|
+
if (parts.length >= 3 && parts.slice(0, 3).every((n) => Number.isFinite(n))) {
|
|
2927
|
+
return [parts[0], parts[1], parts[2]];
|
|
2928
|
+
}
|
|
2929
|
+
}
|
|
2930
|
+
return null;
|
|
2931
|
+
}
|
|
2932
|
+
function isNeutralColor(color) {
|
|
2933
|
+
const rgb = rgbOfColor(color);
|
|
2934
|
+
if (rgb === null) return false;
|
|
2935
|
+
return rgb[0] === rgb[1] && rgb[1] === rgb[2];
|
|
2936
|
+
}
|
|
2937
|
+
function isOpaqueColor(color) {
|
|
2938
|
+
if (color === void 0 || color === null) return false;
|
|
2939
|
+
const c = color.trim().toLowerCase();
|
|
2940
|
+
if (c === "") return false;
|
|
2941
|
+
if (c === "transparent") return false;
|
|
2942
|
+
if (/^#[0-9a-f]{3}$/.test(c) || /^#[0-9a-f]{6}$/.test(c)) return true;
|
|
2943
|
+
if (/^#[0-9a-f]{4}$/.test(c)) return c[4] === "f";
|
|
2944
|
+
if (/^#[0-9a-f]{8}$/.test(c)) return c.slice(7) === "ff";
|
|
2945
|
+
const m = /^rgba?\(([^)]*)\)$/.exec(c);
|
|
2946
|
+
if (m !== null) {
|
|
2947
|
+
const parts = m[1].split(",").map((t) => t.trim());
|
|
2948
|
+
if (parts.length === 3) return true;
|
|
2949
|
+
if (parts.length === 4) {
|
|
2950
|
+
const a = Number(parts[3]);
|
|
2951
|
+
return Number.isFinite(a) && a >= 1;
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2954
|
+
return false;
|
|
2955
|
+
}
|
|
2956
|
+
|
|
2957
|
+
// src/paint-hint.ts
|
|
2958
|
+
function derivePaintHint(input) {
|
|
2959
|
+
const {
|
|
2960
|
+
hasText,
|
|
2961
|
+
textColor,
|
|
2962
|
+
backgroundColor,
|
|
2963
|
+
hasBackgroundImage = false,
|
|
2964
|
+
hasBorderRadius = false,
|
|
2965
|
+
hasBorderWidth = false,
|
|
2966
|
+
opacity,
|
|
2967
|
+
needsCompositing = false,
|
|
2968
|
+
hasTransform = false
|
|
2969
|
+
} = input;
|
|
2970
|
+
const opacityIsOne = opacity === void 0 || opacity === 1;
|
|
2971
|
+
return {
|
|
2972
|
+
isMonochrome: !!(hasText && isOpaqueColor(textColor) && isNeutralColor(textColor) && // ★灰度格式的硬约束(见上方说明)
|
|
2973
|
+
!backgroundColor && !hasBackgroundImage && !hasBorderRadius && opacityIsOne && !needsCompositing),
|
|
2974
|
+
isPureBackground: !!(isOpaqueColor(backgroundColor) && !hasBackgroundImage && !hasBorderWidth && !hasBorderRadius && !needsCompositing && !hasTransform && !hasText && // ★本字段 = "只有一块底色要画"
|
|
2975
|
+
opacityIsOne),
|
|
2976
|
+
// 由树级分析填充(本函数只管单节点)
|
|
2977
|
+
staticSubtree: false,
|
|
2978
|
+
needsCompositingLayer: needsCompositing
|
|
2979
|
+
};
|
|
2980
|
+
}
|
|
2981
|
+
|
|
2982
|
+
// src/pnode-style.ts
|
|
2983
|
+
function resolveLength(raw, ctx = { axis: "horizontal" }) {
|
|
2984
|
+
const v = raw.trim().toLowerCase();
|
|
2985
|
+
if (!v) return void 0;
|
|
2986
|
+
if (v === "0" || /^0(\.0+)?$/.test(v)) return { kind: "absolute", dp: 0 };
|
|
2987
|
+
if (v === "auto") return void 0;
|
|
2988
|
+
const m = /^(-?\d*\.?\d+)\s*([a-z%]*)$/.exec(v);
|
|
2989
|
+
if (!m) return void 0;
|
|
2990
|
+
const n = Number(m[1]);
|
|
2991
|
+
if (!Number.isFinite(n)) return void 0;
|
|
2992
|
+
switch (m[2]) {
|
|
2993
|
+
case "":
|
|
2994
|
+
case "px":
|
|
2995
|
+
return { kind: "absolute", dp: n };
|
|
2996
|
+
case "%":
|
|
2997
|
+
return {
|
|
2998
|
+
kind: "ratio",
|
|
2999
|
+
ratio: n / 100,
|
|
3000
|
+
base: ctx.axis === "vertical" ? "parentHeight" : "parentWidth"
|
|
3001
|
+
};
|
|
3002
|
+
case "vw":
|
|
3003
|
+
return { kind: "ratio", ratio: n / 100, base: "viewportWidth" };
|
|
3004
|
+
case "vh":
|
|
3005
|
+
return { kind: "ratio", ratio: n / 100, base: "viewportHeight" };
|
|
3006
|
+
case "rpx":
|
|
3007
|
+
return { kind: "ratio", ratio: n / 750, base: "viewportWidth" };
|
|
3008
|
+
case "em":
|
|
3009
|
+
return { kind: "ratio", ratio: n, base: "fontSize" };
|
|
3010
|
+
case "rem":
|
|
3011
|
+
return { kind: "ratio", ratio: n, base: "rootFontSize" };
|
|
3012
|
+
case "pt":
|
|
3013
|
+
return { kind: "absolute", dp: n };
|
|
3014
|
+
default:
|
|
3015
|
+
return void 0;
|
|
3016
|
+
}
|
|
3017
|
+
}
|
|
3018
|
+
function parseStyleString(style) {
|
|
3019
|
+
const out = {};
|
|
3020
|
+
for (const decl of style.split(";")) {
|
|
3021
|
+
const i = decl.indexOf(":");
|
|
3022
|
+
if (i <= 0) continue;
|
|
3023
|
+
const key = camel(decl.slice(0, i).trim());
|
|
3024
|
+
const val = decl.slice(i + 1).trim();
|
|
3025
|
+
if (key && val) out[key] = val;
|
|
3026
|
+
}
|
|
3027
|
+
return out;
|
|
3028
|
+
}
|
|
3029
|
+
function camel(k) {
|
|
3030
|
+
return k.replace(/-([a-z])/g, (_m, c) => c.toUpperCase());
|
|
3031
|
+
}
|
|
3032
|
+
function splitTopLevel(value) {
|
|
3033
|
+
const parts = [];
|
|
3034
|
+
let depth = 0;
|
|
3035
|
+
let cur = "";
|
|
3036
|
+
for (const ch of value) {
|
|
3037
|
+
if (ch === "(") depth++;
|
|
3038
|
+
else if (ch === ")") depth--;
|
|
3039
|
+
if (/\s/.test(ch) && depth === 0) {
|
|
3040
|
+
if (cur) parts.push(cur);
|
|
3041
|
+
cur = "";
|
|
3042
|
+
continue;
|
|
3043
|
+
}
|
|
3044
|
+
cur += ch;
|
|
3045
|
+
}
|
|
3046
|
+
if (cur) parts.push(cur);
|
|
3047
|
+
return parts;
|
|
3048
|
+
}
|
|
3049
|
+
function expandEdges(parts) {
|
|
3050
|
+
const [a, b, c, d] = parts;
|
|
3051
|
+
if (parts.length === 1) return { top: a, right: a, bottom: a, left: a };
|
|
3052
|
+
if (parts.length === 2) return { top: a, right: b, bottom: a, left: b };
|
|
3053
|
+
if (parts.length === 3) return { top: a, right: b, bottom: c, left: b };
|
|
3054
|
+
return { top: a, right: b, bottom: c, left: d };
|
|
3055
|
+
}
|
|
3056
|
+
function parseTransform(raw) {
|
|
3057
|
+
const t = {};
|
|
3058
|
+
let threeD = false;
|
|
3059
|
+
for (const m of raw.matchAll(/([a-zA-Z0-9]+)\(([^)]*)\)/g)) {
|
|
3060
|
+
const fn = m[1].toLowerCase();
|
|
3061
|
+
const args = m[2].split(",").map((s) => s.trim());
|
|
3062
|
+
const num = (i) => Number.parseFloat(args[i] ?? "0");
|
|
3063
|
+
switch (fn) {
|
|
3064
|
+
case "translatex":
|
|
3065
|
+
t.translateX = resolveLength(args[0] ?? "0")?.kind === "absolute" ? resolveLength(args[0]).dp : num(0);
|
|
3066
|
+
break;
|
|
3067
|
+
case "translatey":
|
|
3068
|
+
t.translateY = resolveLength(args[0] ?? "0")?.kind === "absolute" ? resolveLength(args[0]).dp : num(0);
|
|
3069
|
+
break;
|
|
3070
|
+
case "translate":
|
|
3071
|
+
t.translateX = num(0);
|
|
3072
|
+
t.translateY = num(1);
|
|
3073
|
+
break;
|
|
3074
|
+
case "scale":
|
|
3075
|
+
t.scale = args.length > 1 ? num(0) : num(0);
|
|
3076
|
+
break;
|
|
3077
|
+
case "rotate":
|
|
3078
|
+
t.rotate = args[0]?.endsWith("deg") ? num(0) : num(0);
|
|
3079
|
+
break;
|
|
3080
|
+
// 3D(L3:创建合成层)
|
|
3081
|
+
case "translatez":
|
|
3082
|
+
case "translate3d":
|
|
3083
|
+
case "rotatex":
|
|
3084
|
+
case "rotatey":
|
|
3085
|
+
case "rotate3d":
|
|
3086
|
+
case "perspective":
|
|
3087
|
+
case "matrix3d":
|
|
3088
|
+
threeD = true;
|
|
3089
|
+
break;
|
|
3090
|
+
default:
|
|
3091
|
+
threeD = true;
|
|
3092
|
+
break;
|
|
3093
|
+
}
|
|
3094
|
+
}
|
|
3095
|
+
return { t, threeD };
|
|
3096
|
+
}
|
|
3097
|
+
var L3_PROPS = /* @__PURE__ */ new Set([
|
|
3098
|
+
"zIndex",
|
|
3099
|
+
"boxShadow",
|
|
3100
|
+
"filter",
|
|
3101
|
+
"backdropFilter",
|
|
3102
|
+
"willChange",
|
|
3103
|
+
"position"
|
|
3104
|
+
// 仅 fixed/sticky 属 L3(由值判定)
|
|
3105
|
+
]);
|
|
3106
|
+
var OUT_OF_PROFILE = /* @__PURE__ */ new Set([
|
|
3107
|
+
"gridTemplateColumns",
|
|
3108
|
+
"gridTemplateRows",
|
|
3109
|
+
"gridArea",
|
|
3110
|
+
"gridColumn",
|
|
3111
|
+
"gridRow",
|
|
3112
|
+
"float",
|
|
3113
|
+
"clear",
|
|
3114
|
+
"animation",
|
|
3115
|
+
"animationName",
|
|
3116
|
+
"animationDuration",
|
|
3117
|
+
"transition",
|
|
3118
|
+
"transitionProperty",
|
|
3119
|
+
"cursor",
|
|
3120
|
+
"userSelect",
|
|
3121
|
+
"content",
|
|
3122
|
+
"clipPath",
|
|
3123
|
+
"mask",
|
|
3124
|
+
"mixBlendMode"
|
|
3125
|
+
]);
|
|
3126
|
+
var L4_PASSTHROUGH = /* @__PURE__ */ new Set(["fontFamily", "fontStyle", "fontVariant", "textDecoration", "direction", "unicodeBidi"]);
|
|
3127
|
+
function normalizeStyleDecls(decls, opts = {}) {
|
|
3128
|
+
const layout = {};
|
|
3129
|
+
const paint = {};
|
|
3130
|
+
const text = {};
|
|
3131
|
+
const diagnostics = [];
|
|
3132
|
+
const push = (severity, code, message) => {
|
|
3133
|
+
diagnostics.push({ severity, code, message, nodeId: opts.nodeId, source: opts.source });
|
|
3134
|
+
};
|
|
3135
|
+
const len = (raw, axis = "horizontal") => {
|
|
3136
|
+
const r = resolveLength(raw, { axis });
|
|
3137
|
+
if (r === void 0 && raw.trim().toLowerCase() !== "auto") {
|
|
3138
|
+
push("warn", "css.invalid-length", `\u65E0\u6CD5\u6298\u53E0\u7684\u957F\u5EA6\u503C\u300C${raw}\u300D\uFF08profile \u652F\u6301 px/%/vw/vh/rpx/em/rem/pt\uFF09`);
|
|
3139
|
+
}
|
|
3140
|
+
return r;
|
|
3141
|
+
};
|
|
3142
|
+
let needsCompositing = false;
|
|
3143
|
+
let hasTransform = false;
|
|
3144
|
+
for (const [kRaw, raw] of Object.entries(decls)) {
|
|
3145
|
+
const k = camel(kRaw);
|
|
3146
|
+
const v = raw.trim();
|
|
3147
|
+
if (!v) continue;
|
|
3148
|
+
if (OUT_OF_PROFILE.has(k)) {
|
|
3149
|
+
push("error", "css.out-of-profile", `\u5C5E\u6027 ${kRaw} \u4E0D\u5728 CSS Profile \u5185\uFF08\u7F16\u8BD1\u671F\u62E6\u622A\uFF0C\u4E0D\u7559\u8FD0\u884C\u65F6\u9759\u9ED8\u964D\u7EA7\uFF09`);
|
|
3150
|
+
continue;
|
|
3151
|
+
}
|
|
3152
|
+
if (L4_PASSTHROUGH.has(k)) continue;
|
|
3153
|
+
switch (k) {
|
|
3154
|
+
/* ── 布局(L2) ── */
|
|
3155
|
+
case "display":
|
|
3156
|
+
if (v === "flex" || v === "none" || v === "block" || v === "inline-block") layout.display = v;
|
|
3157
|
+
else if (v === "grid") push("error", "css.grid-unsupported", "display:grid \u4E0D\u5728 Profile\uFF08\u5F85\u5B9A\u2014\u2014\u5EFA\u8BAE\u6539\u5D4C\u5957 flex\uFF09");
|
|
3158
|
+
else push("warn", "css.invalid-value", `display:${v} \u672A\u652F\u6301`);
|
|
3159
|
+
break;
|
|
3160
|
+
case "flexDirection":
|
|
3161
|
+
if (v === "row" || v === "column" || v === "row-reverse" || v === "column-reverse") layout.flexDirection = v;
|
|
3162
|
+
break;
|
|
3163
|
+
case "flexWrap":
|
|
3164
|
+
if (v === "nowrap" || v === "wrap" || v === "wrap-reverse") layout.flexWrap = v;
|
|
3165
|
+
break;
|
|
3166
|
+
case "justifyContent":
|
|
3167
|
+
case "alignItems":
|
|
3168
|
+
case "alignSelf":
|
|
3169
|
+
case "alignContent":
|
|
3170
|
+
layout[k] = v;
|
|
3171
|
+
break;
|
|
3172
|
+
case "flexGrow":
|
|
3173
|
+
layout.flexGrow = Number(v);
|
|
3174
|
+
break;
|
|
3175
|
+
case "flexShrink":
|
|
3176
|
+
layout.flexShrink = Number(v);
|
|
3177
|
+
break;
|
|
3178
|
+
case "flexBasis":
|
|
3179
|
+
layout.flexBasis = v === "auto" ? "auto" : len(v);
|
|
3180
|
+
break;
|
|
3181
|
+
case "flex": {
|
|
3182
|
+
if (/^-?\d*\.?\d+$/.test(v)) {
|
|
3183
|
+
layout.flexGrow = Number(v);
|
|
3184
|
+
layout.flexShrink = 1;
|
|
3185
|
+
layout.flexBasis = { kind: "absolute", dp: 0 };
|
|
3186
|
+
}
|
|
3187
|
+
break;
|
|
3188
|
+
}
|
|
3189
|
+
case "gap":
|
|
3190
|
+
case "rowGap":
|
|
3191
|
+
case "columnGap": {
|
|
3192
|
+
layout[k] = len(v);
|
|
3193
|
+
break;
|
|
3194
|
+
}
|
|
3195
|
+
case "width":
|
|
3196
|
+
case "minWidth":
|
|
3197
|
+
case "maxWidth":
|
|
3198
|
+
layout[k] = len(v, "horizontal");
|
|
3199
|
+
break;
|
|
3200
|
+
case "height":
|
|
3201
|
+
case "minHeight":
|
|
3202
|
+
case "maxHeight":
|
|
3203
|
+
layout[k] = len(v, "vertical");
|
|
3204
|
+
break;
|
|
3205
|
+
case "margin":
|
|
3206
|
+
case "padding": {
|
|
3207
|
+
const parts = splitTopLevel(v);
|
|
3208
|
+
const axis = parts.map((p, i) => len(p, k === "margin" && i === 1 ? "horizontal" : "horizontal"));
|
|
3209
|
+
void axis;
|
|
3210
|
+
const resolved = parts.map((p) => resolveLength(p, { axis: "horizontal" })).filter((x) => !!x);
|
|
3211
|
+
if (resolved.length === parts.length) layout[k] = expandEdges(resolved);
|
|
3212
|
+
break;
|
|
3213
|
+
}
|
|
3214
|
+
case "marginTop":
|
|
3215
|
+
case "marginRight":
|
|
3216
|
+
case "marginBottom":
|
|
3217
|
+
case "marginLeft":
|
|
3218
|
+
case "paddingTop":
|
|
3219
|
+
case "paddingRight":
|
|
3220
|
+
case "paddingBottom":
|
|
3221
|
+
case "paddingLeft": {
|
|
3222
|
+
const side = k.replace(/^(margin|padding)([A-Z].*)$/, "$2");
|
|
3223
|
+
const short = k.startsWith("margin") ? "margin" : "padding";
|
|
3224
|
+
const l = resolveLength(v, { axis: "horizontal" });
|
|
3225
|
+
if (l) {
|
|
3226
|
+
layout[short] = layout[short] ?? {};
|
|
3227
|
+
layout[short][side.toLowerCase()] = l;
|
|
3228
|
+
}
|
|
3229
|
+
break;
|
|
3230
|
+
}
|
|
3231
|
+
case "position":
|
|
3232
|
+
if (v === "static" || v === "relative" || v === "absolute") layout.position = v;
|
|
3233
|
+
else if (v === "fixed" || v === "sticky") {
|
|
3234
|
+
needsCompositing = true;
|
|
3235
|
+
push("warn", "css.l3-opt-in", `position:${v} \u5C5E L3\uFF08\u72EC\u7ACB\u5408\u6210\u5C42\uFF0C\u5404\u5360\u4E00\u5757 backing store\uFF09\u2014\u2014\u9700\u5728 config \u6216\u7EC4\u4EF6\u7EA7 opt-in`);
|
|
3236
|
+
} else push("warn", "css.invalid-value", `position:${v} \u672A\u652F\u6301`);
|
|
3237
|
+
break;
|
|
3238
|
+
case "top":
|
|
3239
|
+
layout.top = len(v, "vertical");
|
|
3240
|
+
break;
|
|
3241
|
+
case "bottom":
|
|
3242
|
+
layout.bottom = len(v, "vertical");
|
|
3243
|
+
break;
|
|
3244
|
+
case "left":
|
|
3245
|
+
layout.left = len(v, "horizontal");
|
|
3246
|
+
break;
|
|
3247
|
+
case "right":
|
|
3248
|
+
layout.right = len(v, "horizontal");
|
|
3249
|
+
break;
|
|
3250
|
+
case "overflow":
|
|
3251
|
+
case "overflowX":
|
|
3252
|
+
case "overflowY": {
|
|
3253
|
+
const val = v === "visible" || v === "hidden" || v === "scroll" || v === "auto" ? v : void 0;
|
|
3254
|
+
if (val) {
|
|
3255
|
+
if (k === "overflow") layout.overflow = val;
|
|
3256
|
+
else layout.overflow = val;
|
|
3257
|
+
}
|
|
3258
|
+
break;
|
|
3259
|
+
}
|
|
3260
|
+
/* ── 绘制(L1 + L3) ── */
|
|
3261
|
+
case "backgroundColor":
|
|
3262
|
+
paint.backgroundColor = v;
|
|
3263
|
+
break;
|
|
3264
|
+
case "background": {
|
|
3265
|
+
if (/gradient\(/.test(v)) paint.backgroundImage = v;
|
|
3266
|
+
else if (/url\(/.test(v)) push("warn", "css.unsupported-image", "background \u7684\u56FE\u7247\u5F62\u6001\u9700\u6539\u7528 image \u7EC4\u4EF6\uFF08CSS Profile \u4E0D\u542B\u80CC\u666F\u56FE\uFF09");
|
|
3267
|
+
else {
|
|
3268
|
+
const first = splitTopLevel(v)[0] ?? "";
|
|
3269
|
+
const isPlainColor = !/\b(no-repeat|repeat|cover|contain|center|left|right|top|bottom)\b/.test(v);
|
|
3270
|
+
if (isPlainColor && first) paint.backgroundColor = first;
|
|
3271
|
+
else push("warn", "css.background-compound", `background \u590D\u5408\u503C\u300C${v}\u300D\u65E0\u6CD5\u6298\u53E0\u4E3A\u5355\u4E00\u989C\u8272\u2014\u2014\u8BF7\u6539\u7528 background-color`);
|
|
3272
|
+
}
|
|
3273
|
+
break;
|
|
3274
|
+
}
|
|
3275
|
+
case "backgroundImage":
|
|
3276
|
+
if (v.includes("gradient(")) paint.backgroundImage = v;
|
|
3277
|
+
else push("warn", "css.unsupported-image", "background-image \u4EC5\u652F\u6301 gradient\uFF08\u5176\u4F59\u9700\u8D70 image \u7EC4\u4EF6\uFF09");
|
|
3278
|
+
break;
|
|
3279
|
+
case "borderRadius": {
|
|
3280
|
+
const parts = splitTopLevel(v).map((p) => resolveLength(p, { axis: "horizontal" }));
|
|
3281
|
+
if (parts.every((x) => !!x)) paint.borderRadius = expandEdges(parts);
|
|
3282
|
+
break;
|
|
3283
|
+
}
|
|
3284
|
+
case "borderTopLeftRadius":
|
|
3285
|
+
case "borderTopRightRadius":
|
|
3286
|
+
case "borderBottomLeftRadius":
|
|
3287
|
+
case "borderBottomRightRadius": {
|
|
3288
|
+
const side = k.replace(/^border([A-Z].*)Radius$/, "$1");
|
|
3289
|
+
const l = resolveLength(v, { axis: "horizontal" });
|
|
3290
|
+
if (l) {
|
|
3291
|
+
paint.borderRadius = paint.borderRadius ?? {};
|
|
3292
|
+
paint.borderRadius[side.charAt(0).toLowerCase() + side.slice(1)] = l;
|
|
3293
|
+
}
|
|
3294
|
+
break;
|
|
3295
|
+
}
|
|
3296
|
+
case "border": {
|
|
3297
|
+
const parts = splitTopLevel(v);
|
|
3298
|
+
const w = parts.find((p) => /^-?\d*\.?\d+\s*(px|rpx|pt)?$/.test(p));
|
|
3299
|
+
const style = parts.find((p) => ["solid", "dashed", "dotted", "none"].includes(p));
|
|
3300
|
+
const color = parts.find((p) => !p.includes("px") && p !== style && p !== w);
|
|
3301
|
+
if (w) {
|
|
3302
|
+
const l = resolveLength(w, { axis: "horizontal" });
|
|
3303
|
+
if (l) paint.borderWidth = { top: l, right: l, bottom: l, left: l };
|
|
3304
|
+
}
|
|
3305
|
+
if (style) paint.borderStyle = style;
|
|
3306
|
+
if (color) paint.borderColor = color;
|
|
3307
|
+
break;
|
|
3308
|
+
}
|
|
3309
|
+
case "borderWidth": {
|
|
3310
|
+
const parts = splitTopLevel(v).map((p) => resolveLength(p, { axis: "horizontal" }));
|
|
3311
|
+
if (parts.every((x) => !!x)) paint.borderWidth = expandEdges(parts);
|
|
3312
|
+
break;
|
|
3313
|
+
}
|
|
3314
|
+
case "borderColor":
|
|
3315
|
+
paint.borderColor = v;
|
|
3316
|
+
break;
|
|
3317
|
+
case "opacity": {
|
|
3318
|
+
const n = Number(v);
|
|
3319
|
+
if (Number.isFinite(n)) paint.opacity = n;
|
|
3320
|
+
break;
|
|
3321
|
+
}
|
|
3322
|
+
case "transform": {
|
|
3323
|
+
const { t, threeD } = parseTransform(v);
|
|
3324
|
+
hasTransform = true;
|
|
3325
|
+
if (threeD) {
|
|
3326
|
+
needsCompositing = true;
|
|
3327
|
+
push("warn", "css.l3-opt-in", `3D \u53D8\u6362\u5C5E L3\uFF08\u5408\u6210\u5C42 + \u5149\u6805\u5316\uFF09\u2014\u2014\u9700 opt-in\uFF1B\u503C\u300C${v}\u300D\u4EC5\u4F9B\u8BCA\u65AD`);
|
|
3328
|
+
}
|
|
3329
|
+
if (Object.keys(t).length) paint.transform = t;
|
|
3330
|
+
break;
|
|
3331
|
+
}
|
|
3332
|
+
/* ── L3:标记合成层 + 需 opt-in(不静默丢弃——诊断保留原值) ── */
|
|
3333
|
+
case "zIndex":
|
|
3334
|
+
case "boxShadow":
|
|
3335
|
+
case "filter":
|
|
3336
|
+
case "backdropFilter":
|
|
3337
|
+
case "willChange": {
|
|
3338
|
+
needsCompositing = true;
|
|
3339
|
+
void L3_PROPS;
|
|
3340
|
+
push("warn", "css.l3-opt-in", `${kRaw} \u5C5E L3\uFF08\u5408\u6210\u5C42/\u79BB\u5C4F\u6E32\u67D3\u2014\u2014\u5404\u5360\u4E00\u5757 backing store\uFF09\u2014\u2014\u9700\u663E\u5F0F opt-in`);
|
|
3341
|
+
break;
|
|
3342
|
+
}
|
|
3343
|
+
/* ── 文本 ── */
|
|
3344
|
+
case "fontSize":
|
|
3345
|
+
text.fontSize = len(v, "horizontal");
|
|
3346
|
+
break;
|
|
3347
|
+
case "fontWeight":
|
|
3348
|
+
text.fontWeight = v === "normal" || v === "bold" ? v : Number(v);
|
|
3349
|
+
break;
|
|
3350
|
+
case "lineHeight": {
|
|
3351
|
+
const n = Number(v);
|
|
3352
|
+
if (Number.isFinite(n) && !/[a-z%]/.test(v)) text.lineHeight = n;
|
|
3353
|
+
else text.lineHeight = resolveLength(v, { axis: "vertical" });
|
|
3354
|
+
break;
|
|
3355
|
+
}
|
|
3356
|
+
case "letterSpacing":
|
|
3357
|
+
text.letterSpacing = len(v, "horizontal");
|
|
3358
|
+
break;
|
|
3359
|
+
case "color":
|
|
3360
|
+
text.color = v;
|
|
3361
|
+
break;
|
|
3362
|
+
case "textAlign":
|
|
3363
|
+
if (v === "left" || v === "center" || v === "right" || v === "justify") text.textAlign = v;
|
|
3364
|
+
break;
|
|
3365
|
+
case "webkitLineClamp": {
|
|
3366
|
+
const n = Number(v);
|
|
3367
|
+
if (Number.isFinite(n)) text.maxLines = n;
|
|
3368
|
+
break;
|
|
3369
|
+
}
|
|
3370
|
+
case "textOverflow":
|
|
3371
|
+
if (v === "clip" || v === "ellipsis") text.textOverflow = v;
|
|
3372
|
+
break;
|
|
3373
|
+
case "whiteSpace":
|
|
3374
|
+
if (v === "normal" || v === "nowrap" || v === "pre-wrap") text.whiteSpace = v;
|
|
3375
|
+
break;
|
|
3376
|
+
default:
|
|
3377
|
+
push("error", "css.out-of-profile", `\u5C5E\u6027 ${kRaw} \u4E0D\u5728 CSS Profile \u5185`);
|
|
3378
|
+
break;
|
|
3379
|
+
}
|
|
3380
|
+
}
|
|
3381
|
+
const hasText = Object.keys(text).length > 0;
|
|
3382
|
+
const paintHint = derivePaintHint({
|
|
3383
|
+
hasText,
|
|
3384
|
+
textColor: text.color,
|
|
3385
|
+
backgroundColor: paint.backgroundColor,
|
|
3386
|
+
hasBackgroundImage: paint.backgroundImage !== void 0,
|
|
3387
|
+
hasBorderRadius: paint.borderRadius !== void 0,
|
|
3388
|
+
hasBorderWidth: paint.borderWidth !== void 0,
|
|
3389
|
+
opacity: paint.opacity,
|
|
3390
|
+
needsCompositing,
|
|
3391
|
+
hasTransform
|
|
3392
|
+
});
|
|
3393
|
+
return {
|
|
3394
|
+
props: {
|
|
3395
|
+
layout,
|
|
3396
|
+
paint,
|
|
3397
|
+
...hasText ? { text } : {},
|
|
3398
|
+
paintHint
|
|
3399
|
+
},
|
|
3400
|
+
diagnostics
|
|
3401
|
+
};
|
|
3402
|
+
}
|
|
3403
|
+
function normalizeStyleString(style, opts = {}) {
|
|
3404
|
+
return normalizeStyleDecls(parseStyleString(style), opts);
|
|
3405
|
+
}
|
|
3406
|
+
|
|
3407
|
+
// src/pnode-analyze.ts
|
|
3408
|
+
var FLATTENABLE_KINDS = /* @__PURE__ */ new Set(["view", "text", "image"]);
|
|
3409
|
+
function analyzePTree(tree, opts = {}) {
|
|
3410
|
+
const diagnostics = [];
|
|
3411
|
+
const decisions = [];
|
|
3412
|
+
const facts = opts.facts ?? /* @__PURE__ */ new Map();
|
|
3413
|
+
const boundary = opts.boundaryIds ?? /* @__PURE__ */ new Set();
|
|
3414
|
+
const boundProps = /* @__PURE__ */ new Map();
|
|
3415
|
+
for (const b of tree.bindings) {
|
|
3416
|
+
const list = boundProps.get(b.nodeId) ?? [];
|
|
3417
|
+
list.push(b.propKey);
|
|
3418
|
+
boundProps.set(b.nodeId, list);
|
|
3419
|
+
}
|
|
3420
|
+
let nodeCount = 0;
|
|
3421
|
+
let flattenableCount = 0;
|
|
3422
|
+
let compositingCount = 0;
|
|
3423
|
+
const walk = (node) => {
|
|
3424
|
+
nodeCount++;
|
|
3425
|
+
const f = facts.get(node.id);
|
|
3426
|
+
const ownDynamic = (boundProps.get(node.id)?.length ?? 0) > 0 || (f?.dynamicProps?.length ?? 0) > 0;
|
|
3427
|
+
const hasEvent = f?.hasEvent === true || node.flags.hasEvent === true;
|
|
3428
|
+
let childrenStatic = true;
|
|
3429
|
+
for (const child of node.children) {
|
|
3430
|
+
if (!walk(child)) childrenStatic = false;
|
|
3431
|
+
}
|
|
3432
|
+
const isStatic = !ownDynamic && !hasEvent && childrenStatic;
|
|
3433
|
+
node.flags.isStatic = isStatic;
|
|
3434
|
+
node.flags.hasEvent = hasEvent;
|
|
3435
|
+
node.flags.isNativeHost = node.kind === "native-host";
|
|
3436
|
+
node.props.paintHint.staticSubtree = isStatic;
|
|
3437
|
+
if (node.props.paintHint.needsCompositingLayer) compositingCount++;
|
|
3438
|
+
const reasons = [];
|
|
3439
|
+
if (!FLATTENABLE_KINDS.has(node.kind)) reasons.push(`kind=${node.kind} \u975E\u7ED8\u5236\u578B`);
|
|
3440
|
+
if (!isStatic) reasons.push("\u5B50\u6811\u542B\u52A8\u6001\u7ED1\u5B9A");
|
|
3441
|
+
if (hasEvent) reasons.push("\u7ED1\u5B9A\u4E8B\u4EF6\uFF08\u62CD\u5E73\u540E\u4E0D\u518D\u6709\u72EC\u7ACB\u7ED8\u5236\u5BF9\u8C61\uFF0C\u4E8B\u4EF6\u4E0D\u53EF\u8FBE\uFF09");
|
|
3442
|
+
if (f?.isAnimationTarget) reasons.push("\u52A8\u753B/\u8F6C\u573A\u76EE\u6807");
|
|
3443
|
+
if (f?.isComponentRoot) reasons.push("\u81EA\u5B9A\u4E49\u7EC4\u4EF6\u6839\uFF08\u7EC4\u4EF6\u8FB9\u754C\uFF09");
|
|
3444
|
+
if (node.props.paintHint.needsCompositingLayer) reasons.push("\u9700\u5408\u6210\u5C42\uFF08L3 \u7279\u6027\uFF09");
|
|
3445
|
+
if (node.props.paint.transform) reasons.push("\u542B transform");
|
|
3446
|
+
if (boundary.has(node.id)) reasons.push("\u7ED3\u6784\u8FB9\u754C\uFF08\u5BB9\u5668/\u5BBF\u4E3B\uFF09");
|
|
3447
|
+
const flattenEligible = reasons.length === 0;
|
|
3448
|
+
node.flags.flattenEligible = flattenEligible;
|
|
3449
|
+
if (flattenEligible) flattenableCount++;
|
|
3450
|
+
if (f?.requestFlatten && !flattenEligible) {
|
|
3451
|
+
diagnostics.push({
|
|
3452
|
+
severity: "error",
|
|
3453
|
+
code: "css.flatten-violation",
|
|
3454
|
+
message: `\u663E\u5F0F\u8BF7\u6C42\u62CD\u5E73\uFF0C\u4F46\u4E0D\u6EE1\u8DB3\u6761\u4EF6\uFF1A${reasons.join(" / ")}`,
|
|
3455
|
+
nodeId: node.id,
|
|
3456
|
+
source: node.source
|
|
3457
|
+
});
|
|
3458
|
+
}
|
|
3459
|
+
decisions.push({
|
|
3460
|
+
nodeId: node.id,
|
|
3461
|
+
kind: node.kind,
|
|
3462
|
+
semantic: node.semantic,
|
|
3463
|
+
isStatic,
|
|
3464
|
+
flattenEligible,
|
|
3465
|
+
reasons
|
|
3466
|
+
});
|
|
3467
|
+
return isStatic;
|
|
3468
|
+
};
|
|
3469
|
+
for (const root of tree.roots) walk(root);
|
|
3470
|
+
const stats = { nodeCount, flattenableCount, compositingCount };
|
|
3471
|
+
tree.stats = stats;
|
|
3472
|
+
tree.diagnostics.push(...diagnostics);
|
|
3473
|
+
return { diagnostics, stats, decisions };
|
|
3474
|
+
}
|
|
3475
|
+
function flattenRate(stats) {
|
|
3476
|
+
return stats.nodeCount > 0 ? stats.flattenableCount / stats.nodeCount : 0;
|
|
3477
|
+
}
|
|
3478
|
+
function groupBindingsByNode(bindings) {
|
|
3479
|
+
const out = /* @__PURE__ */ new Map();
|
|
3480
|
+
for (const b of bindings) {
|
|
3481
|
+
const list = out.get(b.nodeId) ?? [];
|
|
3482
|
+
list.push(b);
|
|
3483
|
+
out.set(b.nodeId, list);
|
|
3484
|
+
}
|
|
3485
|
+
return out;
|
|
3486
|
+
}
|
|
3487
|
+
|
|
3488
|
+
// ../slot-runtime/src/anim-curve.ts
|
|
3489
|
+
var AnimCurve = {
|
|
3490
|
+
LINEAR: 0,
|
|
3491
|
+
EASE_OUT_CUBIC: 1,
|
|
3492
|
+
EASE_IN_CUBIC: 2,
|
|
3493
|
+
EASE_IN_OUT_CUBIC: 3,
|
|
3494
|
+
SPRING_APPROX: 4
|
|
3495
|
+
};
|
|
3496
|
+
var TABLE_N = 65;
|
|
3497
|
+
function springApprox(u) {
|
|
3498
|
+
return 1 - Math.exp(-6 * u) * Math.cos(10 * u);
|
|
3499
|
+
}
|
|
3500
|
+
function buildTable(curve) {
|
|
3501
|
+
const t = new Float64Array(TABLE_N);
|
|
3502
|
+
for (let i = 0; i < TABLE_N; i++) {
|
|
3503
|
+
const u = i / (TABLE_N - 1);
|
|
3504
|
+
t[i] = curve === AnimCurve.EASE_OUT_CUBIC ? 1 - (1 - u) ** 3 : curve === AnimCurve.EASE_IN_CUBIC ? u ** 3 : curve === AnimCurve.EASE_IN_OUT_CUBIC ? u < 0.5 ? 4 * u ** 3 : 1 - (-2 * u + 2) ** 3 / 2 : curve === AnimCurve.SPRING_APPROX ? springApprox(u) : u;
|
|
3505
|
+
}
|
|
3506
|
+
t[0] = 0;
|
|
3507
|
+
t[TABLE_N - 1] = 1;
|
|
3508
|
+
return t;
|
|
3509
|
+
}
|
|
3510
|
+
var TABLES = [
|
|
3511
|
+
buildTable(AnimCurve.LINEAR),
|
|
3512
|
+
buildTable(AnimCurve.EASE_OUT_CUBIC),
|
|
3513
|
+
buildTable(AnimCurve.EASE_IN_CUBIC),
|
|
3514
|
+
buildTable(AnimCurve.EASE_IN_OUT_CUBIC),
|
|
3515
|
+
buildTable(AnimCurve.SPRING_APPROX)
|
|
3516
|
+
];
|
|
3517
|
+
|
|
3518
|
+
// ../slot-runtime/src/expr.ts
|
|
3519
|
+
var PURE_CALLS = {
|
|
3520
|
+
// —— Math(纯计算)——
|
|
3521
|
+
"Math.abs": Math.abs,
|
|
3522
|
+
"Math.ceil": Math.ceil,
|
|
3523
|
+
"Math.floor": Math.floor,
|
|
3524
|
+
"Math.round": Math.round,
|
|
3525
|
+
"Math.trunc": Math.trunc,
|
|
3526
|
+
"Math.sign": Math.sign,
|
|
3527
|
+
"Math.sqrt": Math.sqrt,
|
|
3528
|
+
"Math.cbrt": Math.cbrt,
|
|
3529
|
+
"Math.pow": Math.pow,
|
|
3530
|
+
"Math.exp": Math.exp,
|
|
3531
|
+
"Math.log": Math.log,
|
|
3532
|
+
"Math.log2": Math.log2,
|
|
3533
|
+
"Math.log10": Math.log10,
|
|
3534
|
+
"Math.min": Math.min,
|
|
3535
|
+
"Math.max": Math.max,
|
|
3536
|
+
// —— 类型转换(call 形态,非 new)——
|
|
3537
|
+
"String": String,
|
|
3538
|
+
"Number": Number,
|
|
3539
|
+
"Boolean": Boolean,
|
|
3540
|
+
// —— 解析 / 判定(全局)——
|
|
3541
|
+
"parseInt": parseInt,
|
|
3542
|
+
"parseFloat": parseFloat,
|
|
3543
|
+
"isNaN": isNaN,
|
|
3544
|
+
"isFinite": isFinite,
|
|
3545
|
+
"Number.isFinite": Number.isFinite,
|
|
3546
|
+
"Number.isInteger": Number.isInteger,
|
|
3547
|
+
"Number.isNaN": Number.isNaN,
|
|
3548
|
+
// —— 数组判定 ——
|
|
3549
|
+
"Array.isArray": Array.isArray
|
|
3550
|
+
};
|
|
3551
|
+
var GLOBAL_CONST_MEMBERS = {
|
|
3552
|
+
"Math.PI": Math.PI,
|
|
3553
|
+
"Math.E": Math.E,
|
|
3554
|
+
"Math.LN2": Math.LN2,
|
|
3555
|
+
"Math.LN10": Math.LN10,
|
|
3556
|
+
"Math.LOG2E": Math.LOG2E,
|
|
3557
|
+
"Math.LOG10E": Math.LOG10E,
|
|
3558
|
+
"Math.SQRT2": Math.SQRT2,
|
|
3559
|
+
"Math.SQRT1_2": Math.SQRT1_2,
|
|
3560
|
+
"Number.MAX_SAFE_INTEGER": Number.MAX_SAFE_INTEGER,
|
|
3561
|
+
"Number.MIN_SAFE_INTEGER": Number.MIN_SAFE_INTEGER,
|
|
3562
|
+
"Number.EPSILON": Number.EPSILON,
|
|
3563
|
+
"Number.MAX_VALUE": Number.MAX_VALUE,
|
|
3564
|
+
"Number.MIN_VALUE": Number.MIN_VALUE
|
|
3565
|
+
};
|
|
3566
|
+
|
|
3567
|
+
// ../slot-runtime/src/directives.ts
|
|
3568
|
+
var HOST_DIRECTIVE_SPECS = {
|
|
3569
|
+
animate: {
|
|
3570
|
+
argKind: "anim-preset",
|
|
3571
|
+
argHint: "\u52A8\u753B\u9884\u8BBE\uFF08fade / slide-up / slide-down / slide-left / slide-right / zoom / fade-slide-up\uFF09",
|
|
3572
|
+
desc: "\u503C\u53D8\u5316\uFF08\u6216\u9996\u6B21\u6C42\u503C\u4E3A\u771F\uFF09\u65F6\u5728\u8BE5\u8282\u70B9**\u64AD\u4E00\u6B21**\u9884\u8BBE\u52A8\u753B\uFF08\u8D70\u5185\u6838\u52A8\u753B\u901A\u9053\uFF0C\u4E0E <Transition> \u540C\u4E00\u5957\uFF09\u3002\u8BED\u4E49\u5BF9\u9F50\uFF1A\u6307\u4EE4\u7684 mounted\uFF08\u9996\u8BC4 truthy \u5373\u64AD\uFF09\u4E0E updated\uFF08\u503C\u53D8\u5316\u5373\u64AD\uFF09\u3002"
|
|
3573
|
+
}
|
|
3574
|
+
};
|
|
3575
|
+
var HOST_DIRECTIVE_NAMES = Object.keys(HOST_DIRECTIVE_SPECS);
|
|
3576
|
+
|
|
3577
|
+
// src/pnode-build.ts
|
|
3578
|
+
function kindFromSemantic(semantic, tag) {
|
|
3579
|
+
const s = semantic ?? "";
|
|
3580
|
+
if (!s) {
|
|
3581
|
+
if (tag === "text" || tag === "p-text") return "text";
|
|
3582
|
+
if (tag === "image" || tag === "img") return "image";
|
|
3583
|
+
return "view";
|
|
3584
|
+
}
|
|
3585
|
+
if (s.startsWith("layout.virtual-list") || s.startsWith("layout.masonry")) return "list";
|
|
3586
|
+
if (s.startsWith("layout.")) return "view";
|
|
3587
|
+
if (s === "ui.text" || s === "ui.heading" || s === "ui.label") return "text";
|
|
3588
|
+
if (s === "ui.rich-text") return "rich-text";
|
|
3589
|
+
if (s.startsWith("ui.image") || s === "ui.icon" || s === "ui.avatar" || s === "ui.media" || s === "ui.svg" || s === "ui.canvas") return "image";
|
|
3590
|
+
if (s === "shell.webview" || s === "ui.map" || s === "ui.camera" || s === "shell.ad" || s === "ui.ad") return "native-host";
|
|
3591
|
+
return "view";
|
|
3592
|
+
}
|
|
3593
|
+
function normalizePropsOf(raw, kind, st, ctx) {
|
|
3594
|
+
const source = { tag: ctx.tag, line: void 0 };
|
|
3595
|
+
const res = typeof raw.style === "string" ? normalizeStyleString(raw.style, { kind, nodeId: ctx.id, source }) : normalizeStyleDecls(raw.style ?? {}, { kind, nodeId: ctx.id, source });
|
|
3596
|
+
st.diagnostics.push(...res.diagnostics);
|
|
3597
|
+
return res.props;
|
|
3598
|
+
}
|
|
3599
|
+
function buildNode(raw, st) {
|
|
3600
|
+
const id = st.nextId++;
|
|
3601
|
+
const tag = raw.tag ?? raw.kind ?? "view";
|
|
3602
|
+
const kind = raw.kind ?? kindFromSemantic(raw.semantic, tag);
|
|
3603
|
+
const props = normalizePropsOf(raw, kind, st, { id, tag });
|
|
3604
|
+
for (const b of raw.bindings ?? []) {
|
|
3605
|
+
const updateKind = b.updateKind ?? inferUpdateKind(b.propKey);
|
|
3606
|
+
st.bindings.push({
|
|
3607
|
+
slotId: st.slot++,
|
|
3608
|
+
nodeId: id,
|
|
3609
|
+
propKey: b.propKey,
|
|
3610
|
+
exprId: b.exprId,
|
|
3611
|
+
updateKind,
|
|
3612
|
+
// ★Vapor IR 扩展(方案 §3.2):opCode 编译期确定 ⇒ 运行时无类型判断
|
|
3613
|
+
opCode: b.opCode ?? inferOpCode(updateKind),
|
|
3614
|
+
// ★缺省 L0:保守优先(误判 L1 = 静默不更新;方案 §5.1)
|
|
3615
|
+
tier: b.tier ?? "L0",
|
|
3616
|
+
deps: b.deps ?? []
|
|
3617
|
+
});
|
|
3618
|
+
}
|
|
3619
|
+
const facts = {
|
|
3620
|
+
hasEvent: raw.facts?.hasEvent,
|
|
3621
|
+
isAnimationTarget: raw.facts?.isAnimationTarget,
|
|
3622
|
+
isComponentRoot: raw.facts?.isComponentRoot,
|
|
3623
|
+
requestFlatten: raw.facts?.requestFlatten,
|
|
3624
|
+
dynamicProps: (raw.bindings ?? []).map((b) => b.propKey)
|
|
3625
|
+
};
|
|
3626
|
+
st.facts.set(id, facts);
|
|
3627
|
+
const node = {
|
|
3628
|
+
id,
|
|
3629
|
+
kind,
|
|
3630
|
+
semantic: raw.semantic,
|
|
3631
|
+
props,
|
|
3632
|
+
children: [],
|
|
3633
|
+
flags: { isStatic: true, flattenEligible: false, isLayoutBoundary: false, hasEvent: false, isNativeHost: kind === "native-host" },
|
|
3634
|
+
source: { tag }
|
|
3635
|
+
};
|
|
3636
|
+
if (raw.text !== void 0) node.text = raw.text;
|
|
3637
|
+
node.children = (raw.children ?? []).map((c) => buildNode(c, st));
|
|
3638
|
+
return node;
|
|
3639
|
+
}
|
|
3640
|
+
function inferUpdateKind(propKey) {
|
|
3641
|
+
if (propKey.startsWith("text.")) return propKey === "text.color" || propKey === "text.fontSize" ? "style" : "text-content";
|
|
3642
|
+
if (propKey.startsWith("layout.")) return "style";
|
|
3643
|
+
if (propKey.startsWith("paint.")) return "style";
|
|
3644
|
+
if (propKey === "visible" || propKey === "display") return "visibility";
|
|
3645
|
+
if (propKey.startsWith("list.")) return "list-data";
|
|
3646
|
+
return "attr";
|
|
3647
|
+
}
|
|
3648
|
+
function inferOpCode(updateKind) {
|
|
3649
|
+
switch (updateKind) {
|
|
3650
|
+
case "attr":
|
|
3651
|
+
return 1 /* SET_PROP */;
|
|
3652
|
+
case "style":
|
|
3653
|
+
return 2 /* SET_STYLE */;
|
|
3654
|
+
case "text-content":
|
|
3655
|
+
return 3 /* SET_TEXT */;
|
|
3656
|
+
case "visibility":
|
|
3657
|
+
return 5 /* TOGGLE_VIS */;
|
|
3658
|
+
case "list-data":
|
|
3659
|
+
return 32 /* LIST_SET */;
|
|
3660
|
+
default: {
|
|
3661
|
+
const never = updateKind;
|
|
3662
|
+
throw new Error(`\u672A\u77E5\u66F4\u65B0\u79CD\u7C7B\uFF1A${String(never)}`);
|
|
3663
|
+
}
|
|
3664
|
+
}
|
|
3665
|
+
}
|
|
3666
|
+
function buildPTree(raws, opts = {}) {
|
|
3667
|
+
const st = {
|
|
3668
|
+
nextId: opts.startId ?? 1,
|
|
3669
|
+
bindings: [],
|
|
3670
|
+
diagnostics: [],
|
|
3671
|
+
facts: /* @__PURE__ */ new Map(),
|
|
3672
|
+
slot: 0
|
|
3673
|
+
};
|
|
3674
|
+
const roots = raws.map((r) => buildNode(r, st));
|
|
3675
|
+
const tree = {
|
|
3676
|
+
roots,
|
|
3677
|
+
bindings: st.bindings,
|
|
3678
|
+
diagnostics: st.diagnostics,
|
|
3679
|
+
stats: { nodeCount: 0, flattenableCount: 0, compositingCount: 0 }
|
|
3680
|
+
};
|
|
3681
|
+
analyzePTree(tree, { facts: st.facts, boundaryIds: opts.analyze?.boundaryIds });
|
|
3682
|
+
return tree;
|
|
3683
|
+
}
|
|
3684
|
+
function styleOf(ir) {
|
|
3685
|
+
const s = ir.props.style;
|
|
3686
|
+
if (typeof s === "string") return s;
|
|
3687
|
+
if (s && typeof s === "object") return s;
|
|
3688
|
+
return void 0;
|
|
3689
|
+
}
|
|
3690
|
+
function rawFromComponentIR(ir) {
|
|
3691
|
+
const children = (ir.children ?? []).map(rawFromComponentIR);
|
|
3692
|
+
const raw = {
|
|
3693
|
+
semantic: ir.semantic,
|
|
3694
|
+
tag: ir.tag,
|
|
3695
|
+
children
|
|
3696
|
+
};
|
|
3697
|
+
const st = styleOf(ir);
|
|
3698
|
+
if (st) raw.style = st;
|
|
3699
|
+
return raw;
|
|
3700
|
+
}
|
|
3701
|
+
function buildPTreeFromComponentIR(roots, opts = {}) {
|
|
3702
|
+
return buildPTree(roots.map(rawFromComponentIR), opts);
|
|
3703
|
+
}
|
|
3704
|
+
|
|
3705
|
+
// src/pnode-trace.ts
|
|
3706
|
+
function formatPNodeLine(decision) {
|
|
3707
|
+
const bits = [
|
|
3708
|
+
`#${decision.nodeId}`,
|
|
3709
|
+
decision.kind,
|
|
3710
|
+
decision.semantic ? `(${decision.semantic})` : "",
|
|
3711
|
+
decision.isStatic ? "\u9759\u6001" : "\u52A8\u6001",
|
|
3712
|
+
decision.flattenEligible ? "\u2705\u53EF\u62CD\u5E73" : `\u26D4\u53D7\u963B\uFF1A${decision.reasons.join(" / ")}`
|
|
3713
|
+
];
|
|
3714
|
+
return bits.filter(Boolean).join(" ");
|
|
3715
|
+
}
|
|
3716
|
+
function formatPTrace(tree, analysis, opts = {}) {
|
|
3717
|
+
const max = opts.maxNodes ?? 200;
|
|
3718
|
+
const lines = [];
|
|
3719
|
+
const rate = (flattenRate(tree.stats) * 100).toFixed(1);
|
|
3720
|
+
lines.push("### \u6E32\u67D3 IR\uFF08\u6E32\u67D3\u5C42\u51B3\u7B56\uFF09");
|
|
3721
|
+
lines.push(` \u8282\u70B9 ${tree.stats.nodeCount} \xB7 \u53EF\u62CD\u5E73 ${tree.stats.flattenableCount}\uFF08\u62CD\u5E73\u7387 ${rate}%\uFF09\xB7 \u9700\u5408\u6210\u5C42 ${tree.stats.compositingCount}`);
|
|
3722
|
+
lines.push(` \u52A8\u6001\u7ED1\u5B9A ${tree.bindings.length} \u4E2A\u69FD\u4F4D`);
|
|
3723
|
+
lines.push(" \uFF08\u53C2\u7167\uFF1A\u771F\u673A\u5B9E\u6D4B 4050 \u5143\u7D20\u62CD\u5E73\u7387 98.8% \u2192 \u5185\u5B58 186.7\u219217.7MB \xB7 \u8017\u65F6 190.5\u2192129.9ms\uFF09");
|
|
3724
|
+
const ordered = [...analysis.decisions].sort((a, b) => a.nodeId - b.nodeId);
|
|
3725
|
+
const shown = ordered.filter((d) => !opts.onlyBlocked || !d.flattenEligible).slice(0, max);
|
|
3726
|
+
lines.push("");
|
|
3727
|
+
lines.push(` \u9010\u8282\u70B9\u51B3\u7B56${opts.onlyBlocked ? "\uFF08\u4EC5\u53D7\u963B\u9879\uFF09" : ""}\uFF1A`);
|
|
3728
|
+
for (const d of shown) lines.push(` ${formatPNodeLine(d)}`);
|
|
3729
|
+
const hidden = ordered.filter((d) => !opts.onlyBlocked || !d.flattenEligible).length - shown.length;
|
|
3730
|
+
if (hidden > 0) lines.push(` \u2026 \u7701\u7565 ${hidden} \u4E2A\u8282\u70B9\uFF08--max-nodes \u8C03\u6574\uFF09`);
|
|
3731
|
+
if (opts.includeDiagnostics !== false && tree.diagnostics.length) {
|
|
3732
|
+
const errs = tree.diagnostics.filter((d) => d.severity === "error");
|
|
3733
|
+
const warns = tree.diagnostics.filter((d) => d.severity === "warn");
|
|
3734
|
+
lines.push("");
|
|
3735
|
+
lines.push(` \u8BCA\u65AD\uFF1Aerror ${errs.length} \xB7 warn ${warns.length}`);
|
|
3736
|
+
for (const d of errs) lines.push(` \u2717 [${d.code}] ${d.message}${d.source ? `\uFF08${d.source.tag}\uFF09` : ""}`);
|
|
3737
|
+
for (const d of warns.slice(0, 20)) lines.push(` \u26A0 [${d.code}] ${d.message}`);
|
|
3738
|
+
if (warns.length > 20) lines.push(` \u2026 \u53E6\u6709 ${warns.length - 20} \u6761 warn`);
|
|
3739
|
+
}
|
|
3740
|
+
return lines.join("\n");
|
|
3741
|
+
}
|
|
3742
|
+
function decisionOf(analysis, nodeId) {
|
|
3743
|
+
return analysis.decisions.find((d) => d.nodeId === nodeId);
|
|
3744
|
+
}
|
|
3745
|
+
function walkPTree(roots, visit) {
|
|
3746
|
+
const walk = (nodes, depth) => {
|
|
3747
|
+
for (const n of nodes) {
|
|
3748
|
+
visit(n, depth);
|
|
3749
|
+
walk(n.children, depth + 1);
|
|
3750
|
+
}
|
|
3751
|
+
};
|
|
3752
|
+
walk(roots, 0);
|
|
3753
|
+
}
|
|
2320
3754
|
export {
|
|
3755
|
+
CAPABILITY_CATEGORY,
|
|
3756
|
+
CAPABILITY_CATEGORY_ORDER,
|
|
2321
3757
|
COMPONENT_IR_SCHEMA,
|
|
2322
3758
|
DEFAULT_DESIGN_WIDTH,
|
|
2323
3759
|
DEGRADATION_TABLE,
|
|
@@ -2339,12 +3775,18 @@ export {
|
|
|
2339
3775
|
SPEC_RATCHET,
|
|
2340
3776
|
TAG_SEMANTIC_ALIASES,
|
|
2341
3777
|
TAG_SEMANTIC_MAP,
|
|
3778
|
+
analyzePTree,
|
|
3779
|
+
auditCapabilityDomains,
|
|
2342
3780
|
auditCatalogConsistency,
|
|
2343
3781
|
auditDegradation,
|
|
2344
3782
|
auditMatrixReferences,
|
|
2345
3783
|
auditMiniprogramCoverage,
|
|
2346
3784
|
auditSpecCoverage,
|
|
2347
3785
|
auditSpecOverrideRefs,
|
|
3786
|
+
buildPTree,
|
|
3787
|
+
buildPTreeFromComponentIR,
|
|
3788
|
+
camel,
|
|
3789
|
+
capabilityDomainOf,
|
|
2348
3790
|
checkComponentSnapshot,
|
|
2349
3791
|
checkPrimitiveCatalog,
|
|
2350
3792
|
checkPropDegradation,
|
|
@@ -2353,19 +3795,40 @@ export {
|
|
|
2353
3795
|
classifySpecComponent,
|
|
2354
3796
|
collectPropDiagnostics,
|
|
2355
3797
|
componentPrimitives,
|
|
3798
|
+
decisionOf,
|
|
2356
3799
|
degradationOf,
|
|
2357
3800
|
degradeProp,
|
|
3801
|
+
derivePaintHint,
|
|
3802
|
+
expandEdges,
|
|
2358
3803
|
extractSemanticTree,
|
|
3804
|
+
flattenRate,
|
|
2359
3805
|
formatCoverageReport,
|
|
2360
3806
|
formatDegradationReport,
|
|
3807
|
+
formatPNodeLine,
|
|
3808
|
+
formatPTrace,
|
|
3809
|
+
groupBindingsByNode,
|
|
2361
3810
|
implementedPrimitives,
|
|
3811
|
+
inferOpCode,
|
|
3812
|
+
inferUpdateKind,
|
|
3813
|
+
isNeutralColor,
|
|
3814
|
+
isOpaqueColor,
|
|
3815
|
+
kindFromSemantic,
|
|
2362
3816
|
mapSemanticToBackend,
|
|
3817
|
+
normalizeStyleDecls,
|
|
3818
|
+
normalizeStyleString,
|
|
3819
|
+
parseStyleString,
|
|
3820
|
+
parseTransform,
|
|
2363
3821
|
primitiveById,
|
|
2364
3822
|
primitiveBySemantic,
|
|
2365
3823
|
primitiveByTag,
|
|
3824
|
+
rawFromComponentIR,
|
|
3825
|
+
resolveLength,
|
|
3826
|
+
rgbOfColor,
|
|
3827
|
+
splitTopLevel,
|
|
2366
3828
|
toComponentIR,
|
|
2367
3829
|
toComponentTree,
|
|
2368
3830
|
validateComponentIR,
|
|
2369
3831
|
validateComponentTree,
|
|
2370
|
-
validateGridConstraints
|
|
3832
|
+
validateGridConstraints,
|
|
3833
|
+
walkPTree
|
|
2371
3834
|
};
|