Fix inspector undo, time changes and legacy disabled-state migration; verify secure template and 1039 regressions

这个提交包含在:
dayu 于 2026-10-21 22:53:12 +08:00 提交
1 父节点 f4c0d608a4
当前提交 f79d24eb01
19 个文件已更改
+298 -15

没有与搜索条件相匹配的文件

+1 -1
查看文件
@@ -41,4 +41,4 @@ xcodebuild -project beatygril/beatygril.xcodeproj -scheme 'Wrapper Application'
使用AppKit自绘卡片、滑块和开关,原生数值输入、肤色圆点、单项/分组启停及重置。FCP仅发布自定义面板210和两项版权信息,撤掉平铺参数与手动分析板块。新面板使用独立NSSecureCoding状态对象完整保存设置,支持复制、撤销数据和数值插值;未编辑时保持旧文档参数值。旧分析数据不参与实时渲染。 使用AppKit自绘卡片、滑块和开关,原生数值输入、肤色圆点、单项/分组启停及重置。FCP仅发布自定义面板210和两项版权信息,撤掉平铺参数与手动分析板块。新面板使用独立NSSecureCoding状态对象完整保存设置,支持复制、撤销数据和数值插值;未编辑时保持旧文档参数值。旧分析数据不参与实时渲染。
`./scripts/test-inspector.sh`验证参数路由、保存恢复、事务、绕过/重置和离屏布局;`python3 scripts/inspector-template.py audit Templates/大鱼人像美化/大鱼人像美化.moef`验证模板自定义数据和发布入口。离屏截图和模拟宿主检查不能替代Motion/FCP的实际嵌入、键盘/VoiceOver和工程重开验收,仍由用户自测。当前阶段尚未替换已安装1.0.6。 `./scripts/test-inspector.sh`验证参数路由、保存恢复、事务、绕过/重置和离屏布局;`python3 scripts/inspector-template.py audit Templates/大鱼人像美化/大鱼人像美化.moef`验证模板自定义数据和发布入口。离屏截图和模拟宿主检查不能替代Motion/FCP的实际嵌入、键盘/VoiceOver和工程重开验收,仍由用户自测。完整1039项回归通过,Inspector/宿主适配/OSC启用ASan。离屏演示图见[面板预览](docs/verification/v1.1.0/inspector-preview.png)。正式签名公证安装正在准备。
+10
查看文件
@@ -58,3 +58,13 @@ FCP模板从 `Templates/大鱼人像美化/` 恢复,将 `.moef` 和 `Drop Zone
`ad54726b17fabd11f46bc6121e664eac38960a23`已独立从上述远程克隆,87文件的byte/blob/mode与14项资源一致,strict/full fsck和559项核心测试通过,无LFS、子模块或APP产物。临时恢复目录已清理,完整报告见`docs/verification/v1.0.6/recovery-ad54726.json`。 `ad54726b17fabd11f46bc6121e664eac38960a23`已独立从上述远程克隆,87文件的byte/blob/mode与14项资源一致,strict/full fsck和559项核心测试通过,无LFS、子模块或APP产物。临时恢复目录已清理,完整报告见`docs/verification/v1.0.6/recovery-ad54726.json`。
1.0.6已完成正式签名、公证(Accepted ab0271c8-0c4b-4b10-9474-cebe9b55385b)、票据粘附、唯一正式目录安装和配套模板同步;临时APP/旧备份APP/公证ZIP已清理。完整回执及有界副本扫描见`docs/verification/v1.0.6/installed-verification.json`。未操作Motion/FCP界面,最终视觉验收保留为用户自测。 1.0.6已完成正式签名、公证(Accepted ab0271c8-0c4b-4b10-9474-cebe9b55385b)、票据粘附、唯一正式目录安装和配套模板同步;临时APP/旧备份APP/公证ZIP已清理。完整回执及有界副本扫描见`docs/verification/v1.0.6/installed-verification.json`。未操作Motion/FCP界面,最终视觉验收保留为用户自测。
## 1.1.0 面板与自动处理
1.1.0(9)使用自有原生卡片面板和PGInspectorState安全归档。源代码、面板资源(全部程序绘制)、Motion模板与自有自定义参数数据均在仓库;新增`./scripts/test-inspector.sh`及`scripts/inspector-template.py audit`验证入口。恢复时需完整保留`.moef`中的自定义参数210、实际dataValue/defaultVal和两项版权发布;不要删掉仍用于旧工程迁移的隐藏参数定义和组路径。不得拿其他插件的自定义归档替换本产品自己的归档。
新渲染统一实时自动人脸跟随,旧手动分析数据仍保存在旧文档中但不参与本版渲染;主界面不再显示前后分析、目标列表、蒙版调试与平铺控件。三张卡片之外的细调收在更多设置。自定义对象保存完整界面状态,首次编辑前兼容旧参数;数值、开关、启停mask按已测试的复制/插值规则恢复。真实宿主UI及工程重开仍需用户自测。
正式构建、公证、安装仍使用同一`python3 scripts/deliver.py --wait-for-hosts --user-tests`,保持固定Bundle ID与APP路径,仅在用户自行退出Motion/FCP后替换,最终版本节点与回执随交付记录补齐。
兼容边界:未编辑面板时读取旧文档原参数;首次编辑会把当前数值采集为完整面板状态。旧版各项独立关键帧曲线尚未自动迁移为新面板关键帧,不宣称编辑前后任意旧动画逐帧等价。面板本身的数值插值与开关保持已做离屏验证,实际宿主关键帧交互仍待用户测试。
@@ -153,5 +153,38 @@
"malformedOrVendorArchiveRejectionCases": 5, "malformedOrVendorArchiveRejectionCases": 5,
"foundationOuterSecureDecode": "pass", "foundationOuterSecureDecode": "pass",
"innerClassDecode": "model generates own archive; structure decoded and exact generated bytes preserved; native host instantiation pending user" "innerClassDecode": "model generates own archive; structure decoded and exact generated bytes preserved; native host instantiation pending user"
},
"automatedInspectorValidation": {
"scope": "offscreen AppKit inspector, real production controller and mocked host callbacks; no Motion/FCP UI",
"fullChecks": 1039,
"counts": {
"beauty": 49,
"face": 116,
"thinChin": 394,
"hostAdapterASan": 248,
"oscASan": 98,
"inspectorASan": 134
},
"nativeVisualQA": "pending user",
"manualAnalysisUI": "removed; legacy IDs hidden and buttons disabled",
"renderMode": "realtime automatic follow; stored manual geometry never used",
"templatePublishedEntries": 3,
"offscreenWidths": [
280,
320,
380,
520
],
"independentViewChecks": 27,
"fixedRegressions": [
"shape group bypass clears face-box overlay",
"disabled legacy detail effects stay disabled on first edit",
"legacy global smoothing remains enabled when its old local group was off",
"new gesture reads current host after immediate undo",
"pending synchronization is bound to CMTime",
"same-time host change refreshes without waiting",
"explicit numeric input is never skipped due to stale UI cache"
],
"archives": "Own PGInspectorState secure payload; Foundation outer and inner decode and Motion alphabet roundtrip tested"
} }
} }
@@ -32,6 +32,7 @@ FOUNDATION_EXPORT PGBeautySettings PGSettingsWithInspectorState(PGBeautySettings
@property (nonatomic, copy, readonly) NSString *statusMessage; @property (nonatomic, copy, readonly) NSString *statusMessage;
- (instancetype)initWithAPIManager:(id<PROAPIAccessing>)manager; - (instancetype)initWithAPIManager:(id<PROAPIAccessing>)manager;
- (void)refresh; - (void)refresh;
- (void)documentDidChange;
- (void)beginInteraction; - (void)beginInteraction;
- (void)endInteraction; - (void)endInteraction;
- (BOOL)setValue:(double)value forParameter:(UInt32)identifier; - (BOOL)setValue:(double)value forParameter:(UInt32)identifier;
+26 -5
查看文件
@@ -116,7 +116,7 @@ PGBeautySettings PGSettingsWithInspectorState(PGBeautySettings s,PGInspectorStat
} }
if(![state valueForParameter:PGSmoothingEnabled])s.amount=s.midtoneDetail=s.highPassDetail=0; if(![state valueForParameter:PGSmoothingEnabled])s.amount=s.midtoneDetail=s.highPassDetail=0;
if(![state valueForParameter:PGColorEnabled]) {s.skinBrightness=s.skinHue=s.skinSaturation=s.skinEvenTone=s.teethWhitening=s.eyeBrightening=0;s.skinTonePreset=0;} if(![state valueForParameter:PGColorEnabled]) {s.skinBrightness=s.skinHue=s.skinSaturation=s.skinEvenTone=s.teethWhitening=s.eyeBrightening=0;s.skinTonePreset=0;}
if(![state valueForParameter:PGShapeEnabled])s.faceThin=s.faceBigEye=s.faceShort=s.faceNarrow=s.faceChin=s.faceV=s.faceJaw=s.faceCheekbone=s.faceSmallHead=0; if(![state valueForParameter:PGShapeEnabled]) {s.faceThin=s.faceBigEye=s.faceShort=s.faceNarrow=s.faceChin=s.faceV=s.faceJaw=s.faceCheekbone=s.faceSmallHead=0;s.showTrackBox=0;}
return s; return s;
} }
@@ -128,6 +128,7 @@ PGBeautySettings PGSettingsWithInspectorState(PGBeautySettings s,PGInspectorStat
@property (nonatomic,strong) id<FxCustomParameterActionAPI_v4> action; @property (nonatomic,strong) id<FxCustomParameterActionAPI_v4> action;
@property (nonatomic) BOOL interacting; @property (nonatomic) BOOL interacting;
@property (nonatomic) NSTimeInterval pendingUntil; @property (nonatomic) NSTimeInterval pendingUntil;
@property (nonatomic) CMTime pendingTime;
@end @end
@implementation PGInspectorController @implementation PGInspectorController
- (instancetype)initWithAPIManager:(id<PROAPIAccessing>)manager { - (instancetype)initWithAPIManager:(id<PROAPIAccessing>)manager {
@@ -145,6 +146,7 @@ PGBeautySettings PGSettingsWithInspectorState(PGBeautySettings s,PGInspectorStat
} }
- (void)finish {if(_interacting){_interacting=NO;[_action endAction:self];_action=nil;}} - (void)finish {if(_interacting){_interacting=NO;[_action endAction:self];_action=nil;}}
- (void)readAtTime:(CMTime)time { - (void)readAtTime:(CMTime)time {
if(_pendingUntil && CMTimeCompare(time,_pendingTime)!=0)_pendingUntil=0;
id<FxParameterRetrievalAPI_v6> api=[_manager apiForProtocol:@protocol(FxParameterRetrievalAPI_v6)]; id<FxParameterRetrievalAPI_v6> api=[_manager apiForProtocol:@protocol(FxParameterRetrievalAPI_v6)];
NSObject *object=nil;[api getCustomParameterValue:&object fromParameter:PGInspectorPanel atTime:time]; NSObject *object=nil;[api getCustomParameterValue:&object fromParameter:PGInspectorPanel atTime:time];
if([object isKindOfClass:PGInspectorState.class] && [(PGInspectorState *)object hasEdits]) { if([object isKindOfClass:PGInspectorState.class] && [(PGInspectorState *)object hasEdits]) {
@@ -162,16 +164,31 @@ PGBeautySettings PGSettingsWithInspectorState(PGBeautySettings s,PGInspectorStat
else [api getFloatValue:&value fromParameter:key atTime:time]; else [api getFloatValue:&value fromParameter:key atTime:time];
values[@(key)]=@(value); values[@(key)]=@(value);
} }
_state=[PGInspectorState stateWithValues:values disabledParameters:[NSSet set] hasEdits:NO]; BOOL legacyDetail=YES;[api getBoolValue:&legacyDetail fromParameter:PGDetailEnabled atTime:time];
NSMutableSet *disabled=legacyDetail?[NSMutableSet set]:[NSMutableSet setWithObjects:@(PGHighPass),@(PGSharpness),nil];
// Earlier versions put high-pass smoothing under a different group. Keep
// its effective state when importing a document whose local-skin group was
// off, rather than turning a still-active global effect off on first edit.
if(![values[@(PGSmoothingEnabled)] boolValue] && legacyDetail && [values[@(PGHighPass)] doubleValue]>0) {
values[@(PGSmoothingEnabled)]=@YES;
[disabled addObject:@(PGMidtone)];[disabled addObject:@(PGAmount)];
}
_state=[PGInspectorState stateWithValues:values disabledParameters:disabled hasEdits:NO];
} }
- (void)refresh { - (void)refresh {
if(_interacting)return; if(_interacting)return;
if(![self start])return; if(![self start])return;
@try {[self readAtTime:[_action currentTime]];_statusMessage=@"";} @finally {[self finish];} @try {[self readAtTime:[_action currentTime]];_statusMessage=@"";} @finally {[self finish];}
} }
- (void)documentDidChange {if(_interacting)return;_pendingUntil=0;[self refresh];}
- (void)beginInteraction { - (void)beginInteraction {
if(_interacting)return; if(_interacting)return;
if([self start])[self readAtTime:[_action currentTime]]; if([self start]) {
// A new gesture starts from the authoritative document, including an
// immediate undo. The short pending hold only prevents display flicker;
// it must never resurrect an undone value in a subsequent edit.
_pendingUntil=0;[self readAtTime:[_action currentTime]];
}
} }
- (void)endInteraction {[self finish];} - (void)endInteraction {[self finish];}
- (BOOL)commit:(PGInspectorState *)next { - (BOOL)commit:(PGInspectorState *)next {
@@ -190,7 +207,7 @@ PGBeautySettings PGSettingsWithInspectorState(PGBeautySettings s,PGInspectorStat
else if([d[@"kind"] isEqual:@"toggle"] || [d[@"kind"] isEqual:@"master"]) [api setBoolValue:value!=0 toParameter:key atTime:time]; else if([d[@"kind"] isEqual:@"toggle"] || [d[@"kind"] isEqual:@"master"]) [api setBoolValue:value!=0 toParameter:key atTime:time];
else [api setFloatValue:value toParameter:key atTime:time]; else [api setFloatValue:value toParameter:key atTime:time];
} }
_state=next;_pendingUntil=NSDate.timeIntervalSinceReferenceDate+.35;_statusMessage=@"";return YES; _state=next;_pendingUntil=NSDate.timeIntervalSinceReferenceDate+.35;_pendingTime=time;_statusMessage=@"";return YES;
} }
- (BOOL)setValue:(double)value forParameter:(UInt32)identifier { - (BOOL)setValue:(double)value forParameter:(UInt32)identifier {
NSDictionary *d=PGInspectorDescriptor(identifier);if(!d || !isfinite(value))return NO; NSDictionary *d=PGInspectorDescriptor(identifier);if(!d || !isfinite(value))return NO;
@@ -202,7 +219,11 @@ PGBeautySettings PGSettingsWithInspectorState(PGBeautySettings s,PGInspectorStat
} }
- (BOOL)toggleEnabledForParameter:(UInt32)identifier { - (BOOL)toggleEnabledForParameter:(UInt32)identifier {
NSDictionary *d=PGInspectorDescriptor(identifier);if(!d)return NO; NSDictionary *d=PGInspectorDescriptor(identifier);if(!d)return NO;
if([d[@"kind"] isEqual:@"master"] || [d[@"kind"] isEqual:@"toggle"])return [self setValue:![self.state valueForParameter:identifier] forParameter:identifier]; if([d[@"kind"] isEqual:@"master"] || [d[@"kind"] isEqual:@"toggle"]) {
BOOL owned=!_interacting;if(owned)[self beginInteraction];if(!_interacting)return NO;
BOOL ok=NO;@try {ok=[self setValue:![_state valueForParameter:identifier] forParameter:identifier];}
@finally {if(owned)[self finish];}return ok;
}
if(![d[@"bypass"] boolValue])return NO; if(![d[@"bypass"] boolValue])return NO;
BOOL owned=!_interacting;if(owned)[self beginInteraction];if(!_interacting)return NO; BOOL owned=!_interacting;if(owned)[self beginInteraction];if(!_interacting)return NO;
BOOL ok=NO;@try { BOOL ok=NO;@try {
@@ -10,6 +10,7 @@ NS_ASSUME_NONNULL_BEGIN
/// Also permits a deterministic controller in an offscreen preview or test. /// Also permits a deterministic controller in an offscreen preview or test.
- (instancetype)initWithController:(PGInspectorController *)controller; - (instancetype)initWithController:(PGInspectorController *)controller;
- (void)refreshFromHost; - (void)refreshFromHost;
- (void)hostParameterChanged;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END
+5 -1
查看文件
@@ -253,7 +253,10 @@ static PGInspectorGlyphButton *PGUIButton(NSString *glyph, NSColor *accent, id t
} }
if(number&&isfinite(number.doubleValue)){ if(number&&isfinite(number.doubleValue)){
double value=number.doubleValue/PGValueScale(_descriptor);value=fmin([_descriptor[@"max"]doubleValue],fmax([_descriptor[@"min"]doubleValue],value)); double value=number.doubleValue/PGValueScale(_descriptor);value=fmin([_descriptor[@"max"]doubleValue],fmax([_descriptor[@"min"]doubleValue],value));
if(fabs(value-[_inspector.controller.state valueForParameter:[_descriptor[@"id"]unsignedIntValue]])>.0000001)[_inspector writeValue:value parameter:[_descriptor[@"id"]unsignedIntValue]]; // Commit explicit input even when it equals our last displayed value:
// the document may have changed while the field editor held focus.
// The controller rereads the host and deduplicates the actual value.
[_inspector writeValue:value parameter:[_descriptor[@"id"]unsignedIntValue]];
}else NSBeep(); }else NSBeep();
_valueField.stringValue=PGFormattedValue(_descriptor,[_inspector.controller.state valueForParameter:[_descriptor[@"id"]unsignedIntValue]]); _valueField.stringValue=PGFormattedValue(_descriptor,[_inspector.controller.state valueForParameter:[_descriptor[@"id"]unsignedIntValue]]);
} }
@@ -331,6 +334,7 @@ static PGInspectorGlyphButton *PGUIButton(NSString *glyph, NSColor *accent, id t
} }
- (BOOL)hasActiveTextEditor { for(PGInspectorCard *card in _cards)for(PGInspectorRow *row in card.rows)if(row.valueField.currentEditor)return YES;return NO; } - (BOOL)hasActiveTextEditor { for(PGInspectorCard *card in _cards)for(PGInspectorRow *row in card.rows)if(row.valueField.currentEditor)return YES;return NO; }
- (void)refreshFromHost { if(_interactionDepth || [self hasActiveTextEditor])return;[_controller refresh];[self reloadControls]; } - (void)refreshFromHost { if(_interactionDepth || [self hasActiveTextEditor])return;[_controller refresh];[self reloadControls]; }
- (void)hostParameterChanged { if(_interactionDepth || [self hasActiveTextEditor])return;[_controller documentDidChange];[self reloadControls]; }
- (void)writeValue:(double)value parameter:(UInt32)identifier { [_controller setValue:value forParameter:identifier];[self reloadControls]; } - (void)writeValue:(double)value parameter:(UInt32)identifier { [_controller setValue:value forParameter:identifier];[self reloadControls]; }
- (void)beginControlInteraction { if(_interactionDepth++==0)[_controller beginInteraction]; } - (void)beginControlInteraction { if(_interactionDepth++==0)[_controller beginInteraction]; }
- (void)endControlInteraction { if(!_interactionDepth)return;if(--_interactionDepth==0){[_controller endInteraction];[self refreshFromHost];} } - (void)endControlInteraction { if(!_interactionDepth)return;if(--_interactionDepth==0){[_controller endInteraction];[self refreshFromHost];} }
+12 -1
查看文件
@@ -93,6 +93,7 @@ static CIImage *PGImageFromTile(FxImageTile *tile) {
@property BOOL analysisForward; @property BOOL analysisForward;
@property (strong) NSArray<NSString *> *loadedTracks; @property (strong) NSArray<NSString *> *loadedTracks;
@property BOOL reportedRender; @property BOOL reportedRender;
@property (strong) NSHashTable<PGInspectorView *> *inspectorViews;
@end @end
@implementation beatygrilPlugIn @implementation beatygrilPlugIn
@@ -103,6 +104,7 @@ static CIImage *PGImageFromTile(FxImageTile *tile) {
_analysisFaceProcessor=[PGFaceProcessor new]; _analysisFaceProcessor=[PGFaceProcessor new];
_contexts=[NSMutableDictionary new]; _analysisSettings=PGBeautyDefaults(); _contexts=[NSMutableDictionary new]; _analysisSettings=PGBeautyDefaults();
_analysisTime=kCMTimeZero; _analysisTime=kCMTimeZero;
_inspectorViews=[NSHashTable weakObjectsHashTable];
} }
return self; return self;
} }
@@ -165,7 +167,16 @@ static CIImage *PGImageFromTile(FxImageTile *tile) {
return ok ? YES : PGFail(error,@"宿主未能创建完整的人像美化参数。"); return ok ? YES : PGFail(error,@"宿主未能创建完整的人像美化参数。");
} }
- (NSView *)createViewForParameterID:(UInt32)identifier { - (NSView *)createViewForParameterID:(UInt32)identifier {
return identifier==PGInspectorPanel ? [[PGInspectorView alloc] initWithAPIManager:_apiManager] : nil; if(identifier!=PGInspectorPanel)return nil;
PGInspectorView *view=[[PGInspectorView alloc] initWithAPIManager:_apiManager];
@synchronized(_inspectorViews){[_inspectorViews addObject:view];}return view;
}
- (BOOL)parameterChanged:(UInt32)identifier atTime:(CMTime)time error:(NSError **)error {
// The host callback covers undo and playhead-driven parameter changes. Hop
// to AppKit's thread and avoid retaining remote inspector views in render.
NSArray *views;@synchronized(_inspectorViews){views=_inspectorViews.allObjects;}
for(PGInspectorView *view in views)dispatch_async(dispatch_get_main_queue(), ^{[view hostParameterChanged];});
return YES;
} }
- (Class)classForCustomParameterID:(UInt32)identifier {return identifier==PGInspectorPanel?PGInspectorState.class:Nil;} - (Class)classForCustomParameterID:(UInt32)identifier {return identifier==PGInspectorPanel?PGInspectorState.class:Nil;}
- (NSSet<Class> *)classesForCustomParameterID:(UInt32)identifier { - (NSSet<Class> *)classesForCustomParameterID:(UInt32)identifier {
+5
查看文件
@@ -0,0 +1,5 @@
Beauty renderer: 49 checks, 0 failures (Metal).
2026-10-21 22:48:21.085 face-tests[18460:5636636] FBBA: creating VNFaceBBoxAligner from VNFaceDetectorRevision2: VNFaceDetectorRevision2
Face processing: 116 checks passed
2026-10-21 22:48:24.576 slim-tests[18472:5636747] FBBA: creating VNFaceBBoxAligner from VNFaceDetectorRevision2: VNFaceDetectorRevision2
PASS: 394 slim alignment checks; maximum UV oracle error 1.09828022e-05
+21
查看文件
@@ -0,0 +1,21 @@
2026-10-21 22:52:28.194 host-tests[18824:5644007] FBBA: creating VNFaceBBoxAligner from VNFaceDetectorRevision2: VNFaceDetectorRevision2
Default IOSurface maximum byte difference: 0
Alternate destination tag maximum byte difference: 0
Anatomy default 0/0 max=0 mean=0.0000000 eye=0/0 chin=0 inverted=0
Anatomy default 0/1 max=0 mean=0.0000000 eye=0/0 chin=0 inverted=0
Anatomy default 1/0 max=0 mean=0.0000000 eye=0/0 chin=0 inverted=0
Anatomy default 1/1 max=0 mean=0.0000000 eye=0/0 chin=0 inverted=0
Anatomy big-eye 0/0 max=2 mean=0.0008917 eye=33510/33880 chin=0 inverted=0
Anatomy big-eye 0/1 max=2 mean=0.0008917 eye=33510/33880 chin=0 inverted=0
Anatomy big-eye 1/0 max=2 mean=0.0008917 eye=33510/33880 chin=0 inverted=0
Anatomy big-eye 1/1 max=2 mean=0.0008917 eye=33510/33880 chin=0 inverted=0
Anatomy chin-positive 0/0 max=1 mean=0.0001459 eye=0/0 chin=116812 inverted=0
Anatomy chin-positive 0/1 max=1 mean=0.0001459 eye=0/0 chin=116812 inverted=0
Anatomy chin-positive 1/0 max=1 mean=0.0001459 eye=0/0 chin=116812 inverted=0
Anatomy chin-positive 1/1 max=1 mean=0.0001459 eye=0/0 chin=116812 inverted=0
Anatomy chin-negative 0/0 max=1 mean=0.0000849 eye=0/0 chin=60426 inverted=0
Anatomy chin-negative 0/1 max=1 mean=0.0000849 eye=0/0 chin=60426 inverted=0
Anatomy chin-negative 1/0 max=1 mean=0.0000849 eye=0/0 chin=60426 inverted=0
Anatomy chin-negative 1/1 max=1 mean=0.0000849 eye=0/0 chin=60426 inverted=0
Legacy fallback upright comparison: max=2 mean=0.0010357
Host adapter: 248 checks, 0 failures
二进制文件未显示。

之后

宽度:  |  高度:  |  大小: 482 KiB

@@ -0,0 +1 @@
Inspector: 134 checks, 0 failures
+21
查看文件
@@ -0,0 +1,21 @@
2026-10-21 22:48:19.739 osc-tests[18452:5636580] PGFaceOSC point-write desired=(0.25,0.75) setter=(0.25,0.75) readback=(0.25,0.75) mode=normalized verified=1
2026-10-21 22:48:19.745 osc-tests[18452:5636580] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(80,180) surface=320x240 origin=0
2026-10-21 22:48:20.198 osc-tests[18452:5636580] PGFaceOSC point-write desired=(0.8,0.3) setter=(1152,-432) readback=(0.8,0.3) mode=centered-pixels verified=1
2026-10-21 22:48:20.198 osc-tests[18452:5636580] PGFaceOSC coordinates selection=(0.7,0.4) objectBounds=(-1920,-1080,3840,2160) canvas=(152,78) surface=320x240 origin=0
2026-10-21 22:48:20.246 osc-tests[18452:5636580] PGFaceOSC point-write desired=(0.8,0.3) setter=(0.8,0.3) readback=(0.12345,0.87654) mode=normalized verified=0
2026-10-21 22:48:20.247 osc-tests[18452:5636580] PGFaceOSC point-write desired=(0.75,0.25) setter=(0.75,0.25) readback=(0.75,0.25) mode=normalized verified=1
2026-10-21 22:48:20.247 osc-tests[18452:5636580] PGFaceOSC coordinates selection=(0.75,0.25) objectBounds=(-100,-50,200,100) canvas=(160,60) surface=320x240 origin=0
2026-10-21 22:48:20.298 osc-tests[18452:5636580] PGFaceOSC point-write desired=(0.625,0.375) setter=(0.625,0.375) readback=(0.625,0.375) mode=normalized verified=1
2026-10-21 22:48:20.298 osc-tests[18452:5636580] PGFaceOSC coordinates selection=(0.625,0.375) objectBounds=(-100,-50,200,100) canvas=(240,60) surface=320x240 origin=0
2026-10-21 22:48:20.348 osc-tests[18452:5636580] PGFaceOSC point-write desired=(0.25,0.75) setter=(0.25,0.75) readback=(0.25,0.75) mode=normalized verified=1
2026-10-21 22:48:20.349 osc-tests[18452:5636580] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(144,180) surface=320x240 origin=0
2026-10-21 22:48:20.398 osc-tests[18452:5636580] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1
2026-10-21 22:48:20.398 osc-tests[18452:5636580] PGFaceOSC coordinates selection=(0,1) objectBounds=(-100,-50,200,100) canvas=(100,140) surface=320x240 origin=0
2026-10-21 22:48:20.447 osc-tests[18452:5636580] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(nan,nan) surface=320x240 origin=0
2026-10-21 22:48:20.500 osc-tests[18452:5636580] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1
2026-10-21 22:48:20.500 osc-tests[18452:5636580] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(inf,nan) surface=320x240 origin=0
2026-10-21 22:48:20.546 osc-tests[18452:5636580] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1
2026-10-21 22:48:20.547 osc-tests[18452:5636580] PGFaceOSC coordinates selection=(0.25,0.75) objectBounds=(-100,-50,200,100) canvas=(80,nan) surface=320x240 origin=0
2026-10-21 22:48:20.608 osc-tests[18452:5636580] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1
2026-10-21 22:48:20.608 osc-tests[18452:5636580] PGFaceOSC point-write desired=(0.5,0.5) setter=(0.5,0.5) readback=(0.5,0.5) mode=normalized verified=1
OSC adapter: 98 checks, 0 failures
@@ -0,0 +1,75 @@
{
"status": "structure_verified_native_loading_pending",
"templateSHA256": "e1c0cff1dd3541ed97cab2cb39a76ad4f28dc659b3be7991cc7ac92dd8ea090b",
"pluginVersion": "1.1.0",
"publications": [
{
"object": "510000011",
"channel": "./2/100",
"name": "FCP 版本作者→"
},
{
"object": "510000012",
"channel": "./2/100",
"name": "更多模板浏览器访问→"
},
{
"object": "510000005",
"channel": "./210",
"name": ""
}
],
"parameterPathsResolved": 50,
"sourceMediaClosed": true,
"mediaSHA256": "a7f8e3a2a431dbb3ce9e777040bb55e701cfe9ab9632e8cbb10359732bc8692e",
"customState": {
"class": "PGInspectorState",
"schema": 1,
"hasEdits": false,
"values": {
"41": 0.0,
"30": 1.0,
"71": 1.0,
"27": 0.0,
"5": 1.0,
"35": 0.0,
"24": 0.0,
"2": 0.0,
"32": 0.0,
"40": 1.7999999523162842,
"70": 1.0,
"18": 0.0,
"29": 0.0,
"26": 0.0,
"15": 0.0,
"4": 0.0,
"23": 0.0,
"12": 0.0,
"42": 0.0,
"72": 1.0,
"31": 0.0,
"1": 1.0,
"39": 0.0,
"28": 0.0,
"17": 0.0,
"25": 0.0,
"14": 0.0,
"3": 8.0,
"33": 0.0,
"22": 0.0
},
"disabled": [],
"classes": [
"NSDictionary",
"NSSet",
"PGInspectorState"
],
"innerBytes": 935,
"innerSHA256": "257540ac6079756a76f3d31dff1b9bca2b33393962057927791316965495ab60",
"outerBytes": 1127,
"outerSHA256": "95d5f1e2c35c40e9f1234b1480894bcc0ee68c0822c9bd3f7c04338062e6a26b",
"encodedCharacters": 1516,
"exactTextRoundtrip": true
},
"nativeLoading": "pending user; no host executed"
}
@@ -0,0 +1,33 @@
{
"scope": "offscreen AppKit inspector, real production controller and mocked host callbacks; no Motion/FCP UI",
"fullChecks": 1039,
"counts": {
"beauty": 49,
"face": 116,
"thinChin": 394,
"hostAdapterASan": 248,
"oscASan": 98,
"inspectorASan": 134
},
"nativeVisualQA": "pending user",
"manualAnalysisUI": "removed; legacy IDs hidden and buttons disabled",
"renderMode": "realtime automatic follow; stored manual geometry never used",
"templatePublishedEntries": 3,
"offscreenWidths": [
280,
320,
380,
520
],
"independentViewChecks": 27,
"fixedRegressions": [
"shape group bypass clears face-box overlay",
"disabled legacy detail effects stay disabled on first edit",
"legacy global smoothing remains enabled when its old local group was off",
"new gesture reads current host after immediate undo",
"pending synchronization is bound to CMTime",
"same-time host change refreshes without waiting",
"explicit numeric input is never skipped due to stale UI cache"
],
"archives": "Own PGInspectorState secure payload; Foundation outer and inner decode and Motion alphabet roundtrip tested"
}
+8
查看文件
@@ -81,3 +81,11 @@ Motion:独立普通视频项目,源PTS0、1920×1080/25fps/10秒,媒体通
配套1.0.6效果模板及Drop Zone.tiff已在宿主退出时同步并逐字节核验;moef SHA-256为`30ca8edf36801264c5de2c753c40b720aba8052633620713a12401098fcd2409`。模板仍保留固定UUID及原有参数发布。旧未标记的跟踪数据会回退实时跟随;需要离线重放时请重新分析目标。 配套1.0.6效果模板及Drop Zone.tiff已在宿主退出时同步并逐字节核验;moef SHA-256为`30ca8edf36801264c5de2c753c40b720aba8052633620713a12401098fcd2409`。模板仍保留固定UUID及原有参数发布。旧未标记的跟踪数据会回退实时跟随;需要离线重放时请重新分析目标。
临时Release APP、旧APP备份和公证ZIP已清理。项目、Codex工作树、DerivedData、桌面、下载、Applications、系统/用户FxPlug目录及本次paintfcp临时目录扫描仅发现正式路径外层APP,扫描错误0;没有将这项有界扫描表述为所有卷的完整搜索。完整回执、签名、发现、模板摘要和扫描范围在`verification/v1.0.6/`。最终发布标签为`v1.0.6`,产品源码与签名构建节点ad54726一致;其后的提交仅更新交付文档、模板说明和验证回执。 临时Release APP、旧APP备份和公证ZIP已清理。项目、Codex工作树、DerivedData、桌面、下载、Applications、系统/用户FxPlug目录及本次paintfcp临时目录扫描仅发现正式路径外层APP,扫描错误0;没有将这项有界扫描表述为所有卷的完整搜索。完整回执、签名、发现、模板摘要和扫描范围在`verification/v1.0.6/`。最终发布标签为`v1.0.6`,产品源码与签名构建节点ad54726一致;其后的提交仅更新交付文档、模板说明和验证回执。
## 1.1.0 原生卡片面板(构建前)
按参考界面的深色圆角卡片、青/绿/蓝紫分组、自绘滑块与开关、肤色圆点和单项重置实现独立AppKit面板,使用大鱼自己的品牌文字。新增自定义参数210及NSSecureCoding状态对象;FCP发布由48入口缩为面板+版权两项共3入口,旧49项参数和组路径保留隐藏兼容,手动分析命令隐藏且禁用。实时渲染不再载入任何保存的手动分析点阵,也不带旧分析大数据进入render snapshot。
完整1039项通过(美化49、人脸116、瘦脸/下巴394、宿主适配248、OSC98、检查器134),后三类适配/检查器使用ASan。检查器覆盖单项路由、完整安全归档、Motion自定义包内实际inner/outer的Foundation解码、初始不覆盖旧文档、数值插值、启停保值、重置、失效写入、事务配对、快速撤销/切时间后新编辑、显式数值输入与窄宽度布局。独立审查另用mock验证27项视图交互;280/320/380/520点宽度及更多展开不越界。测试修复了分组关闭后框仍显示、旧细节禁用状态丢失、快速撤销/换帧旧值回灌和旧显示缓存拦截明确输入等问题。
参考原版仅用于静态结构和外观分析,自有归档不含原版类/私有数据/品牌图。面板PNG为离屏演示参数预览,不是FCP原生截图。尚未操作Motion/FCP界面,真实宿主嵌入滚动、键盘/VoiceOver及工程重开由用户自测。源码与回执会按正式交付节点保存。
+3 -1
查看文件
@@ -8,6 +8,8 @@ compiler_extra=()
if [[ "${PAINTFCP_INSPECTOR_ASAN:-0}" == 1 ]]; then if [[ "${PAINTFCP_INSPECTOR_ASAN:-0}" == 1 ]]; then
compiler_extra=(-fsanitize=address -g) compiler_extra=(-fsanitize=address -g)
fi fi
python3 scripts/inspector-template.py audit 'Templates/大鱼人像美化/大鱼人像美化.moef' \
--inner-output "$output_dir/template-inner.bplist" --outer-output "$output_dir/template-outer.bplist" > /dev/null
xcrun clang "${compiler_extra[@]}" -fobjc-arc -fmodules -Wall -Wextra -Wno-unused-parameter \ xcrun clang "${compiler_extra[@]}" -fobjc-arc -fmodules -Wall -Wextra -Wno-unused-parameter \
-DCI_SILENCE_GL_DEPRECATION -mmacosx-version-min=13.0 -I "$plugin_dir" \ -DCI_SILENCE_GL_DEPRECATION -mmacosx-version-min=13.0 -I "$plugin_dir" \
-F /Library/Developer/SDKs/FxPlug.sdk/Library/Frameworks -F /Library/Developer/Frameworks \ -F /Library/Developer/SDKs/FxPlug.sdk/Library/Frameworks -F /Library/Developer/Frameworks \
@@ -19,4 +21,4 @@ xcrun clang "${compiler_extra[@]}" -fobjc-arc -fmodules -Wall -Wextra -Wno-unuse
-framework CoreMedia -framework CoreVideo -framework IOSurface -framework Metal \ -framework CoreMedia -framework CoreVideo -framework IOSurface -framework Metal \
-framework Vision -framework ImageIO -framework FxPlug -framework PluginManager \ -framework Vision -framework ImageIO -framework FxPlug -framework PluginManager \
-o "$output_dir/inspector-tests" -o "$output_dir/inspector-tests"
"$output_dir/inspector-tests" "$output_dir/inspector-tests" "$output_dir/template-inner.bplist" "$output_dir/template-outer.bplist"
+3 -3
查看文件
@@ -409,7 +409,7 @@ static void TrackingCoordinateTests(NSDictionary *archive, NSDictionary *hostVal
PGHostAPI *trustedAPI = [PGHostAPI new]; trustedAPI.hostTime = time; PGHostAPI *trustedAPI = [PGHostAPI new]; trustedAPI.hostTime = time;
beatygrilPlugIn *trusted = [[beatygrilPlugIn alloc] initWithAPIManager:trustedAPI]; [trusted addParametersWithError:nil]; beatygrilPlugIn *trusted = [[beatygrilPlugIn alloc] initWithAPIManager:trustedAPI]; [trusted addParametersWithError:nil];
trustedAPI.values = [values mutableCopy]; trustedAPI.values[@(PGTrackData3)] = [trustedTrack base64EncodedStringWithOptions:0]; trustedAPI.values = [values mutableCopy]; trustedAPI.values[@(PGTrackData3)] = [trustedTrack base64EncodedStringWithOptions:0];
Check(Render(trusted, output, @[source], Snapshot(trusted,time), time, nil, NULL), @"fresh plugin restores a marked coordinate archive"); Check(Render(trusted, output, @[source], Snapshot(trusted,time), time, nil, NULL), @"fresh plugin accepts a snapshot with marked legacy analysis data");
NSData *trustedBytes = SurfaceBytes(output); NSData *trustedBytes = SurfaceBytes(output);
PGBeautySettings tracked = [trusted settingsAtTime:time]; tracked.trackingEnabled=1; PGBeautySettings tracked = [trusted settingsAtTime:time]; tracked.trackingEnabled=1;
NSData *expectedSaved = ImageBytes(context, ProcessedReference(portrait,tracked,time,trustedTrack)); NSData *expectedSaved = ImageBytes(context, ProcessedReference(portrait,tracked,time,trustedTrack));
@@ -545,7 +545,7 @@ int main(int argc, const char *argv[]) {
api.values[@(PGSelection)] = @[@0.43, @0.76]; api.values[@(PGSelection)] = @[@0.43, @0.76];
NSData *oldRenderState = Snapshot(plugin, api.hostTime); NSData *oldRenderState = Snapshot(plugin, api.hostTime);
[plugin analyzeForward]; [plugin analyzeForward];
Check(api.forwardRequests == 1, @"forward analyze button calls host analysis API"); Check(api.forwardRequests == 1, @"hidden legacy forward-analysis method still accepts old document callbacks");
CMTimeRange desired, clip = CMTimeRangeMake(kCMTimeZero, CMTimeMake(240, 24)); CMTimeRange desired, clip = CMTimeRangeMake(kCMTimeZero, CMTimeMake(240, 24));
[plugin desiredAnalysisTimeRange:&desired forInputWithTimeRange:clip error:&error]; [plugin desiredAnalysisTimeRange:&desired forInputWithTimeRange:clip error:&error];
Check(CMTimeCompare(desired.start, api.hostTime) == 0 && CMTimeCompare(CMTimeRangeGetEnd(desired), CMTimeRangeGetEnd(clip)) == 0, @"analysis seed comes from current host playhead time"); Check(CMTimeCompare(desired.start, api.hostTime) == 0 && CMTimeCompare(CMTimeRangeGetEnd(desired), CMTimeRangeGetEnd(clip)) == 0, @"analysis seed comes from current host playhead time");
@@ -569,7 +569,7 @@ int main(int argc, const char *argv[]) {
Check([api.values[@(PGTrackEnabled)] boolValue] && CMTimeCompare(api.lastSetBoolTime, api.hostTime) == 0, @"analysis enables saved target at host seed time"); Check([api.values[@(PGTrackEnabled)] boolValue] && CMTimeCompare(api.lastSetBoolTime, api.hostTime) == 0, @"analysis enables saved target at host seed time");
NSData *trackedState = Snapshot(plugin, api.hostTime); NSData *trackedState = Snapshot(plugin, api.hostTime);
beatygrilPlugIn *restoredPlugin = [[beatygrilPlugIn alloc] initWithAPIManager:api]; beatygrilPlugIn *restoredPlugin = [[beatygrilPlugIn alloc] initWithAPIManager:api];
Check(Render(restoredPlugin, portraitOutput, @[portraitSource], trackedState, api.hostTime, &error, NULL), @"fresh plugin instance restores target data from serialized host snapshot"); Check(Render(restoredPlugin, portraitOutput, @[portraitSource], trackedState, api.hostTime, &error, NULL), @"fresh plugin instance uses automatic follow despite serialized legacy analysis settings");
[plugin analyzeBackward]; [plugin analyzeBackward];
Check(api.backwardRequests == 1, @"backward analyze button calls host API"); Check(api.backwardRequests == 1, @"backward analyze button calls host API");
[plugin desiredAnalysisTimeRange:&desired forInputWithTimeRange:clip error:&error]; [plugin desiredAnalysisTimeRange:&desired forInputWithTimeRange:clip error:&error];
+39 -3
查看文件
@@ -71,9 +71,9 @@ static void ModelTests(void) {
NSDictionary *bad=@{@(PGChin):@9,@(PGTonePreset):@(-2),@(PGBrightness):@(NAN),@999:@.5}; NSDictionary *bad=@{@(PGChin):@9,@(PGTonePreset):@(-2),@(PGBrightness):@(NAN),@999:@.5};
PGInspectorState *safe=[PGInspectorState stateWithValues:bad disabledParameters:[NSSet setWithObjects:@(PGRadius),@999,nil] hasEdits:YES]; PGInspectorState *safe=[PGInspectorState stateWithValues:bad disabledParameters:[NSSet setWithObjects:@(PGRadius),@999,nil] hasEdits:YES];
Check([safe valueForParameter:PGChin]==1 && [safe valueForParameter:PGTonePreset]==0 && [safe valueForParameter:PGBrightness]==0 && safe.values[@999]==nil && safe.disabledParameters.count==0,@"malformed controls are clamped, finite and limited to the supported schema"); Check([safe valueForParameter:PGChin]==1 && [safe valueForParameter:PGTonePreset]==0 && [safe valueForParameter:PGBrightness]==0 && safe.values[@999]==nil && safe.disabledParameters.count==0,@"malformed controls are clamped, finite and limited to the supported schema");
NSMutableDictionary *off=[a.values mutableCopy];off[@(PGShapeEnabled)]=@0;off[@(PGColorEnabled)]=@0;off[@(PGSmoothingEnabled)]=@0; NSMutableDictionary *off=[a.values mutableCopy];off[@(PGShapeEnabled)]=@0;off[@(PGColorEnabled)]=@0;off[@(PGSmoothingEnabled)]=@0;off[@(PGTrackBoxes)]=@1;
s=PGSettingsWithInspectorState(PGBeautyDefaults(),[PGInspectorState stateWithValues:off disabledParameters:[NSSet set] hasEdits:YES]); s=PGSettingsWithInspectorState(PGBeautyDefaults(),[PGInspectorState stateWithValues:off disabledParameters:[NSSet set] hasEdits:YES]);
Check(s.faceChin==0 && s.faceBigEye==0 && s.skinBrightness==0 && s.skinTonePreset==0 && s.midtoneDetail==0 && s.highPassDetail==0,@"card eye switches bypass the complete associated effect group"); Check(s.faceChin==0 && s.faceBigEye==0 && s.skinBrightness==0 && s.skinTonePreset==0 && s.midtoneDetail==0 && s.highPassDetail==0 && s.showTrackBox==0,@"card eye switches bypass the complete associated effect group");
} }
static void ControllerTests(void) { static void ControllerTests(void) {
PGInspectorTestAPI *api=[PGInspectorTestAPI new];beatygrilPlugIn *plugin=[[beatygrilPlugIn alloc]initWithAPIManager:api];[plugin addParametersWithError:nil]; PGInspectorTestAPI *api=[PGInspectorTestAPI new];beatygrilPlugIn *plugin=[[beatygrilPlugIn alloc]initWithAPIManager:api];[plugin addParametersWithError:nil];
@@ -98,6 +98,27 @@ static void ControllerTests(void) {
api.values=[undo mutableCopy];[[NSRunLoop currentRunLoop]runUntilDate:[NSDate dateWithTimeIntervalSinceNow:.36]];[c refresh]; api.values=[undo mutableCopy];[[NSRunLoop currentRunLoop]runUntilDate:[NSDate dateWithTimeIntervalSinceNow:.36]];[c refresh];
Check(Near([c.state valueForParameter:PGChin],[old valueForParameter:PGChin]),@"host undo refresh restores the prior committed state after pending synchronization expires"); Check(Near([c.state valueForParameter:PGChin],[old valueForParameter:PGChin]),@"host undo refresh restores the prior committed state after pending synchronization expires");
Check(api.accessViolations==0 && api.actionDepth==0 && api.actionStarts==api.actionEnds,@"all arbitrary-time reads and writes are inside balanced FxPlug action pairs"); Check(api.accessViolations==0 && api.actionDepth==0 && api.actionStarts==api.actionEnds,@"all arbitrary-time reads and writes are inside balanced FxPlug action pairs");
[c setValue:.7 forParameter:PGBrightness];NSDictionary *beforeUndo=api.undoValues;
api.values=[beforeUndo mutableCopy];[c setValue:.2 forParameter:PGThin];
PGInspectorState *afterImmediateUndo=api.values[@(PGInspectorPanel)];
PGInspectorState *undoneState=beforeUndo[@(PGInspectorPanel)];
Check(Near([afterImmediateUndo valueForParameter:PGBrightness],[undoneState valueForParameter:PGBrightness]) && Near([afterImmediateUndo valueForParameter:PGThin],.2),@"a new gesture immediately after host undo cannot resurrect the undone value");
NSMutableDictionary *seekValues=[c.state.values mutableCopy];seekValues[@(PGChin)]=@(-.8);seekValues[@(PGMidtone)]=@.6;
api.values[@(PGInspectorPanel)]=[PGInspectorState stateWithValues:seekValues disabledParameters:[NSSet set] hasEdits:YES];api.hostTime=CMTimeAdd(api.hostTime,CMTimeMake(1,24));[c refresh];
Check(Near([c.state valueForParameter:PGChin],-.8) && Near([c.state valueForParameter:PGMidtone],.6),@"seeking invalidates pending display state immediately at the new CMTime");
[c setValue:.1 forParameter:PGBrightness];Check(Near([c.state valueForParameter:PGChin],-.8),@"new-time edits preserve the other controls from that time instead of a stale prior frame");
api.values=[api.undoValues mutableCopy];[c documentDidChange];
Check(Near([c.state valueForParameter:PGBrightness],[seekValues[@(PGBrightness)]doubleValue]),@"a host undo notification refreshes same-time state immediately without a timed wait");
PGHostAPI *legacy=[PGHostAPI new];beatygrilPlugIn *legacyPlugin=[[beatygrilPlugIn alloc]initWithAPIManager:legacy];[legacyPlugin addParametersWithError:nil];
legacy.values[@(PGDetailEnabled)]=@NO;legacy.values[@(PGHighPass)]=@.6;legacy.values[@(PGSharpness)]=@.4;
PGInspectorController *imported=[[PGInspectorController alloc]initWithAPIManager:legacy];[imported refresh];[imported setValue:.3 forParameter:PGBrightness];
PGBeautySettings migrated=PGSettingsWithInspectorState(PGBeautyDefaults(),imported.state);
Check(migrated.highPassDetail==0 && migrated.sharpness==0 && Near([imported.state valueForParameter:PGHighPass],.6),@"first panel edit preserves disabled legacy detail controls and their stored amounts");
legacy.values[@(PGInspectorPanel)]=[PGInspectorState stateWithValues:@{} disabledParameters:[NSSet set] hasEdits:NO];
legacy.values[@(PGDetailEnabled)]=@YES;legacy.values[@(PGSmoothingEnabled)]=@NO;
PGInspectorController *split=[[PGInspectorController alloc]initWithAPIManager:legacy];[split refresh];[split setValue:.2 forParameter:PGBrightness];
PGBeautySettings splitSettings=PGSettingsWithInspectorState(PGBeautyDefaults(),split.state);
Check(Near(splitSettings.highPassDetail,.6) && splitSettings.amount==0 && splitSettings.midtoneDetail==0,@"legacy global smoothing remains active when its old local-skin group was disabled");
Check(api.forwardRequests==0 && api.backwardRequests==0,@"normal inspector edits never request manual analysis"); Check(api.forwardRequests==0 && api.backwardRequests==0,@"normal inspector edits never request manual analysis");
[api.disabledAPIs addObject:@"FxCustomParameterActionAPI_v4"]; [api.disabledAPIs addObject:@"FxCustomParameterActionAPI_v4"];
PGInspectorController *offline=[[PGInspectorController alloc]initWithAPIManager:api];[offline refresh]; PGInspectorController *offline=[[PGInspectorController alloc]initWithAPIManager:api];[offline refresh];
@@ -111,6 +132,10 @@ static void ViewInteractionTests(void) {
Check(Near([s valueForParameter:PGChin],-.75) && [s valueForParameter:PGBigEye]==0,@"actual chin slider events do not write the eye parameter"); Check(Near([s valueForParameter:PGChin],-.75) && [s valueForParameter:PGBigEye]==0,@"actual chin slider events do not write the eye parameter");
NSTextField *number=(NSTextField *)FindControl(view,NSTextField.class,@"下巴数值");number.stringValue=@"0.40";Fire(number);s=api.values[@(PGInspectorPanel)];Check(Near([s valueForParameter:PGChin],.4),@"editable numeric field writes its signed control"); NSTextField *number=(NSTextField *)FindControl(view,NSTextField.class,@"下巴数值");number.stringValue=@"0.40";Fire(number);s=api.values[@(PGInspectorPanel)];Check(Near([s valueForParameter:PGChin],.4),@"editable numeric field writes its signed control");
number.stringValue=@"invalid";Fire(number);s=api.values[@(PGInspectorPanel)];Check(Near([s valueForParameter:PGChin],.4),@"invalid number entry preserves the last valid value"); number.stringValue=@"invalid";Fire(number);s=api.values[@(PGInspectorPanel)];Check(Near([s valueForParameter:PGChin],.4),@"invalid number entry preserves the last valid value");
NSMutableDictionary *changed=[s.values mutableCopy];changed[@(PGChin)]=@.8;
api.values[@(PGInspectorPanel)]=[PGInspectorState stateWithValues:changed disabledParameters:s.disabledParameters hasEdits:YES];
number.stringValue=@"0.40";Fire(number);s=api.values[@(PGInspectorPanel)];
Check(Near([s valueForParameter:PGChin],.4),@"explicit input matching old UI cache still overrides a newer host value");
NSButton *preview=(NSButton *)FindControl(view,NSButton.class,@"人脸框预览");preview.state=NSControlStateValueOn;Fire(preview);s=api.values[@(PGInspectorPanel)];Check([s valueForParameter:PGTrackBoxes]==1,@"preview switch events change the automatic face box setting"); NSButton *preview=(NSButton *)FindControl(view,NSButton.class,@"人脸框预览");preview.state=NSControlStateValueOn;Fire(preview);s=api.values[@(PGInspectorPanel)];Check([s valueForParameter:PGTrackBoxes]==1,@"preview switch events change the automatic face box setting");
for(NSNumber *width in @[@280,@320,@380,@520]) { for(NSNumber *width in @[@280,@320,@380,@520]) {
[view setFrameSize:NSMakeSize(width.doubleValue,20)];[view layoutSubtreeIfNeeded]; [view setFrameSize:NSMakeSize(width.doubleValue,20)];[view layoutSubtreeIfNeeded];
@@ -118,8 +143,19 @@ static void ViewInteractionTests(void) {
for(NSView *card in view.subviews)if(!card.hidden)Check(NSMaxY(card.frame)<=NSHeight(view.bounds)+1 && NSMaxX(card.frame)<=NSWidth(view.bounds)+1,@"every visible card and footer remains inside the panel at all inspector widths"); for(NSView *card in view.subviews)if(!card.hidden)Check(NSMaxY(card.frame)<=NSHeight(view.bounds)+1 && NSMaxX(card.frame)<=NSWidth(view.bounds)+1,@"every visible card and footer remains inside the panel at all inspector widths");
} }
} }
int main(void) {@autoreleasepool { int main(int argc,const char **argv) {@autoreleasepool {
[NSApplication sharedApplication];[NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited]; [NSApplication sharedApplication];[NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited];
RegistrationTests();ModelTests();ControllerTests();ViewInteractionTests(); RegistrationTests();ModelTests();ControllerTests();ViewInteractionTests();
if(argc>2) {
NSData *inner=[NSData dataWithContentsOfFile:@(argv[1])],*outer=[NSData dataWithContentsOfFile:@(argv[2])];NSError *error=nil;
NSKeyedUnarchiver *reader=[[NSKeyedUnarchiver alloc]initForReadingFromData:inner error:&error];reader.requiresSecureCoding=YES;
PGInspectorState *state=[reader decodeObjectOfClass:PGInspectorState.class forKey:@"Custom Data"];[reader finishDecoding];
PGInspectorState *expected=[PGInspectorState stateWithValues:@{} disabledParameters:[NSSet set] hasEdits:NO];
Check(!error && !reader.error && [state isEqual:expected],@"the template's actual Custom Data bytes securely decode to our untouched state class");
NSKeyedUnarchiver *envelope=[[NSKeyedUnarchiver alloc]initForReadingFromData:outer error:&error];envelope.requiresSecureCoding=YES;
NSData *payload=[envelope decodeObjectOfClass:NSData.class forKey:@"BlindDataObject"];
NSNumber *legacy=[envelope decodeObjectOfClass:NSNumber.class forKey:@"DataIsLegacy"];[envelope finishDecoding];
Check(!error && !envelope.error && legacy && !legacy.boolValue && [payload isEqual:inner],@"Foundation reads the Motion envelope as the exact secure custom payload");
}
printf("Inspector: %lu checks, %lu failures\n",(unsigned long)Checks,(unsigned long)Failures);return Failures?1:0; printf("Inspector: %lu checks, %lu failures\n",(unsigned long)Checks,(unsigned long)Failures);return Failures?1:0;
}} }}