diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/cleanup.json b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/cleanup.json new file mode 100644 index 00000000..5ce5df8a --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/cleanup.json @@ -0,0 +1,25 @@ +{ + "borrowedSimulator": "B5F2BA0D-327C-4EE3-94EE-39710A0E1ACC", + "createdSimulators": [], + "ownedRoot": "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-touch.n2JrMa", + "apps": [ + { + "id": "invalid.sodalive.gesturefix.Gesture", + "terminateExit": 3, + "terminateMessage": "An error was encountered processing the command (domain=NSPOSIXErrorDomain, code=3):\nSimulator device failed to terminate invalid.sodalive.gesturefix.Gesture.\nfound nothing to terminate\nUnderlying error (domain=NSPOSIXErrorDomain, code=3):\n\tThe request to terminate \"invalid.sodalive.gesturefix.Gesture\" failed. found nothing to terminate\n\tfound nothing to terminate", + "uninstallExit": 0 + }, + { + "id": "invalid.sodalive.gesturefix.GestureUITests.xctrunner", + "terminateExit": 3, + "terminateMessage": "An error was encountered processing the command (domain=NSPOSIXErrorDomain, code=3):\nSimulator device failed to terminate invalid.sodalive.gesturefix.GestureUITests.xctrunner.\nfound nothing to terminate\nUnderlying error (domain=NSPOSIXErrorDomain, code=3):\n\tThe request to terminate \"invalid.sodalive.gesturefix.GestureUITests.xctrunner\" failed. found nothing to terminate\n\tfound nothing to terminate", + "uninstallExit": 0 + } + ], + "ownedLaunchctlJobsAbsent": true, + "ownedAppsAbsent": true, + "ownedRootAndCachesAbsent": true, + "borrowedSimulatorStillBooted": true, + "parentRootUntouchedAndPresent": true, + "productionSourceSHA256": "b3e4c5333cf8249ccd7773b5cabd372efb5a5565323bd7778034e110386151fd" +} \ No newline at end of file diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/frame-analysis.json b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/frame-analysis.json new file mode 100644 index 00000000..83940144 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/frame-analysis.json @@ -0,0 +1,8108 @@ +{ + "method": "Native CADisplayLink UIWindow.drawHierarchy PNGs; classify row y=240 across 375 pixels; RGB red >200/<100/<100, green <100/>150/<150, blue <100/<160/>200. Counts and x centroids below are observed pixels, not state.", + "sequences": { + "red/automatic": { + "capturedFrames": 117, + "mixedFrameCount": 0, + "selectedFrames": [ + "0000.png", + "0049.png", + "0050.png", + "0116.png" + ], + "samples": [ + { + "file": "0000.png", + "time": 913495.54207174, + "red": 0, + "green": 359 + }, + { + "file": "0001.png", + "time": 913495.5738208961, + "red": 0, + "green": 359 + }, + { + "file": "0002.png", + "time": 913495.6106792261, + "red": 0, + "green": 359 + }, + { + "time": 913495.641569743, + "file": "0003.png", + "red": 0, + "green": 359 + }, + { + "time": 913495.6749411471, + "file": "0004.png", + "red": 0, + "green": 359 + }, + { + "file": "0005.png", + "time": 913495.705216547, + "red": 0, + "green": 359 + }, + { + "time": 913495.7387740631, + "file": "0006.png", + "red": 0, + "green": 359 + }, + { + "file": "0007.png", + "time": 913495.7714096501, + "red": 0, + "green": 359 + }, + { + "time": 913495.805372421, + "file": "0008.png", + "red": 0, + "green": 359 + }, + { + "time": 913495.840020245, + "file": "0009.png", + "red": 0, + "green": 359 + }, + { + "file": "0010.png", + "time": 913495.87342155, + "red": 0, + "green": 359 + }, + { + "file": "0011.png", + "time": 913495.917590192, + "red": 0, + "green": 359 + }, + { + "time": 913495.9391362121, + "file": "0012.png", + "red": 0, + "green": 359 + }, + { + "file": "0013.png", + "time": 913495.9718883941, + "red": 0, + "green": 359 + }, + { + "time": 913496.0168752731, + "file": "0014.png", + "red": 0, + "green": 359 + }, + { + "file": "0015.png", + "time": 913496.0425488071, + "red": 0, + "green": 359 + }, + { + "file": "0016.png", + "time": 913496.0746769861, + "red": 0, + "green": 359 + }, + { + "file": "0017.png", + "time": 913496.1075135141, + "red": 0, + "green": 359 + }, + { + "file": "0018.png", + "time": 913496.141478101, + "red": 0, + "green": 359 + }, + { + "time": 913496.172093114, + "file": "0019.png", + "red": 0, + "green": 359 + }, + { + "file": "0020.png", + "time": 913496.208431857, + "red": 0, + "green": 359 + }, + { + "time": 913496.2405724331, + "file": "0021.png", + "red": 0, + "green": 359 + }, + { + "time": 913496.273345352, + "file": "0022.png", + "red": 0, + "green": 359 + }, + { + "time": 913496.30913038, + "file": "0023.png", + "red": 0, + "green": 359 + }, + { + "file": "0024.png", + "time": 913496.3400979791, + "red": 0, + "green": 359 + }, + { + "file": "0025.png", + "time": 913496.372814718, + "red": 0, + "green": 359 + }, + { + "file": "0026.png", + "time": 913496.455701945, + "red": 0, + "green": 359 + }, + { + "time": 913496.5070653721, + "file": "0027.png", + "red": 0, + "green": 359 + }, + { + "file": "0028.png", + "time": 913496.541253893, + "red": 0, + "green": 359 + }, + { + "file": "0029.png", + "time": 913496.5738092541, + "red": 0, + "green": 359 + }, + { + "file": "0030.png", + "time": 913496.6069761751, + "red": 0, + "green": 359 + }, + { + "file": "0031.png", + "time": 913496.643459904, + "red": 0, + "green": 359 + }, + { + "file": "0032.png", + "time": 913496.6747821551, + "red": 0, + "green": 359 + }, + { + "file": "0033.png", + "time": 913496.7070952271, + "red": 0, + "green": 359 + }, + { + "file": "0034.png", + "time": 913496.741924547, + "red": 0, + "green": 359 + }, + { + "file": "0035.png", + "time": 913496.773077407, + "red": 0, + "green": 359 + }, + { + "time": 913496.809248468, + "file": "0036.png", + "red": 0, + "green": 359 + }, + { + "file": "0037.png", + "time": 913496.8435245, + "red": 0, + "green": 359 + }, + { + "file": "0038.png", + "time": 913496.8727725011, + "red": 0, + "green": 359 + }, + { + "file": "0039.png", + "time": 913496.9077881251, + "red": 0, + "green": 359 + }, + { + "file": "0040.png", + "time": 913496.956194214, + "red": 0, + "green": 359 + }, + { + "file": "0041.png", + "time": 913496.9842686241, + "red": 0, + "green": 359 + }, + { + "time": 913497.0072370011, + "file": "0042.png", + "red": 0, + "green": 359 + }, + { + "time": 913497.041060513, + "file": "0043.png", + "red": 0, + "green": 359 + }, + { + "time": 913497.0743078231, + "file": "0044.png", + "red": 0, + "green": 359 + }, + { + "file": "0045.png", + "time": 913497.107356871, + "red": 0, + "green": 359 + }, + { + "file": "0046.png", + "time": 913497.1433479021, + "red": 0, + "green": 359 + }, + { + "time": 913497.174680085, + "file": "0047.png", + "red": 0, + "green": 359 + }, + { + "time": 913497.2073035331, + "file": "0048.png", + "red": 0, + "green": 359 + }, + { + "file": "0049.png", + "time": 913497.2407012051, + "red": 0, + "green": 359 + }, + { + "file": "0050.png", + "time": 913497.27261283, + "red": 359, + "green": 0 + }, + { + "file": "0051.png", + "time": 913497.308410936, + "red": 359, + "green": 0 + }, + { + "time": 913497.360059548, + "file": "0052.png", + "red": 359, + "green": 0 + }, + { + "file": "0053.png", + "time": 913497.376581441, + "red": 359, + "green": 0 + }, + { + "file": "0054.png", + "time": 913497.406162766, + "red": 359, + "green": 0 + }, + { + "file": "0055.png", + "time": 913497.443720794, + "red": 359, + "green": 0 + }, + { + "time": 913497.4710581651, + "file": "0056.png", + "red": 359, + "green": 0 + }, + { + "file": "0057.png", + "time": 913497.50395031, + "red": 359, + "green": 0 + }, + { + "time": 913497.5408317171, + "file": "0058.png", + "red": 359, + "green": 0 + }, + { + "file": "0059.png", + "time": 913497.573211696, + "red": 359, + "green": 0 + }, + { + "file": "0060.png", + "time": 913497.608653726, + "red": 359, + "green": 0 + }, + { + "time": 913497.6409471381, + "file": "0061.png", + "red": 359, + "green": 0 + }, + { + "time": 913497.6729607941, + "file": "0062.png", + "red": 359, + "green": 0 + }, + { + "file": "0063.png", + "time": 913497.70777579, + "red": 359, + "green": 0 + }, + { + "time": 913497.74030354, + "file": "0064.png", + "red": 359, + "green": 0 + }, + { + "file": "0065.png", + "time": 913497.7736736, + "red": 359, + "green": 0 + }, + { + "file": "0066.png", + "time": 913497.808164278, + "red": 359, + "green": 0 + }, + { + "file": "0067.png", + "time": 913497.8529161201, + "red": 359, + "green": 0 + }, + { + "time": 913497.8853847651, + "file": "0068.png", + "red": 359, + "green": 0 + }, + { + "time": 913497.9067391901, + "file": "0069.png", + "red": 359, + "green": 0 + }, + { + "time": 913497.955912176, + "file": "0070.png", + "red": 359, + "green": 0 + }, + { + "time": 913497.985780111, + "file": "0071.png", + "red": 359, + "green": 0 + }, + { + "file": "0072.png", + "time": 913498.005127888, + "red": 359, + "green": 0 + }, + { + "file": "0073.png", + "time": 913498.05205323, + "red": 359, + "green": 0 + }, + { + "file": "0074.png", + "time": 913498.088945384, + "red": 359, + "green": 0 + }, + { + "time": 913498.1151565431, + "file": "0075.png", + "red": 359, + "green": 0 + }, + { + "file": "0076.png", + "time": 913498.151844567, + "red": 359, + "green": 0 + }, + { + "file": "0077.png", + "time": 913498.1709583921, + "red": 359, + "green": 0 + }, + { + "file": "0078.png", + "time": 913498.219037116, + "red": 359, + "green": 0 + }, + { + "file": "0079.png", + "time": 913498.256338482, + "red": 359, + "green": 0 + }, + { + "time": 913498.281236578, + "file": "0080.png", + "red": 359, + "green": 0 + }, + { + "time": 913498.316294342, + "file": "0081.png", + "red": 359, + "green": 0 + }, + { + "file": "0082.png", + "time": 913498.339072, + "red": 359, + "green": 0 + }, + { + "file": "0083.png", + "time": 913498.385321374, + "red": 359, + "green": 0 + }, + { + "time": 913498.418765353, + "file": "0084.png", + "red": 359, + "green": 0 + }, + { + "time": 913498.4510205501, + "file": "0085.png", + "red": 359, + "green": 0 + }, + { + "time": 913498.4864981121, + "file": "0086.png", + "red": 359, + "green": 0 + }, + { + "file": "0087.png", + "time": 913498.5193576021, + "red": 359, + "green": 0 + }, + { + "time": 913498.5513762251, + "file": "0088.png", + "red": 359, + "green": 0 + }, + { + "time": 913498.58565475, + "file": "0089.png", + "red": 359, + "green": 0 + }, + { + "time": 913498.6150477291, + "file": "0090.png", + "red": 359, + "green": 0 + }, + { + "file": "0091.png", + "time": 913498.6526002641, + "red": 359, + "green": 0 + }, + { + "file": "0092.png", + "time": 913498.68563959, + "red": 359, + "green": 0 + }, + { + "time": 913498.717341399, + "file": "0093.png", + "red": 359, + "green": 0 + }, + { + "time": 913498.7526415391, + "file": "0094.png", + "red": 359, + "green": 0 + }, + { + "file": "0095.png", + "time": 913498.786063538, + "red": 359, + "green": 0 + }, + { + "time": 913498.815427456, + "file": "0096.png", + "red": 359, + "green": 0 + }, + { + "file": "0097.png", + "time": 913498.852643884, + "red": 359, + "green": 0 + }, + { + "file": "0098.png", + "time": 913498.8822384671, + "red": 359, + "green": 0 + }, + { + "time": 913498.9185269681, + "file": "0099.png", + "red": 359, + "green": 0 + }, + { + "file": "0100.png", + "time": 913498.938407565, + "red": 359, + "green": 0 + }, + { + "time": 913498.9707252091, + "file": "0101.png", + "red": 359, + "green": 0 + }, + { + "time": 913499.0093057121, + "file": "0102.png", + "red": 359, + "green": 0 + }, + { + "time": 913499.0412945631, + "file": "0103.png", + "red": 359, + "green": 0 + }, + { + "file": "0104.png", + "time": 913499.072403072, + "red": 359, + "green": 0 + }, + { + "time": 913499.1076014581, + "file": "0105.png", + "red": 359, + "green": 0 + }, + { + "file": "0106.png", + "time": 913499.143486515, + "red": 359, + "green": 0 + }, + { + "file": "0107.png", + "time": 913499.173134434, + "red": 359, + "green": 0 + }, + { + "file": "0108.png", + "time": 913499.2078582661, + "red": 359, + "green": 0 + }, + { + "time": 913499.2408274111, + "file": "0109.png", + "red": 359, + "green": 0 + }, + { + "time": 913499.274462525, + "file": "0110.png", + "red": 359, + "green": 0 + }, + { + "file": "0111.png", + "time": 913499.3080595911, + "red": 359, + "green": 0 + }, + { + "time": 913499.3425685731, + "file": "0112.png", + "red": 359, + "green": 0 + }, + { + "file": "0113.png", + "time": 913499.371937836, + "red": 359, + "green": 0 + }, + { + "time": 913499.4152267361, + "file": "0114.png", + "red": 359, + "green": 0 + }, + { + "time": 913499.449771592, + "file": "0115.png", + "red": 359, + "green": 0 + }, + { + "time": 913499.482066196, + "file": "0116.png", + "red": 359, + "green": 0 + } + ], + "snapSeconds": 0.031911624944768846 + }, + "green/automatic": { + "capturedFrames": 116, + "mixedFrameCount": 10, + "selectedFrames": [ + "0000.png", + "0058.png", + "0063.png", + "0067.png", + "0070.png", + "0115.png" + ], + "samples": [ + { + "time": 913597.4492290791, + "file": "0000.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0001.png", + "time": 913597.4755964761, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0002.png", + "time": 913597.50850726, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913597.5433671961, + "file": "0003.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913597.5739051071, + "file": "0004.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0005.png", + "time": 913597.609845889, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913597.641820132, + "file": "0006.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0007.png", + "time": 913597.676881199, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0008.png", + "time": 913597.7084638361, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0009.png", + "time": 913597.7411874051, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0010.png", + "time": 913597.775570434, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913597.8080575881, + "file": "0011.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0012.png", + "time": 913597.84204845, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0013.png", + "time": 913597.8778581431, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0014.png", + "time": 913597.9616468931, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913598.004829079, + "file": "0015.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0016.png", + "time": 913598.0422670391, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0017.png", + "time": 913598.0752597761, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0018.png", + "time": 913598.1080234471, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913598.141075, + "file": "0019.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0020.png", + "time": 913598.175370774, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0021.png", + "time": 913598.2073697711, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913598.2420130861, + "file": "0022.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0023.png", + "time": 913598.2760424891, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913598.306953603, + "file": "0024.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913598.3411505871, + "file": "0025.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913598.374023861, + "file": "0026.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913598.407951768, + "file": "0027.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0028.png", + "time": 913598.4401897051, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0029.png", + "time": 913598.4735934021, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913598.5078664881, + "file": "0030.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0031.png", + "time": 913598.5434376, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0032.png", + "time": 913598.5772900771, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0033.png", + "time": 913598.607618751, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0034.png", + "time": 913598.641246484, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913598.6736933991, + "file": "0035.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0036.png", + "time": 913598.7074893891, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0037.png", + "time": 913598.743114041, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0038.png", + "time": 913598.7739387951, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0039.png", + "time": 913598.808327698, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913598.841209955, + "file": "0040.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0041.png", + "time": 913598.875832265, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0042.png", + "time": 913598.908611273, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0043.png", + "time": 913598.9920293391, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913599.0429295121, + "file": "0044.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0045.png", + "time": 913599.073915453, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913599.1069630211, + "file": "0046.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0047.png", + "time": 913599.139273638, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0048.png", + "time": 913599.171037629, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0049.png", + "time": 913599.2040882771, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913599.2386047101, + "file": "0050.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0051.png", + "time": 913599.2723036291, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913599.3079158181, + "file": "0052.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0053.png", + "time": 913599.3431351511, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913599.376199943, + "file": "0054.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0055.png", + "time": 913599.4078560281, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913599.440783828, + "file": "0056.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913599.4710975741, + "file": "0057.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "75681ba7c2ee52579521194b826e9f80d1f71afbc36ceea5d0d902d2fde49b34" + }, + { + "file": "0058.png", + "time": 913599.5026518331, + "counts": { + "red": 21, + "green": 336, + "blue": 0 + }, + "centroids": { + "red": 360.2, + "green": 167.8, + "blue": null + }, + "bannerHash": "513692c21a9ef4c6d1098e7d4ec596940d4f33dc775452b7a1342daa9fcfc7d7" + }, + { + "time": 913599.5386346581, + "file": "0059.png", + "counts": { + "red": 76, + "green": 282, + "blue": 0 + }, + "centroids": { + "red": 335.4, + "green": 140.8, + "blue": null + }, + "bannerHash": "402caaefe1790c5b0a6b44f0d42a73520612feaf40c548b23ac7f86f506e54c6" + }, + { + "time": 913599.5707385921, + "file": "0060.png", + "counts": { + "red": 127, + "green": 231, + "blue": 0 + }, + "centroids": { + "red": 310.4, + "green": 115.4, + "blue": null + }, + "bannerHash": "2c46b3265cf7bb6177a2443f36caca43c1a074e69a7b7fe2c6903d504020f9df" + }, + { + "file": "0061.png", + "time": 913599.602594416, + "counts": { + "red": 175, + "green": 183, + "blue": 0 + }, + "centroids": { + "red": 286.5, + "green": 91.4, + "blue": null + }, + "bannerHash": "d10f63f680271dfcaa26609ff287d0d848d6a6e47ef9ec5bf8bc6b6dc94a2ee1" + }, + { + "time": 913599.6393803691, + "file": "0062.png", + "counts": { + "red": 218, + "green": 140, + "blue": 0 + }, + "centroids": { + "red": 265.1, + "green": 70.1, + "blue": null + }, + "bannerHash": "d69397d2187ed01083f1dd5315790b774faef33960fc9f843a5f822308287972" + }, + { + "file": "0063.png", + "time": 913599.6682303511, + "counts": { + "red": 258, + "green": 101, + "blue": 0 + }, + "centroids": { + "red": 245.2, + "green": 50.9, + "blue": null + }, + "bannerHash": "47557b2644a58ab3c767a581af618d1a16b5edd188b3158a573e8d042d354508" + }, + { + "file": "0064.png", + "time": 913599.7060550201, + "counts": { + "red": 293, + "green": 66, + "blue": 0 + }, + "centroids": { + "red": 227.7, + "green": 33.8, + "blue": null + }, + "bannerHash": "6ed2bc6418e9f90cb62e20064a9f55a16910779144e7b81148a31cf1f45ae03a" + }, + { + "time": 913599.736194105, + "file": "0065.png", + "counts": { + "red": 322, + "green": 36, + "blue": 0 + }, + "centroids": { + "red": 213.2, + "green": 19.7, + "blue": null + }, + "bannerHash": "f5108b371c490fa8dbb954a9317df7453db26a0cded2ff7f4fa9b6f9e15fc878" + }, + { + "file": "0066.png", + "time": 913599.7714051381, + "counts": { + "red": 344, + "green": 12, + "blue": 0 + }, + "centroids": { + "red": 202.2, + "green": 13.0, + "blue": null + }, + "bannerHash": "2103cffe0cf3e86182655d600bb6edf6ca9abe9da95dfbc1f9038c846209eaa5" + }, + { + "time": 913599.802721531, + "file": "0067.png", + "counts": { + "red": 352, + "green": 6, + "blue": 0 + }, + "centroids": { + "red": 189.4, + "green": 2.5, + "blue": null + }, + "bannerHash": "aceb544fb8df436b51d37d2b1c9a61c4cfffbc2d165953ec9b8b98019a246625" + }, + { + "time": 913599.873930242, + "file": "0068.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "time": 913599.893517905, + "file": "0069.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0070.png", + "time": 913599.913150748, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0071.png", + "time": 913599.942140343, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "time": 913599.97637692, + "file": "0072.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "time": 913600.0079948221, + "file": "0073.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0074.png", + "time": 913600.0425524261, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0075.png", + "time": 913600.0739939511, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "time": 913600.109444809, + "file": "0076.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "time": 913600.14165221, + "file": "0077.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "time": 913600.1751490401, + "file": "0078.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "time": 913600.207547056, + "file": "0079.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "time": 913600.241096996, + "file": "0080.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0081.png", + "time": 913600.2762579471, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0082.png", + "time": 913600.306952715, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "time": 913600.3412508611, + "file": "0083.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "time": 913600.3752544611, + "file": "0084.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0085.png", + "time": 913600.4240071401, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "time": 913600.4467707721, + "file": "0086.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "time": 913600.4733675481, + "file": "0087.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "time": 913600.507895805, + "file": "0088.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0089.png", + "time": 913600.5426770151, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0090.png", + "time": 913600.573082122, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0091.png", + "time": 913600.6076171291, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0092.png", + "time": 913600.6396793531, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0093.png", + "time": 913600.675508002, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0094.png", + "time": 913600.7075339471, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "time": 913600.74025594, + "file": "0095.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "time": 913600.775323804, + "file": "0096.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0097.png", + "time": 913600.8072155941, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "time": 913600.8411561421, + "file": "0098.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0099.png", + "time": 913600.8750394251, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0100.png", + "time": 913600.90762452, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0101.png", + "time": 913600.940233796, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "time": 913600.9744563231, + "file": "0102.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "time": 913601.00685121, + "file": "0103.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0104.png", + "time": 913601.040866059, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "time": 913601.074510779, + "file": "0105.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0106.png", + "time": 913601.1079004781, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0107.png", + "time": 913601.1438779951, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0108.png", + "time": 913601.1751519741, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "time": 913601.2079005501, + "file": "0109.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0110.png", + "time": 913601.24195652, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "time": 913601.277139337, + "file": "0111.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "time": 913601.307590552, + "file": "0112.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0113.png", + "time": 913601.3412412221, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "time": 913601.3733143321, + "file": "0114.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0115.png", + "time": 913601.4083740071, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + } + ] + }, + "green/manual": { + "capturedFrames": 67, + "mixedFrameCount": 21, + "selectedFrames": [ + "0000.png", + "0019.png", + "0029.png", + "0039.png", + "0042.png", + "0066.png" + ], + "samples": [ + { + "file": "0000.png", + "time": 913649.4789123321, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "file": "0001.png", + "time": 913649.5409981351, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "file": "0002.png", + "time": 913649.5719159461, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "time": 913649.604001975, + "file": "0003.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "file": "0004.png", + "time": 913649.637579345, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "file": "0005.png", + "time": 913649.6715946691, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "time": 913649.7373082781, + "file": "0006.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "file": "0007.png", + "time": 913649.773161266, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "file": "0008.png", + "time": 913649.837708718, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "file": "0009.png", + "time": 913649.897244942, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "file": "0010.png", + "time": 913649.916900538, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "file": "0011.png", + "time": 913650.0185351061, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "file": "0012.png", + "time": 913650.040369812, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "file": "0013.png", + "time": 913650.0959979381, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "file": "0014.png", + "time": 913650.115852643, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "time": 913650.1387285291, + "file": "0015.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "time": 913650.1890770551, + "file": "0016.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "time": 913650.209703319, + "file": "0017.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "file": "0018.png", + "time": 913650.257690393, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "file": "0019.png", + "time": 913650.2955483911, + "counts": { + "red": 347, + "green": 0, + "blue": 11 + }, + "centroids": { + "red": 194.8, + "green": null, + "blue": 7.0 + }, + "bannerHash": "b758a6e3193f2568bc24adf79b9ad0a463883fd59463d004551cb8aa4386fed9" + }, + { + "file": "0020.png", + "time": 913650.321425457, + "counts": { + "red": 337, + "green": 0, + "blue": 21 + }, + "centroids": { + "red": 205.8, + "green": null, + "blue": 14.2 + }, + "bannerHash": "281a051714a1f6ba10f5ab76e0fe2d0843c5414b1e4ebdd2f8a7b19ef19020a7" + }, + { + "file": "0021.png", + "time": 913650.352865361, + "counts": { + "red": 312, + "green": 0, + "blue": 47 + }, + "centroids": { + "red": 218.2, + "green": null, + "blue": 24.9 + }, + "bannerHash": "fd3ee89b264b9f016f516cc05adbf68f5b2031b70ac5cdcd3478c52e02af3430" + }, + { + "time": 913650.3855440071, + "file": "0022.png", + "counts": { + "red": 293, + "green": 0, + "blue": 65 + }, + "centroids": { + "red": 227.7, + "green": null, + "blue": 33.2 + }, + "bannerHash": "27255d3c9e2909bf6115c2df8204f0c016f017653db048e53bd7827563a87e9b" + }, + { + "file": "0023.png", + "time": 913650.422998685, + "counts": { + "red": 274, + "green": 0, + "blue": 84 + }, + "centroids": { + "red": 237.2, + "green": null, + "blue": 42.5 + }, + "bannerHash": "925c24c649710f841d2ffc90318fd009d7258bbe7f96d047f2352067960bef64" + }, + { + "time": 913650.4526552961, + "file": "0024.png", + "counts": { + "red": 254, + "green": 0, + "blue": 103 + }, + "centroids": { + "red": 247.1, + "green": null, + "blue": 51.8 + }, + "bannerHash": "4b73ccc7b5dfb51f36618ab54cf1afae1c0a4d8646974446a3726258f6fa2995" + }, + { + "file": "0025.png", + "time": 913650.485515214, + "counts": { + "red": 235, + "green": 0, + "blue": 122 + }, + "centroids": { + "red": 256.6, + "green": null, + "blue": 61.2 + }, + "bannerHash": "74898e43fa333f7b1e0860eae7ef5f2d807fe20810ecc2206dfb5e0fe48cea5f" + }, + { + "time": 913650.5055894121, + "file": "0026.png", + "counts": { + "red": 235, + "green": 0, + "blue": 122 + }, + "centroids": { + "red": 256.6, + "green": null, + "blue": 61.2 + }, + "bannerHash": "74898e43fa333f7b1e0860eae7ef5f2d807fe20810ecc2206dfb5e0fe48cea5f" + }, + { + "file": "0027.png", + "time": 913650.5543494751, + "counts": { + "red": 198, + "green": 0, + "blue": 159 + }, + "centroids": { + "red": 275.0, + "green": null, + "blue": 79.5 + }, + "bannerHash": "10c73e64d50b1799d693481ce33c858a72f177fc1315c7ec06fbf66d39348b0a" + }, + { + "time": 913650.5751487471, + "file": "0028.png", + "counts": { + "red": 198, + "green": 0, + "blue": 159 + }, + "centroids": { + "red": 275.0, + "green": null, + "blue": 79.5 + }, + "bannerHash": "10c73e64d50b1799d693481ce33c858a72f177fc1315c7ec06fbf66d39348b0a" + }, + { + "file": "0029.png", + "time": 913650.620362143, + "counts": { + "red": 166, + "green": 0, + "blue": 193 + }, + "centroids": { + "red": 291.0, + "green": null, + "blue": 96.5 + }, + "bannerHash": "9e0d48997ebcfb94531db2e3c04d065586746b73219ada63eaa5c94d829a6000" + }, + { + "file": "0030.png", + "time": 913650.640958155, + "counts": { + "red": 166, + "green": 0, + "blue": 193 + }, + "centroids": { + "red": 291.0, + "green": null, + "blue": 96.5 + }, + "bannerHash": "9e0d48997ebcfb94531db2e3c04d065586746b73219ada63eaa5c94d829a6000" + }, + { + "file": "0031.png", + "time": 913650.6845618271, + "counts": { + "red": 142, + "green": 0, + "blue": 217 + }, + "centroids": { + "red": 302.9, + "green": null, + "blue": 108.4 + }, + "bannerHash": "4d0cc041928fc3f9b3982d6a4b7a84fa13dd9b4bf70cbc8734b54778090d9afb" + }, + { + "time": 913650.7045911091, + "file": "0032.png", + "counts": { + "red": 142, + "green": 0, + "blue": 217 + }, + "centroids": { + "red": 302.9, + "green": null, + "blue": 108.4 + }, + "bannerHash": "4d0cc041928fc3f9b3982d6a4b7a84fa13dd9b4bf70cbc8734b54778090d9afb" + }, + { + "file": "0033.png", + "time": 913650.734892515, + "counts": { + "red": 109, + "green": 0, + "blue": 250 + }, + "centroids": { + "red": 319.2, + "green": null, + "blue": 124.9 + }, + "bannerHash": "5d39ffde26f9b4d0e93cbbd6f88afa59cc239b0fa2c4391badd25e6c71ab1b21" + }, + { + "time": 913650.774539203, + "file": "0034.png", + "counts": { + "red": 88, + "green": 0, + "blue": 270 + }, + "centroids": { + "red": 329.6, + "green": null, + "blue": 134.8 + }, + "bannerHash": "e84875667f548cf70099012da11bded41fdd7ad0233e8bc39d323a3a856f4dfc" + }, + { + "file": "0035.png", + "time": 913650.8017526851, + "counts": { + "red": 69, + "green": 0, + "blue": 289 + }, + "centroids": { + "red": 338.8, + "green": null, + "blue": 144.3 + }, + "bannerHash": "8f573567dae3d780f8c8905ea8da89f7dc531638224cf8f7530f07bb9698488e" + }, + { + "file": "0036.png", + "time": 913650.8394169301, + "counts": { + "red": 52, + "green": 0, + "blue": 307 + }, + "centroids": { + "red": 346.8, + "green": null, + "blue": 153.3 + }, + "bannerHash": "af2e2f178964c70bcdf41db6210fb05793a3bd92b0412e766e4a2a7baeb48d3f" + }, + { + "file": "0037.png", + "time": 913650.8681549091, + "counts": { + "red": 36, + "green": 0, + "blue": 322 + }, + "centroids": { + "red": 354.1, + "green": null, + "blue": 160.7 + }, + "bannerHash": "d33ba4c6b7d6f5e8a0c2c7d8b039891438e7595de62e23ec88b1d3e1d79e6b24" + }, + { + "file": "0038.png", + "time": 913650.926757905, + "counts": { + "red": 22, + "green": 0, + "blue": 336 + }, + "centroids": { + "red": 359.5, + "green": null, + "blue": 167.7 + }, + "bannerHash": "6855a96ee56caf1d1d5cd3cf743727fe27072d841e775d279e436019f31434d4" + }, + { + "time": 913650.972167773, + "file": "0039.png", + "counts": { + "red": 10, + "green": 0, + "blue": 347 + }, + "centroids": { + "red": 363.5, + "green": null, + "blue": 176.2 + }, + "bannerHash": "b3169ae359a5cab9bc16fe752ff3ddcd26cd8d9a5ce9d9335c0fd4d41a426bfa" + }, + { + "time": 913651.03541228, + "file": "0040.png", + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "ab9766dc64b2a9efcb8594ebe099f8b205d2e3c2d59ade7bbf91f613fdffbcbf" + }, + { + "time": 913651.109038961, + "file": "0041.png", + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "time": 913651.1392424961, + "file": "0042.png", + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "time": 913651.175997302, + "file": "0043.png", + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "file": "0044.png", + "time": 913651.207577649, + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "time": 913651.2412511851, + "file": "0045.png", + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "file": "0046.png", + "time": 913651.272701774, + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "file": "0047.png", + "time": 913651.307512223, + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "file": "0048.png", + "time": 913651.343124047, + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "time": 913651.3754428921, + "file": "0049.png", + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "time": 913651.4083072931, + "file": "0050.png", + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "file": "0051.png", + "time": 913651.441592662, + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "file": "0052.png", + "time": 913651.4762968901, + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "file": "0053.png", + "time": 913651.506200829, + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "time": 913651.5413629961, + "file": "0054.png", + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "file": "0055.png", + "time": 913651.5741396641, + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "time": 913651.607966713, + "file": "0056.png", + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "file": "0057.png", + "time": 913651.640467989, + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "file": "0058.png", + "time": 913651.675139843, + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "time": 913651.707729437, + "file": "0059.png", + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "time": 913651.740942755, + "file": "0060.png", + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "time": 913651.7733378131, + "file": "0061.png", + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "time": 913651.805534414, + "file": "0062.png", + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "file": "0063.png", + "time": 913651.8411757961, + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "file": "0064.png", + "time": 913651.876669439, + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "time": 913651.9073031051, + "file": "0065.png", + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "time": 913651.941832795, + "file": "0066.png", + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + } + ] + }, + "green/forward": { + "capturedFrames": 70, + "mixedFrameCount": 22, + "selectedFrames": [ + "0000.png", + "0021.png", + "0032.png", + "0042.png", + "0045.png", + "0069.png" + ], + "samples": [ + { + "time": 913652.291601978, + "file": "0000.png", + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "time": 913652.311797108, + "file": "0001.png", + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "time": 913652.3391558371, + "file": "0002.png", + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "time": 913652.376711928, + "file": "0003.png", + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "file": "0004.png", + "time": 913652.4078878481, + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "file": "0005.png", + "time": 913652.4395461461, + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "file": "0006.png", + "time": 913652.4726654671, + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "time": 913652.538570735, + "file": "0007.png", + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "time": 913652.586738405, + "file": "0008.png", + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "file": "0009.png", + "time": 913652.60548328, + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "file": "0010.png", + "time": 913652.663007152, + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "file": "0011.png", + "time": 913652.682149627, + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "file": "0012.png", + "time": 913652.7373864851, + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "file": "0013.png", + "time": 913652.805418989, + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "time": 913652.904987868, + "file": "0014.png", + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "time": 913652.9665794891, + "file": "0015.png", + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "file": "0016.png", + "time": 913652.987309437, + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "time": 913653.0076745551, + "file": "0017.png", + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "file": "0018.png", + "time": 913653.039906566, + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "time": 913653.0749919331, + "file": "0019.png", + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "time": 913653.109595312, + "file": "0020.png", + "counts": { + "red": 0, + "green": 0, + "blue": 359 + }, + "centroids": { + "red": null, + "green": null, + "blue": 187.0 + }, + "bannerHash": "bdca9583c32452a3b737327908fcd436e864c7b6796c4f449f5d3f15a4fbc5ec" + }, + { + "file": "0021.png", + "time": 913653.138689461, + "counts": { + "red": 11, + "green": 0, + "blue": 348 + }, + "centroids": { + "red": 367.0, + "green": null, + "blue": 179.8 + }, + "bannerHash": "a83a02bb07f383c5e25ebc212735a99a851d5c0ed165f67a90367d48b064a22b" + }, + { + "file": "0022.png", + "time": 913653.1710794021, + "counts": { + "red": 21, + "green": 0, + "blue": 338 + }, + "centroids": { + "red": 359.8, + "green": null, + "blue": 168.8 + }, + "bannerHash": "2324ecad25ae08ec19bd5b5d841c4afb9b7c6a1487a48128b900510577d7ba2f" + }, + { + "file": "0023.png", + "time": 913653.2016720751, + "counts": { + "red": 38, + "green": 0, + "blue": 321 + }, + "centroids": { + "red": 353.2, + "green": null, + "blue": 160.3 + }, + "bannerHash": "90cd3ec61f8c2d98eef13d9bfd64c02d3d31de8db427ec48e08354b78314dd99" + }, + { + "file": "0024.png", + "time": 913653.2382149621, + "counts": { + "red": 56, + "green": 0, + "blue": 302 + }, + "centroids": { + "red": 345.1, + "green": null, + "blue": 150.8 + }, + "bannerHash": "d4aeef0b630e930487055910a7d8c73e742664ac0f56f958f28fdbdbf4f617db" + }, + { + "time": 913653.268156755, + "file": "0025.png", + "counts": { + "red": 75, + "green": 0, + "blue": 284 + }, + "centroids": { + "red": 335.8, + "green": null, + "blue": 141.8 + }, + "bannerHash": "0fa7d1f22115796e6e31659b06dac2dbc024dbb19e2ee0bb7f1297f9c4e5eeca" + }, + { + "file": "0026.png", + "time": 913653.3231254701, + "counts": { + "red": 103, + "green": 0, + "blue": 255 + }, + "centroids": { + "red": 322.2, + "green": null, + "blue": 127.3 + }, + "bannerHash": "5504e6d1c62f8fc023093d414bd070f0d1ee008afd3d03155d38316a8d87a9da" + }, + { + "file": "0027.png", + "time": 913653.3458972421, + "counts": { + "red": 113, + "green": 0, + "blue": 246 + }, + "centroids": { + "red": 317.2, + "green": null, + "blue": 122.9 + }, + "bannerHash": "437ad7e27b20eda2d23d9d021a0f49d3148865c0e80be50e40675a6aef479ead" + }, + { + "time": 913653.3699691831, + "file": "0028.png", + "counts": { + "red": 122, + "green": 0, + "blue": 236 + }, + "centroids": { + "red": 312.8, + "green": null, + "blue": 117.9 + }, + "bannerHash": "e9cf0887dbd1231d47fda4ec4e57936a2a29341ed210ecb1cc040d2fe7e5c080" + }, + { + "time": 913653.4018004481, + "file": "0029.png", + "counts": { + "red": 149, + "green": 0, + "blue": 208 + }, + "centroids": { + "red": 299.4, + "green": null, + "blue": 103.9 + }, + "bannerHash": "0ca2c317e2fca4d75fe3399cfb8c9a110ed8e0ae1aeb2f256db26f431c3d1b7c" + }, + { + "file": "0030.png", + "time": 913653.4518237461, + "counts": { + "red": 177, + "green": 0, + "blue": 182 + }, + "centroids": { + "red": 285.5, + "green": null, + "blue": 91.0 + }, + "bannerHash": "2a8fafc5e6301a671387e89c6afc6e1aeb9573c8d3bd9e046055c26fb20931b6" + }, + { + "time": 913653.472333822, + "file": "0031.png", + "counts": { + "red": 177, + "green": 0, + "blue": 182 + }, + "centroids": { + "red": 285.5, + "green": null, + "blue": 91.0 + }, + "bannerHash": "2a8fafc5e6301a671387e89c6afc6e1aeb9573c8d3bd9e046055c26fb20931b6" + }, + { + "file": "0032.png", + "time": 913653.520925558, + "counts": { + "red": 205, + "green": 0, + "blue": 152 + }, + "centroids": { + "red": 271.6, + "green": null, + "blue": 76.0 + }, + "bannerHash": "ce9cd23602341104a378b1d28ee93568114c5fa440aee49289765c980d27d518" + }, + { + "file": "0033.png", + "time": 913653.5443326001, + "counts": { + "red": 211, + "green": 0, + "blue": 146 + }, + "centroids": { + "red": 268.6, + "green": null, + "blue": 73.0 + }, + "bannerHash": "564d43a9acb07a5e8fc923939f9274901468419bd213fedcbd6a7034b437cd20" + }, + { + "file": "0034.png", + "time": 913653.5703751601, + "counts": { + "red": 217, + "green": 0, + "blue": 142 + }, + "centroids": { + "red": 265.6, + "green": null, + "blue": 71.1 + }, + "bannerHash": "d2305e1e2bff681c110e265a5e94f61a0d0cc127dc9e77814b88f202eba85383" + }, + { + "file": "0035.png", + "time": 913653.6053882821, + "counts": { + "red": 228, + "green": 0, + "blue": 130 + }, + "centroids": { + "red": 260.1, + "green": null, + "blue": 65.2 + }, + "bannerHash": "e32bd02e3d9742e8726a897955ad35320a05483b10f5e3ca2e13c4ba4c9b3e30" + }, + { + "file": "0036.png", + "time": 913653.6348647911, + "counts": { + "red": 250, + "green": 0, + "blue": 108 + }, + "centroids": { + "red": 249.2, + "green": null, + "blue": 54.3 + }, + "bannerHash": "59a03e98da5a204a2ef7f03dede35a4d053f48da19490cb5eff0d01ac88db6cb" + }, + { + "file": "0037.png", + "time": 913653.6737473761, + "counts": { + "red": 271, + "green": 0, + "blue": 87 + }, + "centroids": { + "red": 238.7, + "green": null, + "blue": 43.9 + }, + "bannerHash": "96cbccc4b7624a21af20936f7dd1135bc6bf7eb31a3c012f1145fa87f529f3e1" + }, + { + "file": "0038.png", + "time": 913653.701857235, + "counts": { + "red": 290, + "green": 0, + "blue": 68 + }, + "centroids": { + "red": 229.2, + "green": null, + "blue": 34.7 + }, + "bannerHash": "5eb8f74756591568c934d9f00f546ec32132b334e61460e15ddc98c5cece4a77" + }, + { + "file": "0039.png", + "time": 913653.7411189, + "counts": { + "red": 307, + "green": 0, + "blue": 51 + }, + "centroids": { + "red": 220.7, + "green": null, + "blue": 26.7 + }, + "bannerHash": "3b5397bc2f85d5a561e621643a310e71ff3ce01dfe74190925e35925caf3f242" + }, + { + "file": "0040.png", + "time": 913653.798327717, + "counts": { + "red": 323, + "green": 0, + "blue": 35 + }, + "centroids": { + "red": 212.7, + "green": null, + "blue": 19.3 + }, + "bannerHash": "aed758de8a67307a80616263ce5b3e26fd57c66c63ede60175338577fc8bbc0a" + }, + { + "file": "0041.png", + "time": 913653.8416348731, + "counts": { + "red": 341, + "green": 0, + "blue": 17 + }, + "centroids": { + "red": 203.7, + "green": null, + "blue": 12.7 + }, + "bannerHash": "1738a1194e1b61b9807a8f9b77e305bcaffaf2e4d91bc1b57e279d642ce13006" + }, + { + "time": 913653.8685095541, + "file": "0042.png", + "counts": { + "red": 347, + "green": 0, + "blue": 10 + }, + "centroids": { + "red": 192.7, + "green": null, + "blue": 5.5 + }, + "bannerHash": "0cf0e4ea6835f155623db887ed6643629bc77916d63793b380a4efe3c5ea0d4b" + }, + { + "file": "0043.png", + "time": 913653.904678173, + "counts": { + "red": 355, + "green": 0, + "blue": 2 + }, + "centroids": { + "red": 188.9, + "green": null, + "blue": 0.5 + }, + "bannerHash": "45f7602ffe675ea5d685317eba416f37d7df081c05181ec7a32cd4ff94ba2b36" + }, + { + "time": 913653.9346417851, + "file": "0044.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "time": 913653.9724434691, + "file": "0045.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "file": "0046.png", + "time": 913654.0039547511, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "file": "0047.png", + "time": 913654.03900551, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "time": 913654.072691896, + "file": "0048.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "file": "0049.png", + "time": 913654.1242488661, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "file": "0050.png", + "time": 913654.145452633, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "time": 913654.172808056, + "file": "0051.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "file": "0052.png", + "time": 913654.2075264001, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "time": 913654.242221885, + "file": "0053.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "time": 913654.276169833, + "file": "0054.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "time": 913654.306327088, + "file": "0055.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "file": "0056.png", + "time": 913654.341744952, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "file": "0057.png", + "time": 913654.3735942411, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "file": "0058.png", + "time": 913654.407528787, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "file": "0059.png", + "time": 913654.4408740861, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "file": "0060.png", + "time": 913654.475743551, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "time": 913654.5077910591, + "file": "0061.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "file": "0062.png", + "time": 913654.5393077991, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "time": 913654.5721815861, + "file": "0063.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "file": "0064.png", + "time": 913654.605473313, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "file": "0065.png", + "time": 913654.6419051881, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "file": "0066.png", + "time": 913654.6744255021, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "file": "0067.png", + "time": 913654.705513195, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "file": "0068.png", + "time": 913654.740565455, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + }, + { + "file": "0069.png", + "time": 913654.7747490801, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "60146cd389e33447b8e2efe4e8be1f670dee4b6b66347d5a65a206a130bb334f" + } + ] + }, + "green/replace": { + "capturedFrames": 116, + "mixedFrameCount": 1, + "selectedFrames": [ + "0000.png", + "0050.png", + "0053.png", + "0115.png" + ], + "samples": [ + { + "time": 913677.182000758, + "file": "0000.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0001.png", + "time": 913677.208739417, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913677.243329994, + "file": "0002.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0003.png", + "time": 913677.2766025431, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913677.3236773841, + "file": "0004.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913677.364179086, + "file": "0005.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0006.png", + "time": 913677.385782319, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913677.4096495911, + "file": "0007.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0008.png", + "time": 913677.4411729571, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913677.4755007031, + "file": "0009.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913677.507597494, + "file": "0010.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913677.541143598, + "file": "0011.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913677.575468246, + "file": "0012.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913677.607664886, + "file": "0013.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0014.png", + "time": 913677.63882929, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0015.png", + "time": 913677.670936167, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913677.704879379, + "file": "0016.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0017.png", + "time": 913677.7407021441, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913677.770956119, + "file": "0018.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0019.png", + "time": 913677.805286434, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0020.png", + "time": 913677.840920433, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913677.8730100831, + "file": "0021.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913677.9581180731, + "file": "0022.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0023.png", + "time": 913678.00875871, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913678.0434123791, + "file": "0024.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0025.png", + "time": 913678.073637673, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913678.1048476781, + "file": "0026.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0027.png", + "time": 913678.143166751, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0028.png", + "time": 913678.1782429271, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0029.png", + "time": 913678.2079582481, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0030.png", + "time": 913678.239294811, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913678.272196469, + "file": "0031.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913678.306947525, + "file": "0032.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0033.png", + "time": 913678.3415255671, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0034.png", + "time": 913678.372522235, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913678.407802038, + "file": "0035.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913678.440060911, + "file": "0036.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0037.png", + "time": 913678.4746635171, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913678.504926466, + "file": "0038.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913678.540071622, + "file": "0039.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0040.png", + "time": 913678.57196552, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0041.png", + "time": 913678.608095773, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0042.png", + "time": 913678.640771551, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0043.png", + "time": 913678.674382659, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0044.png", + "time": 913678.705199606, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913678.740146241, + "file": "0045.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913678.771469746, + "file": "0046.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0047.png", + "time": 913678.8079382931, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913678.837268878, + "file": "0048.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913678.8705325951, + "file": "0049.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "75681ba7c2ee52579521194b826e9f80d1f71afbc36ceea5d0d902d2fde49b34" + }, + { + "time": 913678.957523811, + "file": "0050.png", + "counts": { + "red": 21, + "green": 336, + "blue": 0 + }, + "centroids": { + "red": 360.2, + "green": 167.8, + "blue": null + }, + "bannerHash": "513692c21a9ef4c6d1098e7d4ec596940d4f33dc775452b7a1342daa9fcfc7d7" + }, + { + "file": "0051.png", + "time": 913679.0235937741, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "time": 913679.0449015361, + "file": "0052.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0053.png", + "time": 913679.0738721241, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0054.png", + "time": 913679.1065804161, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0055.png", + "time": 913679.140883742, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0056.png", + "time": 913679.173805641, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "time": 913679.209024106, + "file": "0057.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0058.png", + "time": 913679.2407425691, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "time": 913679.2749094791, + "file": "0059.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "time": 913679.307120681, + "file": "0060.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0061.png", + "time": 913679.3397686051, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "time": 913679.3735209911, + "file": "0062.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0063.png", + "time": 913679.407494329, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "time": 913679.4407483081, + "file": "0064.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "time": 913679.4731188971, + "file": "0065.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "time": 913679.5077481561, + "file": "0066.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0067.png", + "time": 913679.557752116, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "time": 913679.5799938161, + "file": "0068.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0069.png", + "time": 913679.6070735101, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0070.png", + "time": 913679.640295565, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "time": 913679.6770715461, + "file": "0071.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0072.png", + "time": 913679.707296167, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0073.png", + "time": 913679.740801326, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0074.png", + "time": 913679.7748053831, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "time": 913679.8080684431, + "file": "0075.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0076.png", + "time": 913679.8412805101, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0077.png", + "time": 913679.875727502, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0078.png", + "time": 913679.9071129421, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0079.png", + "time": 913679.9597379351, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "time": 913679.97902312, + "file": "0080.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0081.png", + "time": 913680.0047622471, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0082.png", + "time": 913680.042046424, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0083.png", + "time": 913680.0745555841, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0084.png", + "time": 913680.1082944991, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0085.png", + "time": 913680.142798252, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "time": 913680.173877244, + "file": "0086.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "time": 913680.2076699181, + "file": "0087.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "time": 913680.240965635, + "file": "0088.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "time": 913680.276695547, + "file": "0089.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0090.png", + "time": 913680.306939149, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0091.png", + "time": 913680.339200414, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0092.png", + "time": 913680.3754256241, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0093.png", + "time": 913680.40727143, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "time": 913680.4413317901, + "file": "0094.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0095.png", + "time": 913680.4747653401, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "time": 913680.5317274261, + "file": "0096.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0097.png", + "time": 913680.5543979681, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0098.png", + "time": 913680.5904828061, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0099.png", + "time": 913680.6107395231, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "time": 913680.6469458761, + "file": "0100.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "time": 913680.6707490981, + "file": "0101.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0102.png", + "time": 913680.724500303, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "time": 913680.7592690381, + "file": "0103.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0104.png", + "time": 913680.7851261741, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0105.png", + "time": 913680.805062163, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0106.png", + "time": 913680.8376410251, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "time": 913680.8706479521, + "file": "0107.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0108.png", + "time": 913680.9038749371, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0109.png", + "time": 913680.9381074051, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0110.png", + "time": 913680.987470016, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0111.png", + "time": 913681.0077511901, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0112.png", + "time": 913681.0417480101, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "time": 913681.0748786661, + "file": "0113.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "time": 913681.107035094, + "file": "0114.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + }, + { + "file": "0115.png", + "time": 913681.1391082171, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "6922d4b38970f9464faa5e6f723e45e190131cbbd87c29201d89753cc19c37cf" + } + ] + }, + "green/hide": { + "capturedFrames": 119, + "mixedFrameCount": 1, + "selectedFrames": [ + "0000.png", + "0053.png", + "0056.png", + "0118.png" + ], + "samples": [ + { + "file": "0000.png", + "time": 913625.5146856791, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0001.png", + "time": 913625.543152694, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913625.578383538, + "file": "0002.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0003.png", + "time": 913625.6081350951, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0004.png", + "time": 913625.642946815, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0005.png", + "time": 913625.675942062, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0006.png", + "time": 913625.7072436911, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913625.7405944001, + "file": "0007.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0008.png", + "time": 913625.772961818, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0009.png", + "time": 913625.8083248751, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0010.png", + "time": 913625.839961951, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0011.png", + "time": 913625.875382437, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0012.png", + "time": 913625.907734352, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913625.9399363911, + "file": "0013.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0014.png", + "time": 913625.9756715071, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913626.0076115291, + "file": "0015.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913626.040202938, + "file": "0016.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913626.0763464931, + "file": "0017.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913626.1074425881, + "file": "0018.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0019.png", + "time": 913626.142858917, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913626.174399673, + "file": "0020.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0021.png", + "time": 913626.2089162191, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913626.245077291, + "file": "0022.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0023.png", + "time": 913626.273724116, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913626.308378434, + "file": "0024.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913626.342996428, + "file": "0025.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0026.png", + "time": 913626.375832002, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0027.png", + "time": 913626.4081797751, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0028.png", + "time": 913626.4584254951, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913626.47900169, + "file": "0029.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0030.png", + "time": 913626.505358272, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0031.png", + "time": 913626.539463017, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0032.png", + "time": 913626.573096725, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0033.png", + "time": 913626.608633795, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913626.6417559021, + "file": "0034.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0035.png", + "time": 913626.6760256, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913626.707807358, + "file": "0036.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0037.png", + "time": 913626.7404586211, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0038.png", + "time": 913626.7742304481, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913626.8069071311, + "file": "0039.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913626.841651942, + "file": "0040.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913626.875099051, + "file": "0041.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0042.png", + "time": 913626.906885443, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0043.png", + "time": 913626.9402810431, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0044.png", + "time": 913626.9741933261, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0045.png", + "time": 913627.0088141051, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0046.png", + "time": 913627.04069364, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0047.png", + "time": 913627.0738882851, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913627.106783885, + "file": "0048.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0049.png", + "time": 913627.142314809, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0050.png", + "time": 913627.1741426891, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "file": "0051.png", + "time": 913627.207692999, + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "5b3a943c60039a7b292a3fc1fbb2c99bd8e3ab71a9a525edb2e0e0e7c43651e1" + }, + { + "time": 913627.2382389171, + "file": "0052.png", + "counts": { + "red": 0, + "green": 359, + "blue": 0 + }, + "centroids": { + "red": null, + "green": 187.0, + "blue": null + }, + "bannerHash": "75681ba7c2ee52579521194b826e9f80d1f71afbc36ceea5d0d902d2fde49b34" + }, + { + "time": 913627.2715684241, + "file": "0053.png", + "counts": { + "red": 21, + "green": 336, + "blue": 0 + }, + "centroids": { + "red": 360.2, + "green": 167.8, + "blue": null + }, + "bannerHash": "513692c21a9ef4c6d1098e7d4ec596940d4f33dc775452b7a1342daa9fcfc7d7" + }, + { + "file": "0054.png", + "time": 913627.3016557251, + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "file": "0055.png", + "time": 913627.3565241981, + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "time": 913627.3890422831, + "file": "0056.png", + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "time": 913627.410564169, + "file": "0057.png", + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "file": "0058.png", + "time": 913627.44241933, + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "time": 913627.47227425, + "file": "0059.png", + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "time": 913627.503578981, + "file": "0060.png", + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "time": 913627.53656089, + "file": "0061.png", + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "time": 913627.5706346061, + "file": "0062.png", + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "time": 913627.6059774631, + "file": "0063.png", + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "file": "0064.png", + "time": 913627.642300406, + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "time": 913627.672786911, + "file": "0065.png", + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "file": "0066.png", + "time": 913627.7059342221, + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "file": "0067.png", + "time": 913627.7404805991, + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "file": "0068.png", + "time": 913627.772288126, + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "file": "0069.png", + "time": 913627.806091586, + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "file": "0070.png", + "time": 913627.8423069571, + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "time": 913627.872227921, + "file": "0071.png", + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "file": "0072.png", + "time": 913627.904815815, + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "time": 913627.9391412591, + "file": "0073.png", + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "file": "0074.png", + "time": 913627.974508933, + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "time": 913628.0064958821, + "file": "0075.png", + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "time": 913628.0413351611, + "file": "0076.png", + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "file": "0077.png", + "time": 913628.072915519, + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "file": "0078.png", + "time": 913628.1060072781, + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "file": "0079.png", + "time": 913628.141235796, + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "file": "0080.png", + "time": 913628.1745066891, + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "file": "0081.png", + "time": 913628.20653584, + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "file": "0082.png", + "time": 913628.240295661, + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "time": 913628.274166323, + "file": "0083.png", + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "time": 913628.306417725, + "file": "0084.png", + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "file": "0085.png", + "time": 913628.341532737, + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "time": 913628.3915037521, + "file": "0086.png", + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "file": "0087.png", + "time": 913628.411066954, + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "time": 913628.455057331, + "file": "0088.png", + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "file": "0089.png", + "time": 913628.474613611, + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "time": 913628.503860494, + "file": "0090.png", + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "file": "0091.png", + "time": 913628.5366643631, + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "file": "0092.png", + "time": 913628.573629267, + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "file": "0093.png", + "time": 913628.604441763, + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "time": 913628.6594196, + "file": "0094.png", + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "file": "0095.png", + "time": 913628.6832831871, + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "57a040acb73e3096eaa0c69f705d9d83929a7254d8b631f4971df4049cb79b05" + }, + { + "file": "0096.png", + "time": 913628.7330405031, + "counts": { + "red": 0, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": null, + "green": null, + "blue": null + }, + "bannerHash": "52de3b16397f12794bef6895702bf16f65ad74ecd416af4faa508db52778d115" + }, + { + "time": 913628.77642887, + "file": "0097.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0098.png", + "time": 913628.8043427521, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0099.png", + "time": 913628.840021964, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0100.png", + "time": 913628.8716845771, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "time": 913628.905594829, + "file": "0101.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0102.png", + "time": 913628.93960048, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0103.png", + "time": 913628.9711876811, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "time": 913629.0029976941, + "file": "0104.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0105.png", + "time": 913629.0400746231, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0106.png", + "time": 913629.071553146, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0107.png", + "time": 913629.103350113, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0108.png", + "time": 913629.140292271, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "time": 913629.168505785, + "file": "0109.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "time": 913629.207598084, + "file": "0110.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0111.png", + "time": 913629.236739716, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "time": 913629.272875122, + "file": "0112.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0113.png", + "time": 913629.307721958, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0114.png", + "time": 913629.3432565581, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0115.png", + "time": 913629.3731304981, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0116.png", + "time": 913629.407447105, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "file": "0117.png", + "time": 913629.4423861081, + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + }, + { + "time": 913629.477664105, + "file": "0118.png", + "counts": { + "red": 359, + "green": 0, + "blue": 0 + }, + "centroids": { + "red": 187.0, + "green": null, + "blue": null + }, + "bannerHash": "f8d76393ec72d9502c16972d5857d172344005ed528ed3409c855cb92654cf75" + } + ] + } + }, + "replaceDelayAfterObservedMixedSeconds": 0.0014174090465530753, + "hideDelayAfterObservedMixedSeconds": 0.0010020940098911524 +} \ No newline at end of file diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/automatic/0000.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/automatic/0000.png new file mode 100644 index 00000000..ad35d71f Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/automatic/0000.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/automatic/0058.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/automatic/0058.png new file mode 100644 index 00000000..203e546d Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/automatic/0058.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/automatic/0063.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/automatic/0063.png new file mode 100644 index 00000000..cbe9894c Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/automatic/0063.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/automatic/0067.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/automatic/0067.png new file mode 100644 index 00000000..9a0f0118 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/automatic/0067.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/automatic/0070.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/automatic/0070.png new file mode 100644 index 00000000..a170a290 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/automatic/0070.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/automatic/0115.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/automatic/0115.png new file mode 100644 index 00000000..a170a290 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/automatic/0115.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/automatic/frames.json b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/automatic/frames.json new file mode 100644 index 00000000..26e99e0f --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/automatic/frames.json @@ -0,0 +1,466 @@ +[ + { + "time": 913597.4492290791, + "file": "0000.png" + }, + { + "file": "0001.png", + "time": 913597.4755964761 + }, + { + "file": "0002.png", + "time": 913597.50850726 + }, + { + "time": 913597.5433671961, + "file": "0003.png" + }, + { + "time": 913597.5739051071, + "file": "0004.png" + }, + { + "file": "0005.png", + "time": 913597.609845889 + }, + { + "time": 913597.641820132, + "file": "0006.png" + }, + { + "file": "0007.png", + "time": 913597.676881199 + }, + { + "file": "0008.png", + "time": 913597.7084638361 + }, + { + "file": "0009.png", + "time": 913597.7411874051 + }, + { + "file": "0010.png", + "time": 913597.775570434 + }, + { + "time": 913597.8080575881, + "file": "0011.png" + }, + { + "file": "0012.png", + "time": 913597.84204845 + }, + { + "file": "0013.png", + "time": 913597.8778581431 + }, + { + "file": "0014.png", + "time": 913597.9616468931 + }, + { + "time": 913598.004829079, + "file": "0015.png" + }, + { + "file": "0016.png", + "time": 913598.0422670391 + }, + { + "file": "0017.png", + "time": 913598.0752597761 + }, + { + "file": "0018.png", + "time": 913598.1080234471 + }, + { + "time": 913598.141075, + "file": "0019.png" + }, + { + "file": "0020.png", + "time": 913598.175370774 + }, + { + "file": "0021.png", + "time": 913598.2073697711 + }, + { + "time": 913598.2420130861, + "file": "0022.png" + }, + { + "file": "0023.png", + "time": 913598.2760424891 + }, + { + "time": 913598.306953603, + "file": "0024.png" + }, + { + "time": 913598.3411505871, + "file": "0025.png" + }, + { + "time": 913598.374023861, + "file": "0026.png" + }, + { + "time": 913598.407951768, + "file": "0027.png" + }, + { + "file": "0028.png", + "time": 913598.4401897051 + }, + { + "file": "0029.png", + "time": 913598.4735934021 + }, + { + "time": 913598.5078664881, + "file": "0030.png" + }, + { + "file": "0031.png", + "time": 913598.5434376 + }, + { + "file": "0032.png", + "time": 913598.5772900771 + }, + { + "file": "0033.png", + "time": 913598.607618751 + }, + { + "file": "0034.png", + "time": 913598.641246484 + }, + { + "time": 913598.6736933991, + "file": "0035.png" + }, + { + "file": "0036.png", + "time": 913598.7074893891 + }, + { + "file": "0037.png", + "time": 913598.743114041 + }, + { + "file": "0038.png", + "time": 913598.7739387951 + }, + { + "file": "0039.png", + "time": 913598.808327698 + }, + { + "time": 913598.841209955, + "file": "0040.png" + }, + { + "file": "0041.png", + "time": 913598.875832265 + }, + { + "file": "0042.png", + "time": 913598.908611273 + }, + { + "file": "0043.png", + "time": 913598.9920293391 + }, + { + "time": 913599.0429295121, + "file": "0044.png" + }, + { + "file": "0045.png", + "time": 913599.073915453 + }, + { + "time": 913599.1069630211, + "file": "0046.png" + }, + { + "file": "0047.png", + "time": 913599.139273638 + }, + { + "file": "0048.png", + "time": 913599.171037629 + }, + { + "file": "0049.png", + "time": 913599.2040882771 + }, + { + "time": 913599.2386047101, + "file": "0050.png" + }, + { + "file": "0051.png", + "time": 913599.2723036291 + }, + { + "time": 913599.3079158181, + "file": "0052.png" + }, + { + "file": "0053.png", + "time": 913599.3431351511 + }, + { + "time": 913599.376199943, + "file": "0054.png" + }, + { + "file": "0055.png", + "time": 913599.4078560281 + }, + { + "time": 913599.440783828, + "file": "0056.png" + }, + { + "time": 913599.4710975741, + "file": "0057.png" + }, + { + "file": "0058.png", + "time": 913599.5026518331 + }, + { + "time": 913599.5386346581, + "file": "0059.png" + }, + { + "time": 913599.5707385921, + "file": "0060.png" + }, + { + "file": "0061.png", + "time": 913599.602594416 + }, + { + "time": 913599.6393803691, + "file": "0062.png" + }, + { + "file": "0063.png", + "time": 913599.6682303511 + }, + { + "file": "0064.png", + "time": 913599.7060550201 + }, + { + "time": 913599.736194105, + "file": "0065.png" + }, + { + "file": "0066.png", + "time": 913599.7714051381 + }, + { + "time": 913599.802721531, + "file": "0067.png" + }, + { + "time": 913599.873930242, + "file": "0068.png" + }, + { + "time": 913599.893517905, + "file": "0069.png" + }, + { + "file": "0070.png", + "time": 913599.913150748 + }, + { + "file": "0071.png", + "time": 913599.942140343 + }, + { + "time": 913599.97637692, + "file": "0072.png" + }, + { + "time": 913600.0079948221, + "file": "0073.png" + }, + { + "file": "0074.png", + "time": 913600.0425524261 + }, + { + "file": "0075.png", + "time": 913600.0739939511 + }, + { + "time": 913600.109444809, + "file": "0076.png" + }, + { + "time": 913600.14165221, + "file": "0077.png" + }, + { + "time": 913600.1751490401, + "file": "0078.png" + }, + { + "time": 913600.207547056, + "file": "0079.png" + }, + { + "time": 913600.241096996, + "file": "0080.png" + }, + { + "file": "0081.png", + "time": 913600.2762579471 + }, + { + "file": "0082.png", + "time": 913600.306952715 + }, + { + "time": 913600.3412508611, + "file": "0083.png" + }, + { + "time": 913600.3752544611, + "file": "0084.png" + }, + { + "file": "0085.png", + "time": 913600.4240071401 + }, + { + "time": 913600.4467707721, + "file": "0086.png" + }, + { + "time": 913600.4733675481, + "file": "0087.png" + }, + { + "time": 913600.507895805, + "file": "0088.png" + }, + { + "file": "0089.png", + "time": 913600.5426770151 + }, + { + "file": "0090.png", + "time": 913600.573082122 + }, + { + "file": "0091.png", + "time": 913600.6076171291 + }, + { + "file": "0092.png", + "time": 913600.6396793531 + }, + { + "file": "0093.png", + "time": 913600.675508002 + }, + { + "file": "0094.png", + "time": 913600.7075339471 + }, + { + "time": 913600.74025594, + "file": "0095.png" + }, + { + "time": 913600.775323804, + "file": "0096.png" + }, + { + "file": "0097.png", + "time": 913600.8072155941 + }, + { + "time": 913600.8411561421, + "file": "0098.png" + }, + { + "file": "0099.png", + "time": 913600.8750394251 + }, + { + "file": "0100.png", + "time": 913600.90762452 + }, + { + "file": "0101.png", + "time": 913600.940233796 + }, + { + "time": 913600.9744563231, + "file": "0102.png" + }, + { + "time": 913601.00685121, + "file": "0103.png" + }, + { + "file": "0104.png", + "time": 913601.040866059 + }, + { + "time": 913601.074510779, + "file": "0105.png" + }, + { + "file": "0106.png", + "time": 913601.1079004781 + }, + { + "file": "0107.png", + "time": 913601.1438779951 + }, + { + "file": "0108.png", + "time": 913601.1751519741 + }, + { + "time": 913601.2079005501, + "file": "0109.png" + }, + { + "file": "0110.png", + "time": 913601.24195652 + }, + { + "time": 913601.277139337, + "file": "0111.png" + }, + { + "time": 913601.307590552, + "file": "0112.png" + }, + { + "file": "0113.png", + "time": 913601.3412412221 + }, + { + "time": 913601.3733143321, + "file": "0114.png" + }, + { + "file": "0115.png", + "time": 913601.4083740071 + } +] \ No newline at end of file diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/events.json b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/events.json new file mode 100644 index 00000000..5dbb03a2 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/events.json @@ -0,0 +1,362 @@ +[ + { + "pid": 34695, + "test": "-[SubBannerGestureUITests testAutomaticFiveSecondWrapFrames]", + "time": 913588.4641189601, + "event": "launch" + }, + { + "event": "phase-inactive", + "pid": 34695, + "time": 913589.395679436, + "test": "-[SubBannerGestureUITests testAutomaticFiveSecondWrapFrames]" + }, + { + "event": "phase-active", + "time": 913589.412622352, + "pid": 34695, + "test": "-[SubBannerGestureUITests testAutomaticFiveSecondWrapFrames]" + }, + { + "pid": 34695, + "event": "record-automatic", + "time": 913597.4139605111, + "test": "-[SubBannerGestureUITests testAutomaticFiveSecondWrapFrames]" + }, + { + "test": "-[SubBannerGestureUITests testAutomaticFiveSecondWrapFrames]", + "pid": 34695, + "time": 913602.1043090811, + "event": "tap-1" + }, + { + "time": 913604.48309818, + "pid": 34735, + "event": "launch", + "test": "-[SubBannerGestureUITests testDirectionsWrapShortDragAndTap]" + }, + { + "pid": 34735, + "event": "phase-inactive", + "time": 913605.391383399, + "test": "-[SubBannerGestureUITests testDirectionsWrapShortDragAndTap]" + }, + { + "test": "-[SubBannerGestureUITests testDirectionsWrapShortDragAndTap]", + "event": "phase-active", + "time": 913605.4323043161, + "pid": 34735 + }, + { + "event": "tap-1", + "time": 913608.887246688, + "pid": 34735, + "test": "-[SubBannerGestureUITests testDirectionsWrapShortDragAndTap]" + }, + { + "event": "tap-3", + "time": 913610.570216782, + "pid": 34735, + "test": "-[SubBannerGestureUITests testDirectionsWrapShortDragAndTap]" + }, + { + "event": "tap-1", + "time": 913613.587584087, + "pid": 34735, + "test": "-[SubBannerGestureUITests testDirectionsWrapShortDragAndTap]" + }, + { + "pid": 34767, + "test": "-[SubBannerGestureUITests testDisappearanceDuringAutomaticWrap]", + "event": "launch", + "time": 913615.994330842 + }, + { + "test": "-[SubBannerGestureUITests testDisappearanceDuringAutomaticWrap]", + "event": "phase-inactive", + "time": 913616.8966431321, + "pid": 34767 + }, + { + "pid": 34767, + "event": "phase-active", + "time": 913616.9213246681, + "test": "-[SubBannerGestureUITests testDisappearanceDuringAutomaticWrap]" + }, + { + "test": "-[SubBannerGestureUITests testDisappearanceDuringAutomaticWrap]", + "time": 913625.4543779121, + "event": "record-hide", + "pid": 34767 + }, + { + "pid": 34767, + "test": "-[SubBannerGestureUITests testDisappearanceDuringAutomaticWrap]", + "time": 913627.272562311, + "event": "observed-mixed-hide" + }, + { + "time": 913627.273564405, + "pid": 34767, + "test": "-[SubBannerGestureUITests testDisappearanceDuringAutomaticWrap]", + "event": "hide" + }, + { + "event": "show", + "time": 913628.662756823, + "pid": 34767, + "test": "-[SubBannerGestureUITests testDisappearanceDuringAutomaticWrap]" + }, + { + "test": "-[SubBannerGestureUITests testInactiveSceneCancelsAutomaticAdvance]", + "time": 913632.1317507351, + "pid": 34805, + "event": "launch" + }, + { + "time": 913633.0326134231, + "pid": 34805, + "test": "-[SubBannerGestureUITests testInactiveSceneCancelsAutomaticAdvance]", + "event": "phase-inactive" + }, + { + "time": 913633.0621602761, + "event": "phase-active", + "pid": 34805, + "test": "-[SubBannerGestureUITests testInactiveSceneCancelsAutomaticAdvance]" + }, + { + "test": "-[SubBannerGestureUITests testInactiveSceneCancelsAutomaticAdvance]", + "time": 913636.61430926, + "pid": 34805, + "event": "phase-inactive" + }, + { + "time": 913637.3474018341, + "pid": 34805, + "test": "-[SubBannerGestureUITests testInactiveSceneCancelsAutomaticAdvance]", + "event": "phase-background" + }, + { + "event": "phase-inactive", + "time": 913642.895329179, + "pid": 34805, + "test": "-[SubBannerGestureUITests testInactiveSceneCancelsAutomaticAdvance]" + }, + { + "pid": 34805, + "test": "-[SubBannerGestureUITests testInactiveSceneCancelsAutomaticAdvance]", + "event": "phase-active", + "time": 913643.185701646 + }, + { + "pid": 34837, + "test": "-[SubBannerGestureUITests testManualBoundaryFrames]", + "event": "launch", + "time": 913645.102787193 + }, + { + "event": "phase-inactive", + "pid": 34837, + "time": 913645.9961799241, + "test": "-[SubBannerGestureUITests testManualBoundaryFrames]" + }, + { + "test": "-[SubBannerGestureUITests testManualBoundaryFrames]", + "time": 913646.027965715, + "event": "phase-active", + "pid": 34837 + }, + { + "time": 913649.4333553461, + "test": "-[SubBannerGestureUITests testManualBoundaryFrames]", + "pid": 34837, + "event": "record-manual" + }, + { + "pid": 34837, + "test": "-[SubBannerGestureUITests testManualBoundaryFrames]", + "event": "record-forward", + "time": 913652.2542932191 + }, + { + "test": "-[SubBannerGestureUITests testMixedRatiosAndReplacement]", + "pid": 34870, + "time": 913657.008808529, + "event": "launch" + }, + { + "time": 913657.9150553821, + "event": "phase-inactive", + "pid": 34870, + "test": "-[SubBannerGestureUITests testMixedRatiosAndReplacement]" + }, + { + "test": "-[SubBannerGestureUITests testMixedRatiosAndReplacement]", + "event": "phase-active", + "time": 913657.9402682821, + "pid": 34870 + }, + { + "time": 913664.836766332, + "test": "-[SubBannerGestureUITests testMixedRatiosAndReplacement]", + "pid": 34870, + "event": "replace" + }, + { + "event": "tap-9", + "time": 913665.2533201941, + "pid": 34870, + "test": "-[SubBannerGestureUITests testMixedRatiosAndReplacement]" + }, + { + "time": 913667.64497189, + "test": "-[SubBannerGestureUITests testReplacementDuringAutomaticWrap]", + "pid": 34894, + "event": "launch" + }, + { + "event": "phase-inactive", + "time": 913668.5523268571, + "pid": 34894, + "test": "-[SubBannerGestureUITests testReplacementDuringAutomaticWrap]" + }, + { + "test": "-[SubBannerGestureUITests testReplacementDuringAutomaticWrap]", + "event": "phase-active", + "pid": 34894, + "time": 913668.589080931 + }, + { + "time": 913677.1188918981, + "test": "-[SubBannerGestureUITests testReplacementDuringAutomaticWrap]", + "pid": 34894, + "event": "record-replace" + }, + { + "event": "observed-mixed-replace", + "test": "-[SubBannerGestureUITests testReplacementDuringAutomaticWrap]", + "time": 913678.958359765, + "pid": 34894 + }, + { + "test": "-[SubBannerGestureUITests testReplacementDuringAutomaticWrap]", + "time": 913678.959777174, + "pid": 34894, + "event": "replace" + }, + { + "time": 913680.7371332, + "event": "tap-9", + "test": "-[SubBannerGestureUITests testReplacementDuringAutomaticWrap]", + "pid": 34894 + }, + { + "pid": 34933, + "test": "-[SubBannerGestureUITests testSingleAndEmpty]", + "event": "launch", + "time": 913683.2259921201 + }, + { + "event": "phase-inactive", + "pid": 34933, + "time": 913684.1276096171, + "test": "-[SubBannerGestureUITests testSingleAndEmpty]" + }, + { + "pid": 34933, + "test": "-[SubBannerGestureUITests testSingleAndEmpty]", + "event": "phase-active", + "time": 913684.1499581131 + }, + { + "time": 913687.586987999, + "pid": 34933, + "event": "tap-1", + "test": "-[SubBannerGestureUITests testSingleAndEmpty]" + }, + { + "test": "-[SubBannerGestureUITests testSingleAndEmpty]", + "time": 913690.6921521671, + "event": "launch", + "pid": 34951 + }, + { + "time": 913691.5782256341, + "test": "-[SubBannerGestureUITests testSingleAndEmpty]", + "event": "phase-inactive", + "pid": 34951 + }, + { + "time": 913691.5995834051, + "event": "phase-active", + "pid": 34951, + "test": "-[SubBannerGestureUITests testSingleAndEmpty]" + }, + { + "time": 913696.0071675461, + "pid": 34968, + "test": "-[SubBannerGestureUITests testSwipeDoesNotTap]", + "event": "launch" + }, + { + "pid": 34968, + "time": 913696.9234373821, + "event": "phase-inactive", + "test": "-[SubBannerGestureUITests testSwipeDoesNotTap]" + }, + { + "event": "phase-active", + "pid": 34968, + "test": "-[SubBannerGestureUITests testSwipeDoesNotTap]", + "time": 913696.949425647 + }, + { + "event": "tap-2", + "test": "-[SubBannerGestureUITests testSwipeDoesNotTap]", + "time": 913701.720210077, + "pid": 34968 + }, + { + "event": "launch", + "pid": 34991, + "test": "-[SubBannerGestureUITests testSwipeReleasePositionsAndRepeatedInput]", + "time": 913704.0864146911 + }, + { + "event": "phase-inactive", + "test": "-[SubBannerGestureUITests testSwipeReleasePositionsAndRepeatedInput]", + "time": 913704.9734107051, + "pid": 34991 + }, + { + "pid": 34991, + "event": "phase-active", + "time": 913705.0040099691, + "test": "-[SubBannerGestureUITests testSwipeReleasePositionsAndRepeatedInput]" + }, + { + "test": "-[SubBannerGestureUITests testSwipeReleasePositionsAndRepeatedInput]", + "event": "tap-1", + "time": 913712.636718675, + "pid": 34991 + }, + { + "pid": 35015, + "event": "launch", + "time": 913715.053948565, + "test": "-[SubBannerGestureUITests testVerticalParentScrollDoesNotTap]" + }, + { + "time": 913715.943832408, + "pid": 35015, + "test": "-[SubBannerGestureUITests testVerticalParentScrollDoesNotTap]", + "event": "phase-inactive" + }, + { + "pid": 35015, + "event": "phase-active", + "test": "-[SubBannerGestureUITests testVerticalParentScrollDoesNotTap]", + "time": 913715.967804307 + } +] \ No newline at end of file diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/final-confirmed-after.sha256 b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/final-confirmed-after.sha256 new file mode 100644 index 00000000..54381962 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/final-confirmed-after.sha256 @@ -0,0 +1 @@ +b3e4c5333cf8249ccd7773b5cabd372efb5a5565323bd7778034e110386151fd /Users/klaus/Develop/sodalive/iOS/SodaLive/SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/final-confirmed-before.sha256 b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/final-confirmed-before.sha256 new file mode 100644 index 00000000..54381962 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/final-confirmed-before.sha256 @@ -0,0 +1 @@ +b3e4c5333cf8249ccd7773b5cabd372efb5a5565323bd7778034e110386151fd /Users/klaus/Develop/sodalive/iOS/SodaLive/SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/final-confirmed-compiled.sha256 b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/final-confirmed-compiled.sha256 new file mode 100644 index 00000000..54381962 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/final-confirmed-compiled.sha256 @@ -0,0 +1 @@ +b3e4c5333cf8249ccd7773b5cabd372efb5a5565323bd7778034e110386151fd /Users/klaus/Develop/sodalive/iOS/SodaLive/SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/focused.log b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/focused.log new file mode 100644 index 00000000..717c6152 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/focused.log @@ -0,0 +1,5 @@ +PASS: actual APIs/repositories, two exact GETs, requestPlain, anonymous/whitespace auth, original bearer, adjacent follow POST/body/header +PASS: actual two VMs reject non200 valid/invalid JSON, success false, decode/transport errors; preserve error UI and loading +PASS: actual DTOs/VM success, >2^53 and Int.max, enum, required array, 23+ full order, placement/eligibility, legacy banner, subbanner-only/filtered-empty, failure retains data +PASS: HTTP(S)+host, absolute URI/blacklist, malformed/blank/auto-encoded rejection, exact escaped URI, all 8 link combinations, completion-based iOS/web attempts, Android never +PASS: sub-banner production-code checks; native Combine, Moya transport double; no live requests diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/forward/0000.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/forward/0000.png new file mode 100644 index 00000000..03ffce4c Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/forward/0000.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/forward/0021.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/forward/0021.png new file mode 100644 index 00000000..6f8ba3b7 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/forward/0021.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/forward/0032.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/forward/0032.png new file mode 100644 index 00000000..1f3ca8c4 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/forward/0032.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/forward/0042.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/forward/0042.png new file mode 100644 index 00000000..f194aab0 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/forward/0042.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/forward/0045.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/forward/0045.png new file mode 100644 index 00000000..00835f10 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/forward/0045.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/forward/0069.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/forward/0069.png new file mode 100644 index 00000000..00835f10 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/forward/0069.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/forward/frames.json b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/forward/frames.json new file mode 100644 index 00000000..99bb15e0 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/forward/frames.json @@ -0,0 +1,282 @@ +[ + { + "time": 913652.291601978, + "file": "0000.png" + }, + { + "time": 913652.311797108, + "file": "0001.png" + }, + { + "time": 913652.3391558371, + "file": "0002.png" + }, + { + "time": 913652.376711928, + "file": "0003.png" + }, + { + "file": "0004.png", + "time": 913652.4078878481 + }, + { + "file": "0005.png", + "time": 913652.4395461461 + }, + { + "file": "0006.png", + "time": 913652.4726654671 + }, + { + "time": 913652.538570735, + "file": "0007.png" + }, + { + "time": 913652.586738405, + "file": "0008.png" + }, + { + "file": "0009.png", + "time": 913652.60548328 + }, + { + "file": "0010.png", + "time": 913652.663007152 + }, + { + "file": "0011.png", + "time": 913652.682149627 + }, + { + "file": "0012.png", + "time": 913652.7373864851 + }, + { + "file": "0013.png", + "time": 913652.805418989 + }, + { + "time": 913652.904987868, + "file": "0014.png" + }, + { + "time": 913652.9665794891, + "file": "0015.png" + }, + { + "file": "0016.png", + "time": 913652.987309437 + }, + { + "time": 913653.0076745551, + "file": "0017.png" + }, + { + "file": "0018.png", + "time": 913653.039906566 + }, + { + "time": 913653.0749919331, + "file": "0019.png" + }, + { + "time": 913653.109595312, + "file": "0020.png" + }, + { + "file": "0021.png", + "time": 913653.138689461 + }, + { + "file": "0022.png", + "time": 913653.1710794021 + }, + { + "file": "0023.png", + "time": 913653.2016720751 + }, + { + "file": "0024.png", + "time": 913653.2382149621 + }, + { + "time": 913653.268156755, + "file": "0025.png" + }, + { + "file": "0026.png", + "time": 913653.3231254701 + }, + { + "file": "0027.png", + "time": 913653.3458972421 + }, + { + "time": 913653.3699691831, + "file": "0028.png" + }, + { + "time": 913653.4018004481, + "file": "0029.png" + }, + { + "file": "0030.png", + "time": 913653.4518237461 + }, + { + "time": 913653.472333822, + "file": "0031.png" + }, + { + "file": "0032.png", + "time": 913653.520925558 + }, + { + "file": "0033.png", + "time": 913653.5443326001 + }, + { + "file": "0034.png", + "time": 913653.5703751601 + }, + { + "file": "0035.png", + "time": 913653.6053882821 + }, + { + "file": "0036.png", + "time": 913653.6348647911 + }, + { + "file": "0037.png", + "time": 913653.6737473761 + }, + { + "file": "0038.png", + "time": 913653.701857235 + }, + { + "file": "0039.png", + "time": 913653.7411189 + }, + { + "file": "0040.png", + "time": 913653.798327717 + }, + { + "file": "0041.png", + "time": 913653.8416348731 + }, + { + "time": 913653.8685095541, + "file": "0042.png" + }, + { + "file": "0043.png", + "time": 913653.904678173 + }, + { + "time": 913653.9346417851, + "file": "0044.png" + }, + { + "time": 913653.9724434691, + "file": "0045.png" + }, + { + "file": "0046.png", + "time": 913654.0039547511 + }, + { + "file": "0047.png", + "time": 913654.03900551 + }, + { + "time": 913654.072691896, + "file": "0048.png" + }, + { + "file": "0049.png", + "time": 913654.1242488661 + }, + { + "file": "0050.png", + "time": 913654.145452633 + }, + { + "time": 913654.172808056, + "file": "0051.png" + }, + { + "file": "0052.png", + "time": 913654.2075264001 + }, + { + "time": 913654.242221885, + "file": "0053.png" + }, + { + "time": 913654.276169833, + "file": "0054.png" + }, + { + "time": 913654.306327088, + "file": "0055.png" + }, + { + "file": "0056.png", + "time": 913654.341744952 + }, + { + "file": "0057.png", + "time": 913654.3735942411 + }, + { + "file": "0058.png", + "time": 913654.407528787 + }, + { + "file": "0059.png", + "time": 913654.4408740861 + }, + { + "file": "0060.png", + "time": 913654.475743551 + }, + { + "time": 913654.5077910591, + "file": "0061.png" + }, + { + "file": "0062.png", + "time": 913654.5393077991 + }, + { + "time": 913654.5721815861, + "file": "0063.png" + }, + { + "file": "0064.png", + "time": 913654.605473313 + }, + { + "file": "0065.png", + "time": 913654.6419051881 + }, + { + "file": "0066.png", + "time": 913654.6744255021 + }, + { + "file": "0067.png", + "time": 913654.705513195 + }, + { + "file": "0068.png", + "time": 913654.740565455 + }, + { + "file": "0069.png", + "time": 913654.7747490801 + } +] \ No newline at end of file diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/harness.sha256 b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/harness.sha256 new file mode 100644 index 00000000..f705d212 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/harness.sha256 @@ -0,0 +1,4 @@ +fb9c6a4fa0ada92335df184de26eba1f560d243ae7ee0a22df92a3ab0841bba0 work/tests/SubBannerGestureHarness.swift +17ca9e2b84ede28fcd69043585a0ae8fc684e889c86196431ddb67904ae49f94 work/tests/SubBannerGestureUITests.swift +d1ace05fd9a0e703aec1eeada0630951b69aff0094e51d2e2824c6f059c51cb0 work/tests/run-sub-banner-gesture-tests.sh +90d49c05bd5b2ef8872842fd0dcbb372687ad363fd8293417d61e320c33ada8c work/tests/generate-sub-banner-gesture-project.rb diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/hide/0000.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/hide/0000.png new file mode 100644 index 00000000..ad35d71f Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/hide/0000.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/hide/0053.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/hide/0053.png new file mode 100644 index 00000000..203e546d Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/hide/0053.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/hide/0056.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/hide/0056.png new file mode 100644 index 00000000..9c85dea2 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/hide/0056.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/hide/0118.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/hide/0118.png new file mode 100644 index 00000000..a170a290 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/hide/0118.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/hide/frames.json b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/hide/frames.json new file mode 100644 index 00000000..8652e207 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/hide/frames.json @@ -0,0 +1,478 @@ +[ + { + "file": "0000.png", + "time": 913625.5146856791 + }, + { + "file": "0001.png", + "time": 913625.543152694 + }, + { + "time": 913625.578383538, + "file": "0002.png" + }, + { + "file": "0003.png", + "time": 913625.6081350951 + }, + { + "file": "0004.png", + "time": 913625.642946815 + }, + { + "file": "0005.png", + "time": 913625.675942062 + }, + { + "file": "0006.png", + "time": 913625.7072436911 + }, + { + "time": 913625.7405944001, + "file": "0007.png" + }, + { + "file": "0008.png", + "time": 913625.772961818 + }, + { + "file": "0009.png", + "time": 913625.8083248751 + }, + { + "file": "0010.png", + "time": 913625.839961951 + }, + { + "file": "0011.png", + "time": 913625.875382437 + }, + { + "file": "0012.png", + "time": 913625.907734352 + }, + { + "time": 913625.9399363911, + "file": "0013.png" + }, + { + "file": "0014.png", + "time": 913625.9756715071 + }, + { + "time": 913626.0076115291, + "file": "0015.png" + }, + { + "time": 913626.040202938, + "file": "0016.png" + }, + { + "time": 913626.0763464931, + "file": "0017.png" + }, + { + "time": 913626.1074425881, + "file": "0018.png" + }, + { + "file": "0019.png", + "time": 913626.142858917 + }, + { + "time": 913626.174399673, + "file": "0020.png" + }, + { + "file": "0021.png", + "time": 913626.2089162191 + }, + { + "time": 913626.245077291, + "file": "0022.png" + }, + { + "file": "0023.png", + "time": 913626.273724116 + }, + { + "time": 913626.308378434, + "file": "0024.png" + }, + { + "time": 913626.342996428, + "file": "0025.png" + }, + { + "file": "0026.png", + "time": 913626.375832002 + }, + { + "file": "0027.png", + "time": 913626.4081797751 + }, + { + "file": "0028.png", + "time": 913626.4584254951 + }, + { + "time": 913626.47900169, + "file": "0029.png" + }, + { + "file": "0030.png", + "time": 913626.505358272 + }, + { + "file": "0031.png", + "time": 913626.539463017 + }, + { + "file": "0032.png", + "time": 913626.573096725 + }, + { + "file": "0033.png", + "time": 913626.608633795 + }, + { + "time": 913626.6417559021, + "file": "0034.png" + }, + { + "file": "0035.png", + "time": 913626.6760256 + }, + { + "time": 913626.707807358, + "file": "0036.png" + }, + { + "file": "0037.png", + "time": 913626.7404586211 + }, + { + "file": "0038.png", + "time": 913626.7742304481 + }, + { + "time": 913626.8069071311, + "file": "0039.png" + }, + { + "time": 913626.841651942, + "file": "0040.png" + }, + { + "time": 913626.875099051, + "file": "0041.png" + }, + { + "file": "0042.png", + "time": 913626.906885443 + }, + { + "file": "0043.png", + "time": 913626.9402810431 + }, + { + "file": "0044.png", + "time": 913626.9741933261 + }, + { + "file": "0045.png", + "time": 913627.0088141051 + }, + { + "file": "0046.png", + "time": 913627.04069364 + }, + { + "file": "0047.png", + "time": 913627.0738882851 + }, + { + "time": 913627.106783885, + "file": "0048.png" + }, + { + "file": "0049.png", + "time": 913627.142314809 + }, + { + "file": "0050.png", + "time": 913627.1741426891 + }, + { + "file": "0051.png", + "time": 913627.207692999 + }, + { + "time": 913627.2382389171, + "file": "0052.png" + }, + { + "time": 913627.2715684241, + "file": "0053.png" + }, + { + "file": "0054.png", + "time": 913627.3016557251 + }, + { + "file": "0055.png", + "time": 913627.3565241981 + }, + { + "time": 913627.3890422831, + "file": "0056.png" + }, + { + "time": 913627.410564169, + "file": "0057.png" + }, + { + "file": "0058.png", + "time": 913627.44241933 + }, + { + "time": 913627.47227425, + "file": "0059.png" + }, + { + "time": 913627.503578981, + "file": "0060.png" + }, + { + "time": 913627.53656089, + "file": "0061.png" + }, + { + "time": 913627.5706346061, + "file": "0062.png" + }, + { + "time": 913627.6059774631, + "file": "0063.png" + }, + { + "file": "0064.png", + "time": 913627.642300406 + }, + { + "time": 913627.672786911, + "file": "0065.png" + }, + { + "file": "0066.png", + "time": 913627.7059342221 + }, + { + "file": "0067.png", + "time": 913627.7404805991 + }, + { + "file": "0068.png", + "time": 913627.772288126 + }, + { + "file": "0069.png", + "time": 913627.806091586 + }, + { + "file": "0070.png", + "time": 913627.8423069571 + }, + { + "time": 913627.872227921, + "file": "0071.png" + }, + { + "file": "0072.png", + "time": 913627.904815815 + }, + { + "time": 913627.9391412591, + "file": "0073.png" + }, + { + "file": "0074.png", + "time": 913627.974508933 + }, + { + "time": 913628.0064958821, + "file": "0075.png" + }, + { + "time": 913628.0413351611, + "file": "0076.png" + }, + { + "file": "0077.png", + "time": 913628.072915519 + }, + { + "file": "0078.png", + "time": 913628.1060072781 + }, + { + "file": "0079.png", + "time": 913628.141235796 + }, + { + "file": "0080.png", + "time": 913628.1745066891 + }, + { + "file": "0081.png", + "time": 913628.20653584 + }, + { + "file": "0082.png", + "time": 913628.240295661 + }, + { + "time": 913628.274166323, + "file": "0083.png" + }, + { + "time": 913628.306417725, + "file": "0084.png" + }, + { + "file": "0085.png", + "time": 913628.341532737 + }, + { + "time": 913628.3915037521, + "file": "0086.png" + }, + { + "file": "0087.png", + "time": 913628.411066954 + }, + { + "time": 913628.455057331, + "file": "0088.png" + }, + { + "file": "0089.png", + "time": 913628.474613611 + }, + { + "time": 913628.503860494, + "file": "0090.png" + }, + { + "file": "0091.png", + "time": 913628.5366643631 + }, + { + "file": "0092.png", + "time": 913628.573629267 + }, + { + "file": "0093.png", + "time": 913628.604441763 + }, + { + "time": 913628.6594196, + "file": "0094.png" + }, + { + "file": "0095.png", + "time": 913628.6832831871 + }, + { + "file": "0096.png", + "time": 913628.7330405031 + }, + { + "time": 913628.77642887, + "file": "0097.png" + }, + { + "file": "0098.png", + "time": 913628.8043427521 + }, + { + "file": "0099.png", + "time": 913628.840021964 + }, + { + "file": "0100.png", + "time": 913628.8716845771 + }, + { + "time": 913628.905594829, + "file": "0101.png" + }, + { + "file": "0102.png", + "time": 913628.93960048 + }, + { + "file": "0103.png", + "time": 913628.9711876811 + }, + { + "time": 913629.0029976941, + "file": "0104.png" + }, + { + "file": "0105.png", + "time": 913629.0400746231 + }, + { + "file": "0106.png", + "time": 913629.071553146 + }, + { + "file": "0107.png", + "time": 913629.103350113 + }, + { + "file": "0108.png", + "time": 913629.140292271 + }, + { + "time": 913629.168505785, + "file": "0109.png" + }, + { + "time": 913629.207598084, + "file": "0110.png" + }, + { + "file": "0111.png", + "time": 913629.236739716 + }, + { + "time": 913629.272875122, + "file": "0112.png" + }, + { + "file": "0113.png", + "time": 913629.307721958 + }, + { + "file": "0114.png", + "time": 913629.3432565581 + }, + { + "file": "0115.png", + "time": 913629.3731304981 + }, + { + "file": "0116.png", + "time": 913629.407447105 + }, + { + "file": "0117.png", + "time": 913629.4423861081 + }, + { + "time": 913629.477664105, + "file": "0118.png" + } +] \ No newline at end of file diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/manual-settled.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/manual-settled.png new file mode 100644 index 00000000..2685fe46 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/manual-settled.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/manual/0000.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/manual/0000.png new file mode 100644 index 00000000..2c496ec5 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/manual/0000.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/manual/0019.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/manual/0019.png new file mode 100644 index 00000000..f36b8da0 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/manual/0019.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/manual/0029.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/manual/0029.png new file mode 100644 index 00000000..f71706f1 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/manual/0029.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/manual/0039.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/manual/0039.png new file mode 100644 index 00000000..ebc274df Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/manual/0039.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/manual/0042.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/manual/0042.png new file mode 100644 index 00000000..268cc4b1 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/manual/0042.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/manual/0066.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/manual/0066.png new file mode 100644 index 00000000..268cc4b1 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/manual/0066.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/manual/frames.json b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/manual/frames.json new file mode 100644 index 00000000..9f33ec36 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/manual/frames.json @@ -0,0 +1,270 @@ +[ + { + "file": "0000.png", + "time": 913649.4789123321 + }, + { + "file": "0001.png", + "time": 913649.5409981351 + }, + { + "file": "0002.png", + "time": 913649.5719159461 + }, + { + "time": 913649.604001975, + "file": "0003.png" + }, + { + "file": "0004.png", + "time": 913649.637579345 + }, + { + "file": "0005.png", + "time": 913649.6715946691 + }, + { + "time": 913649.7373082781, + "file": "0006.png" + }, + { + "file": "0007.png", + "time": 913649.773161266 + }, + { + "file": "0008.png", + "time": 913649.837708718 + }, + { + "file": "0009.png", + "time": 913649.897244942 + }, + { + "file": "0010.png", + "time": 913649.916900538 + }, + { + "file": "0011.png", + "time": 913650.0185351061 + }, + { + "file": "0012.png", + "time": 913650.040369812 + }, + { + "file": "0013.png", + "time": 913650.0959979381 + }, + { + "file": "0014.png", + "time": 913650.115852643 + }, + { + "time": 913650.1387285291, + "file": "0015.png" + }, + { + "time": 913650.1890770551, + "file": "0016.png" + }, + { + "time": 913650.209703319, + "file": "0017.png" + }, + { + "file": "0018.png", + "time": 913650.257690393 + }, + { + "file": "0019.png", + "time": 913650.2955483911 + }, + { + "file": "0020.png", + "time": 913650.321425457 + }, + { + "file": "0021.png", + "time": 913650.352865361 + }, + { + "time": 913650.3855440071, + "file": "0022.png" + }, + { + "file": "0023.png", + "time": 913650.422998685 + }, + { + "time": 913650.4526552961, + "file": "0024.png" + }, + { + "file": "0025.png", + "time": 913650.485515214 + }, + { + "time": 913650.5055894121, + "file": "0026.png" + }, + { + "file": "0027.png", + "time": 913650.5543494751 + }, + { + "time": 913650.5751487471, + "file": "0028.png" + }, + { + "file": "0029.png", + "time": 913650.620362143 + }, + { + "file": "0030.png", + "time": 913650.640958155 + }, + { + "file": "0031.png", + "time": 913650.6845618271 + }, + { + "time": 913650.7045911091, + "file": "0032.png" + }, + { + "file": "0033.png", + "time": 913650.734892515 + }, + { + "time": 913650.774539203, + "file": "0034.png" + }, + { + "file": "0035.png", + "time": 913650.8017526851 + }, + { + "file": "0036.png", + "time": 913650.8394169301 + }, + { + "file": "0037.png", + "time": 913650.8681549091 + }, + { + "file": "0038.png", + "time": 913650.926757905 + }, + { + "time": 913650.972167773, + "file": "0039.png" + }, + { + "time": 913651.03541228, + "file": "0040.png" + }, + { + "time": 913651.109038961, + "file": "0041.png" + }, + { + "time": 913651.1392424961, + "file": "0042.png" + }, + { + "time": 913651.175997302, + "file": "0043.png" + }, + { + "file": "0044.png", + "time": 913651.207577649 + }, + { + "time": 913651.2412511851, + "file": "0045.png" + }, + { + "file": "0046.png", + "time": 913651.272701774 + }, + { + "file": "0047.png", + "time": 913651.307512223 + }, + { + "file": "0048.png", + "time": 913651.343124047 + }, + { + "time": 913651.3754428921, + "file": "0049.png" + }, + { + "time": 913651.4083072931, + "file": "0050.png" + }, + { + "file": "0051.png", + "time": 913651.441592662 + }, + { + "file": "0052.png", + "time": 913651.4762968901 + }, + { + "file": "0053.png", + "time": 913651.506200829 + }, + { + "time": 913651.5413629961, + "file": "0054.png" + }, + { + "file": "0055.png", + "time": 913651.5741396641 + }, + { + "time": 913651.607966713, + "file": "0056.png" + }, + { + "file": "0057.png", + "time": 913651.640467989 + }, + { + "file": "0058.png", + "time": 913651.675139843 + }, + { + "time": 913651.707729437, + "file": "0059.png" + }, + { + "time": 913651.740942755, + "file": "0060.png" + }, + { + "time": 913651.7733378131, + "file": "0061.png" + }, + { + "time": 913651.805534414, + "file": "0062.png" + }, + { + "file": "0063.png", + "time": 913651.8411757961 + }, + { + "file": "0064.png", + "time": 913651.876669439 + }, + { + "time": 913651.9073031051, + "file": "0065.png" + }, + { + "time": 913651.941832795, + "file": "0066.png" + } +] \ No newline at end of file diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/native-tests.log b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/native-tests.log new file mode 100644 index 00000000..5307450a --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/native-tests.log @@ -0,0 +1,69 @@ +Test Case '-[GestureUITests.SubBannerGestureUITests testAutomaticFiveSecondWrapFrames]' started. + t = 17.06s Synthesize event +Test Case '-[GestureUITests.SubBannerGestureUITests testAutomaticFiveSecondWrapFrames]' passed (17.665 seconds). +Test Case '-[GestureUITests.SubBannerGestureUITests testDirectionsWrapShortDragAndTap]' started. + t = 6.17s Synthesize event + t = 6.54s Press "1 / 3" Button[0.20, 0.50] for 0.1 seconds, then drag to "1 / 3" Button[0.80, 0.50] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds + t = 6.62s Synthesize event + t = 7.74s Synthesize event + t = 8.22s Press "3 / 3" Button[0.80, 0.50] for 0.1 seconds, then drag to "3 / 3" Button[0.20, 0.50] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds + t = 8.30s Synthesize event + t = 9.34s Press "1 / 3" Button[0.50, 0.50] for 0.1 seconds, then drag to "1 / 3" Button[0.41, 0.50] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds + t = 9.42s Synthesize event + t = 10.10s Press "1 / 3" Button[0.50, 0.50] for 0.1 seconds, then drag to "1 / 3" Button[0.46, 0.50] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds + t = 10.18s Synthesize event + t = 10.88s Synthesize event +Test Case '-[GestureUITests.SubBannerGestureUITests testDirectionsWrapShortDragAndTap]' passed (11.506 seconds). +Test Case '-[GestureUITests.SubBannerGestureUITests testDisappearanceDuringAutomaticWrap]' started. + t = 14.44s Synthesize event +Test Case '-[GestureUITests.SubBannerGestureUITests testDisappearanceDuringAutomaticWrap]' passed (16.138 seconds). +Test Case '-[GestureUITests.SubBannerGestureUITests testInactiveSceneCancelsAutomaticAdvance]' started. +Test Case '-[GestureUITests.SubBannerGestureUITests testInactiveSceneCancelsAutomaticAdvance]' passed (12.944 seconds). +Test Case '-[GestureUITests.SubBannerGestureUITests testManualBoundaryFrames]' started. + t = 6.14s Synthesize event + t = 6.46s Press "1 / 3" Button[0.20, 0.50] for 0.1 seconds, then drag to "1 / 3" Button[0.80, 0.50] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds + t = 6.59s Synthesize event + t = 8.96s Synthesize event + t = 9.29s Press "3 / 3" Button[0.80, 0.50] for 0.1 seconds, then drag to "3 / 3" Button[0.20, 0.50] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds + t = 9.42s Synthesize event +Test Case '-[GestureUITests.SubBannerGestureUITests testManualBoundaryFrames]' passed (11.906 seconds). +Test Case '-[GestureUITests.SubBannerGestureUITests testMixedRatiosAndReplacement]' started. + t = 6.18s Press "1 / 3" Button[0.80, 0.50] for 0.1 seconds, then drag to "1 / 3" Button[0.20, 0.50] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds + t = 6.26s Synthesize event + t = 7.32s Press "2 / 3" Button[0.20, 0.50] for 0.1 seconds, then drag to "2 / 3" Button[0.80, 0.50] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds + t = 7.41s Synthesize event + t = 8.39s Press "1 / 3" Button[0.20, 0.50] for 0.1 seconds, then drag to "1 / 3" Button[0.80, 0.50] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds + t = 8.47s Synthesize event + t = 9.63s Synthesize event + t = 10.06s Synthesize event +Test Case '-[GestureUITests.SubBannerGestureUITests testMixedRatiosAndReplacement]' passed (10.653 seconds). +Test Case '-[GestureUITests.SubBannerGestureUITests testReplacementDuringAutomaticWrap]' started. + t = 14.86s Synthesize event +Test Case '-[GestureUITests.SubBannerGestureUITests testReplacementDuringAutomaticWrap]' passed (15.490 seconds). +Test Case '-[GestureUITests.SubBannerGestureUITests testSingleAndEmpty]' started. + t = 6.12s Synthesize event + t = 6.47s Press "1 / 1" Button[0.80, 0.50] for 0.1 seconds, then drag to "1 / 1" Button[0.20, 0.50] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds + t = 6.55s Synthesize event +Test Case '-[GestureUITests.SubBannerGestureUITests testSingleAndEmpty]' passed (12.696 seconds). +Test Case '-[GestureUITests.SubBannerGestureUITests testSwipeDoesNotTap]' started. + t = 6.14s Press "1 / 3" Button[0.80, 0.50] for 0.1 seconds, then drag to "1 / 3" Button[0.20, 0.50] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds + t = 6.23s Synthesize event + t = 7.37s Synthesize event +Test Case '-[GestureUITests.SubBannerGestureUITests testSwipeDoesNotTap]' passed (8.157 seconds). +Test Case '-[GestureUITests.SubBannerGestureUITests testSwipeReleasePositionsAndRepeatedInput]' started. + t = 6.05s Press "1 / 3" Button[0.90, 0.50] for 0.2 seconds, then drag to "1 / 3" Button[0.60, 0.50] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds + t = 6.14s Synthesize event + t = 8.18s Press "2 / 3" Button[0.70, 0.50] for 0.0 seconds, then drag to "2 / 3" Button[0.15, 0.50] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds + t = 8.26s Synthesize event + t = 9.13s Press "3 / 3" Button[0.60, 0.50] for 0.1 seconds, then drag to "3 / 3" Button[0.10, 0.50] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds + t = 9.21s Synthesize event + t = 10.32s Synthesize event +Test Case '-[GestureUITests.SubBannerGestureUITests testSwipeReleasePositionsAndRepeatedInput]' passed (10.950 seconds). +Test Case '-[GestureUITests.SubBannerGestureUITests testVerticalParentScrollDoesNotTap]' started. + t = 6.10s Press "1 / 3" Button[0.50, 0.90] for 0.1 seconds, then drag to "1 / 3" Button[0.50, 0.10] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds + t = 6.18s Synthesize event +Test Case '-[GestureUITests.SubBannerGestureUITests testVerticalParentScrollDoesNotTap]' passed (9.099 seconds). + Executed 11 tests, with 0 failures (0 unexpected) in 137.204 (137.216) seconds + Executed 11 tests, with 0 failures (0 unexpected) in 137.204 (137.217) seconds + Executed 11 tests, with 0 failures (0 unexpected) in 137.204 (137.219) seconds +** TEST SUCCEEDED ** diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/production-SubBannerCarousel.swift b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/production-SubBannerCarousel.swift new file mode 100644 index 00000000..7cc56abb --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/production-SubBannerCarousel.swift @@ -0,0 +1,181 @@ +import SwiftUI + +import Kingfisher + +struct SubBannerCarousel: View { + let items: [SubBanner] + let onTap: (SubBanner) -> Void + + @Environment(\.scenePhase) private var scenePhase + @State private var displayIndex = 1 + @State private var itemsRevision = 0 + @State private var containerWidth: CGFloat = 0 + @State private var imageRatios: [String: CGFloat] = [:] + @State private var lastKnownRatio: CGFloat? + @GestureState private var isSwiping = false + @GestureState private var dragOffset: CGFloat = 0 + + private static let autoScrollNanoseconds: UInt64 = 5_000_000_000 + private static let transitionDuration: TimeInterval = 0.35 + + private var safeDisplayIndex: Int { + items.count > 1 ? min(max(displayIndex, 0), items.count + 1) : 0 + } + + private var safeIndex: Int { + items.count > 1 ? (safeDisplayIndex + items.count - 1) % items.count : 0 + } + + private var pages: [(id: String, item: SubBanner)] { + let originals = items.map { (id: "item-\($0.id)", item: $0) } + guard items.count > 1, let first = items.first, let last = items.last else { return originals } + return [(id: "leading-\(last.id)", item: last)] + originals + [(id: "trailing-\(first.id)", item: first)] + } + + private var currentRatio: CGFloat? { + guard !items.isEmpty else { return nil } + return imageRatios[items[safeIndex].imageUrl] + } + + private var height: CGFloat { + guard !items.isEmpty, let ratio = currentRatio ?? lastKnownRatio else { return 0 } + return containerWidth / ratio + } + + var body: some View { + GeometryReader { geometry in + HStack(spacing: 0) { + ForEach(pages, id: \.id) { page in + Button { + onTap(page.item) + } label: { + KFImage(page.item.imageURL) + .placeholder { Color.gray800 } + .onSuccess { result in + let size = result.image.size + guard size.width > 0, size.height > 0 else { return } + imageRatios[page.item.imageUrl] = size.width / size.height + } + .startLoadingBeforeViewAppear() + .resizable() + .scaledToFit() + .frame(width: geometry.size.width, height: height) + .background(Color.gray800) + } + .buttonStyle(SubBannerTapStyle(drag: dragGesture(width: geometry.size.width))) + .accessibilityLabel(Text("\(safeIndex + 1) / \(items.count)")) + .accessibilityHidden(pages[safeDisplayIndex].id != page.id) + } + } + .offset(x: -CGFloat(safeDisplayIndex) * geometry.size.width + dragOffset) + .animation(.easeOut(duration: Self.transitionDuration), value: displayIndex) + .frame(width: geometry.size.width, height: height, alignment: .leading) + .contentShape(Rectangle()) + .clipped() + .overlay(alignment: .topTrailing) { + if !items.isEmpty && height > 0 { + pageIndicator + .padding(SodaSpacing.s14) + } + } + .onAppear { containerWidth = geometry.size.width } + .onChange(of: geometry.size.width) { containerWidth = $0 } + } + .frame(height: height) + .onChange(of: currentRatio) { ratio in + if let ratio { lastKnownRatio = ratio } + } + .onChange(of: items) { _ in + itemsRevision += 1 + normalize(to: items.count > 1 ? 1 : 0) + if items.isEmpty { lastKnownRatio = nil } + } + .onDisappear { + itemsRevision += 1 + normalize(to: items.count > 1 ? safeIndex + 1 : 0) + } + .task(id: scenePhase == .active && items.count > 1 ? items : []) { + guard scenePhase == .active, items.count > 1 else { return } + while !Task.isCancelled { + do { + try await Task.sleep(nanoseconds: Self.autoScrollNanoseconds) + } catch { + return + } + guard !Task.isCancelled else { return } + if !isSwiping { move(by: 1) } + } + } + .accessibilityAdjustableAction { direction in + switch direction { + case .increment: move(by: 1) + case .decrement: move(by: -1) + @unknown default: break + } + } + } + + private var pageIndicator: some View { + HStack(spacing: 0) { + Text(String(format: "%02d", safeIndex + 1)) + .foregroundColor(.white) + Text("/" + String(format: "%02d", items.count)) + .foregroundColor(.gray400) + } + .appFont(.body3) + .lineLimit(1) + .fixedSize(horizontal: true, vertical: false) + .padding(.horizontal, SodaSpacing.s6) + .padding(.vertical, SodaSpacing.s4) + .background(Color.gray900) + .clipShape(Capsule()) + .accessibilityHidden(true) + } + + private func dragGesture(width: CGFloat) -> some Gesture { + DragGesture(minimumDistance: SodaSpacing.s12, coordinateSpace: .global) + .updating($dragOffset) { value, offset, _ in + if items.count > 1, abs(value.translation.width) > abs(value.translation.height) { + offset = value.translation.width + } + } + .updating($isSwiping) { _, swiping, _ in swiping = true } + .onEnded { value in + guard abs(value.translation.width) > abs(value.translation.height) else { return } + if value.translation.width < -width * 0.2 { + move(by: 1) + } else if value.translation.width > width * 0.2 { + move(by: -1) + } + } + } + + private func move(by step: Int) { + guard items.count > 1, (1...items.count).contains(displayIndex) else { return } + let next = displayIndex + step + displayIndex = next + guard next == 0 || next == items.count + 1 else { return } + let revision = itemsRevision + DispatchQueue.main.asyncAfter(deadline: .now() + Self.transitionDuration) { + guard itemsRevision == revision, displayIndex == next else { return } + normalize(to: next == 0 ? items.count : 1) + } + } + + private func normalize(to index: Int) { + var transaction = Transaction() + transaction.disablesAnimations = true + withTransaction(transaction) { displayIndex = index } + } +} + +private struct SubBannerTapStyle: PrimitiveButtonStyle { + let drag: Drag + + func makeBody(configuration: Configuration) -> some View { + configuration.label + .contentShape(Rectangle()) + .simultaneousGesture(drag.exclusively(before: TapGesture().onEnded { configuration.trigger() })) + .accessibilityAction { configuration.trigger() } + } +} diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/replace/0000.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/replace/0000.png new file mode 100644 index 00000000..ad35d71f Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/replace/0000.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/replace/0050.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/replace/0050.png new file mode 100644 index 00000000..203e546d Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/replace/0050.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/replace/0053.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/replace/0053.png new file mode 100644 index 00000000..37c1754b Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/replace/0053.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/replace/0115.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/replace/0115.png new file mode 100644 index 00000000..80aaa062 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/replace/0115.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/replace/frames.json b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/replace/frames.json new file mode 100644 index 00000000..9edde2bd --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/replace/frames.json @@ -0,0 +1,466 @@ +[ + { + "time": 913677.182000758, + "file": "0000.png" + }, + { + "file": "0001.png", + "time": 913677.208739417 + }, + { + "time": 913677.243329994, + "file": "0002.png" + }, + { + "file": "0003.png", + "time": 913677.2766025431 + }, + { + "time": 913677.3236773841, + "file": "0004.png" + }, + { + "time": 913677.364179086, + "file": "0005.png" + }, + { + "file": "0006.png", + "time": 913677.385782319 + }, + { + "time": 913677.4096495911, + "file": "0007.png" + }, + { + "file": "0008.png", + "time": 913677.4411729571 + }, + { + "time": 913677.4755007031, + "file": "0009.png" + }, + { + "time": 913677.507597494, + "file": "0010.png" + }, + { + "time": 913677.541143598, + "file": "0011.png" + }, + { + "time": 913677.575468246, + "file": "0012.png" + }, + { + "time": 913677.607664886, + "file": "0013.png" + }, + { + "file": "0014.png", + "time": 913677.63882929 + }, + { + "file": "0015.png", + "time": 913677.670936167 + }, + { + "time": 913677.704879379, + "file": "0016.png" + }, + { + "file": "0017.png", + "time": 913677.7407021441 + }, + { + "time": 913677.770956119, + "file": "0018.png" + }, + { + "file": "0019.png", + "time": 913677.805286434 + }, + { + "file": "0020.png", + "time": 913677.840920433 + }, + { + "time": 913677.8730100831, + "file": "0021.png" + }, + { + "time": 913677.9581180731, + "file": "0022.png" + }, + { + "file": "0023.png", + "time": 913678.00875871 + }, + { + "time": 913678.0434123791, + "file": "0024.png" + }, + { + "file": "0025.png", + "time": 913678.073637673 + }, + { + "time": 913678.1048476781, + "file": "0026.png" + }, + { + "file": "0027.png", + "time": 913678.143166751 + }, + { + "file": "0028.png", + "time": 913678.1782429271 + }, + { + "file": "0029.png", + "time": 913678.2079582481 + }, + { + "file": "0030.png", + "time": 913678.239294811 + }, + { + "time": 913678.272196469, + "file": "0031.png" + }, + { + "time": 913678.306947525, + "file": "0032.png" + }, + { + "file": "0033.png", + "time": 913678.3415255671 + }, + { + "file": "0034.png", + "time": 913678.372522235 + }, + { + "time": 913678.407802038, + "file": "0035.png" + }, + { + "time": 913678.440060911, + "file": "0036.png" + }, + { + "file": "0037.png", + "time": 913678.4746635171 + }, + { + "time": 913678.504926466, + "file": "0038.png" + }, + { + "time": 913678.540071622, + "file": "0039.png" + }, + { + "file": "0040.png", + "time": 913678.57196552 + }, + { + "file": "0041.png", + "time": 913678.608095773 + }, + { + "file": "0042.png", + "time": 913678.640771551 + }, + { + "file": "0043.png", + "time": 913678.674382659 + }, + { + "file": "0044.png", + "time": 913678.705199606 + }, + { + "time": 913678.740146241, + "file": "0045.png" + }, + { + "time": 913678.771469746, + "file": "0046.png" + }, + { + "file": "0047.png", + "time": 913678.8079382931 + }, + { + "time": 913678.837268878, + "file": "0048.png" + }, + { + "time": 913678.8705325951, + "file": "0049.png" + }, + { + "time": 913678.957523811, + "file": "0050.png" + }, + { + "file": "0051.png", + "time": 913679.0235937741 + }, + { + "time": 913679.0449015361, + "file": "0052.png" + }, + { + "file": "0053.png", + "time": 913679.0738721241 + }, + { + "file": "0054.png", + "time": 913679.1065804161 + }, + { + "file": "0055.png", + "time": 913679.140883742 + }, + { + "file": "0056.png", + "time": 913679.173805641 + }, + { + "time": 913679.209024106, + "file": "0057.png" + }, + { + "file": "0058.png", + "time": 913679.2407425691 + }, + { + "time": 913679.2749094791, + "file": "0059.png" + }, + { + "time": 913679.307120681, + "file": "0060.png" + }, + { + "file": "0061.png", + "time": 913679.3397686051 + }, + { + "time": 913679.3735209911, + "file": "0062.png" + }, + { + "file": "0063.png", + "time": 913679.407494329 + }, + { + "time": 913679.4407483081, + "file": "0064.png" + }, + { + "time": 913679.4731188971, + "file": "0065.png" + }, + { + "time": 913679.5077481561, + "file": "0066.png" + }, + { + "file": "0067.png", + "time": 913679.557752116 + }, + { + "time": 913679.5799938161, + "file": "0068.png" + }, + { + "file": "0069.png", + "time": 913679.6070735101 + }, + { + "file": "0070.png", + "time": 913679.640295565 + }, + { + "time": 913679.6770715461, + "file": "0071.png" + }, + { + "file": "0072.png", + "time": 913679.707296167 + }, + { + "file": "0073.png", + "time": 913679.740801326 + }, + { + "file": "0074.png", + "time": 913679.7748053831 + }, + { + "time": 913679.8080684431, + "file": "0075.png" + }, + { + "file": "0076.png", + "time": 913679.8412805101 + }, + { + "file": "0077.png", + "time": 913679.875727502 + }, + { + "file": "0078.png", + "time": 913679.9071129421 + }, + { + "file": "0079.png", + "time": 913679.9597379351 + }, + { + "time": 913679.97902312, + "file": "0080.png" + }, + { + "file": "0081.png", + "time": 913680.0047622471 + }, + { + "file": "0082.png", + "time": 913680.042046424 + }, + { + "file": "0083.png", + "time": 913680.0745555841 + }, + { + "file": "0084.png", + "time": 913680.1082944991 + }, + { + "file": "0085.png", + "time": 913680.142798252 + }, + { + "time": 913680.173877244, + "file": "0086.png" + }, + { + "time": 913680.2076699181, + "file": "0087.png" + }, + { + "time": 913680.240965635, + "file": "0088.png" + }, + { + "time": 913680.276695547, + "file": "0089.png" + }, + { + "file": "0090.png", + "time": 913680.306939149 + }, + { + "file": "0091.png", + "time": 913680.339200414 + }, + { + "file": "0092.png", + "time": 913680.3754256241 + }, + { + "file": "0093.png", + "time": 913680.40727143 + }, + { + "time": 913680.4413317901, + "file": "0094.png" + }, + { + "file": "0095.png", + "time": 913680.4747653401 + }, + { + "time": 913680.5317274261, + "file": "0096.png" + }, + { + "file": "0097.png", + "time": 913680.5543979681 + }, + { + "file": "0098.png", + "time": 913680.5904828061 + }, + { + "file": "0099.png", + "time": 913680.6107395231 + }, + { + "time": 913680.6469458761, + "file": "0100.png" + }, + { + "time": 913680.6707490981, + "file": "0101.png" + }, + { + "file": "0102.png", + "time": 913680.724500303 + }, + { + "time": 913680.7592690381, + "file": "0103.png" + }, + { + "file": "0104.png", + "time": 913680.7851261741 + }, + { + "file": "0105.png", + "time": 913680.805062163 + }, + { + "file": "0106.png", + "time": 913680.8376410251 + }, + { + "time": 913680.8706479521, + "file": "0107.png" + }, + { + "file": "0108.png", + "time": 913680.9038749371 + }, + { + "file": "0109.png", + "time": 913680.9381074051 + }, + { + "file": "0110.png", + "time": 913680.987470016 + }, + { + "file": "0111.png", + "time": 913681.0077511901 + }, + { + "file": "0112.png", + "time": 913681.0417480101 + }, + { + "time": 913681.0748786661, + "file": "0113.png" + }, + { + "time": 913681.107035094, + "file": "0114.png" + }, + { + "file": "0115.png", + "time": 913681.1391082171 + } +] \ No newline at end of file diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/test-summary.json b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/test-summary.json new file mode 100644 index 00000000..e1bbac0d --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/test-summary.json @@ -0,0 +1,42 @@ +{ + "devicesAndConfigurations" : [ + { + "device" : { + "architecture" : "x86_64", + "deviceId" : "B5F2BA0D-327C-4EE3-94EE-39710A0E1ACC", + "deviceName" : "SubBannerGestureQA-20261030", + "modelName" : "iPhone SE (3rd generation)", + "osBuildNumber" : "22D8075", + "osVersion" : "18.3.1", + "platform" : "iOS Simulator" + }, + "expectedFailures" : 0, + "failedTests" : 0, + "passedTests" : 11, + "skippedTests" : 0, + "testPlanConfiguration" : { + "configurationId" : "1", + "configurationName" : "Test Scheme Action" + } + } + ], + "environmentDescription" : "Gesture · Built with macOS 26.0", + "expectedFailures" : 0, + "failedTests" : 0, + "finishTime" : 1793292296.135, + "passedTests" : 11, + "result" : "Passed", + "skippedTests" : 0, + "startTime" : 1793292150.956, + "statistics" : [ + + ], + "testFailures" : [ + + ], + "title" : "Test - Gesture", + "topInsights" : [ + + ], + "totalTestCount" : 11 +} diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/ui-typecheck.log b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/ui-typecheck.log new file mode 100644 index 00000000..00c26b40 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/ui-typecheck.log @@ -0,0 +1 @@ +PASS: production carousel/model/UIKit opener iOS16.6 typecheck with installed Kingfisher and real theme/font files diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/vertical-scroll.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/vertical-scroll.png new file mode 100644 index 00000000..9b503f70 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/green/vertical-scroll.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/implementation.md b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/implementation.md new file mode 100644 index 00000000..b3957b92 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/implementation.md @@ -0,0 +1,97 @@ +# P3-T1: 실제 터치 오탭과 경계 순환 수정 + +## 결과와 범위 +- 제품 변경은 `SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift` 한 파일이다. 기존 main `BannerCarousel`, 두 호출부, 모델/API/opener와 production project는 수정하지 않았다. +- 실제 XCTest 좌표 터치 RED를 제품 변경 전에 확인했다. 최종 독립 native app/UI-test target은 **11 tests, 0 failures**, `xcodebuild` exit 0이다. 소스 문자열이나 상태 직접 호출을 제스처 증거로 사용하지 않았다. +- `check-sub-banners.sh`와 `--ui` 모두 exit 0이다. 테스트용 앱, runner와 소유 root/cache를 정리했고 빌려 쓴 simulator와 parent root는 유지했다. + +## 원인과 최소 수정 +1. 기본 Button과 simultaneous drag의 경합에서 `@GestureState isSwiping`이 release 시 초기화되어 이전 배너 action이 실행됐다. `PrimitiveButtonStyle` 안에서 `drag.exclusively(before: TapGesture())`로 상호 배제한다. 외부 부모 ScrollView에는 simultaneous로 참여하고 접근성 activation은 `configuration.trigger()`를 유지한다. 고정 시간 탭 억제나 high-priority gesture를 사용하지 않았다. +2. 인식기를 이동하는 라벨에 붙인 뒤 로컬 좌표로는 112pt 이동이 페이지 임계값에 못 미치는 실제 회귀가 발생했다. `.global` 좌표계로 바꾼 같은 테스트가 통과했다. 17pt·34pt의 인식된 짧은 드래그는 tap 없이 제자리를 유지한다. +3. 실제 경계 이동에서 애니메이션을 끄던 분기를 제거했다. `[last] + originals + [first]`에 별도 clone identity와 display index를 부여하고 0.35초 같은 방향 전환 후 같은 이미지 위치로만 무애니메이션 정렬한다. 정렬 callback은 revision/index로 보호하며 목록 교체·disappear가 revision을 무효화한다. 경계 정렬 전 display index를 더 넘기지 않는다. + +API, 전체 목록, 색상/간격/font tokens, 링크 전달, 실제 페이지 수, URL별 원본 비율, 5초 간격과 scene task cancellation은 유지했다. `isSwiping`은 자동 전환 보류에만 쓰며 tap 판정에 사용하지 않는다. + +## 하네스와 실행 환경 +- 도구: Xcode 26.0 / Swift 5 mode, x86_64 simulator, Ruby `xcodeproj` 1.27.0. 별도 의존성 설치 없음. +- 사용자 소유 기기: `B5F2BA0D-327C-4EE3-94EE-39710A0E1ACC`, iPhone SE 3, 실행 결과의 OS 18.3.1 / 22D8075. 모든 run은 `-parallel-testing-enabled NO`, 별도 device 생성 없음. +- 자체 root: `/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-touch.n2JrMa` (검증 후 삭제). +- 앱은 현재 production component/model과 실제 `Color.swift`, `Spacing.swift`, `ColorExtension.swift`, `FontModifier.swift`를 원본 경로에서 컴파일한다. 독립 앱에 필요한 `LanguageOption` enum만 테스트 대역이다. +- 기존 `/Users/klaus/Library/Developer/Xcode/DerivedData/SodaLive-clzdtvqrsfonoxcijatzkrqdzrsu/Build/Products/Debug-iphonesimulator/Kingfisher.o`와 module을 읽기 전용으로 링크한다. 생성 이미지 3장을 실제 Kingfisher memory cache에 미리 저장한다. 모든 downloader 요청은 로컬 URLProtocol에서 차단하므로 live request가 없다. +- 부모 ScrollView 안에서 actual component를 렌더한다. `XCUIApplication`, `coordinate.press(forDuration:thenDragTo:)`, `.tap()`으로 simulator 입력을 보낸다. 화면의 callback 횟수/마지막 ID와 기존 버튼 접근성 label을 assert한다. 입력 전용 scenario는 harness environment만 `.inactive`; 자동/백그라운드 scenario는 실제 scene phase다. +- `work/tests/run-sub-banner-gesture-tests.sh`만 명시적으로 native tests를 실행한다. 기존 `check-*.sh` 자동 검사에는 추가하지 않았다. runner가 출력한 root는 호출자가 증거 추출 후 정리하는 방식이다. + +## 실행 명령과 RED/GREEN + +제품 수정 전 최초 RED (runner가 위 root를 생성): +```bash +bash work/tests/run-sub-banner-gesture-tests.sh \ + -only-testing:GestureUITests/SubBannerGestureUITests/testSwipeDoesNotTap \ + -only-testing:GestureUITests/SubBannerGestureUITests/testManualBoundaryFrames \ + -only-testing:GestureUITests/SubBannerGestureUITests/testAutomaticFiveSecondWrapFrames +``` +**결과:** exit 65. 좌표 drag 뒤 페이지 2 또는 3 도달 assertion은 성공했으나 `taps=1;last=1`이 `taps=0;last=0`과 달라 두 테스트가 실패했다. [native-tests.log](red/native-tests.log), [test-summary.json](red/test-summary.json). 빌드/도구 오류를 RED로 세지 않았다. + +최종 GREEN: +```bash +SB_RUN_ROOT=/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-touch.n2JrMa \ +SB_RUN_NAME=final-confirmed bash work/tests/run-sub-banner-gesture-tests.sh +``` +**결과:** exit 0, 11/11 PASS, 테스트 수행 137.219초. [native-tests.log](green/native-tests.log), [test-summary.json](green/test-summary.json). + +| 실제 실행 시나리오 | 확인 결과 | +|---|---| +| 정상 tap, left/right, 양방향 wrap, 직후 tap | drag callback 0, 현재 ID를 tap하면 정확히 1회. clone의 접근성 버튼 중복 없음 | +| 17pt·34pt subthreshold drag, 112pt 이동, 여러 release 위치/hold duration, 연속 입력 | 짧은 drag는 callback/페이지 이동 없음. 충분한 이동과 반복 좌우·wrap 입력은 기대 페이지 도달 | +| 배너 위에서 세로 drag | callback 0, 부모 콘텐츠 row의 실제 화면 Y가 30pt 이상 감소. [vertical-scroll.png](green/vertical-scroll.png) | +| single / empty / 2:1 및 3:1 ratio / 목록 교체 | single tap 유지, drag callback 없음, empty 높이/불필요 영역 없음, 원본 비율 프레임 확인, 교체 항목 ID 9 tap | +| 실제 두 번의 5초 전환 / scene inactive / delayed wrap 중 replace·hide | 자동 1→2→1, 실제 Home 버튼으로 background 6초 후 같은 페이지, 중간 프레임에서 교체·이탈 후 정상 tap/재표시 | + +집중 검사 (각 명령에 `TMPDIR=$SB_RUN_ROOT/tmp`, `NODE_COMPILE_CACHE=$SB_RUN_ROOT/node`, `BUN_RUNTIME_TRANSPILER_CACHE_PATH=$SB_RUN_ROOT/bun` 지정): +```bash +bash work/tests/check-sub-banners.sh +bash work/tests/check-sub-banners.sh --ui +``` +두 명령 exit 0. [focused.log](green/focused.log), [ui-typecheck.log](green/ui-typecheck.log). `--ui`는 실제 Kingfisher와 theme/font를 사용하는 iOS16.6 target typecheck다. + +## 경계 애니메이션: 실제 렌더 프레임 +`CADisplayLink` 30fps에서 실제 UIWindow `drawHierarchy` PNG를 저장했다. 375×667 PNG signature/크기를 확인했고 보존한 중간/종료 이미지를 직접 열어 확인했다. 최종 전체 캡처는 자동 116, reverse 67, forward 70, replace 116, hide 119 frames다. 보존 PNG는 각 시퀀스의 시작/중간/종료 일부이며 모든 샘플의 timestamp·색 픽셀 수는 [frame-analysis.json](frame-analysis.json)에 있다. 전체 frame manifest에는 보존하지 않은 샘플 이름도 포함한다. + +| 증거 | 관찰 | +|---|---| +| 원본 자동 경계 [0049](red/automatic/0049.png) → [0050](red/automatic/0050.png) | 117 samples 중 양 이미지가 섞인 프레임 0. 약 **31.9ms** 사이에 green 359px → red 359px로 통째 교체 | +| 수정 자동 경계 [0063](green/automatic/0063.png) | 10개 중간 프레임. red가 오른쪽에서 들어오며 green을 왼쪽으로 밀어냄 | +| 수정 수동 reverse [0029](green/manual/0029.png), forward [0032](green/forward/0032.png) | 각각 21/22개 중간 프레임. 들어오는 이미지 영역이 같은 방향으로 단조 증가. 3개 목록에서 중간 green 배너를 역주행하지 않음 | +| replace/hide [events.json](green/events.json) | 실제 red+green 중간 PNG를 감지한 뒤 각각 **1.42ms / 1.00ms**에 목록 교체/제거 실행. 고정 시각을 전환 중 증거로 간주하지 않음 | +| 정렬 후 종료 프레임 | 자동/양방향 수동 모두 마지막 5개 배너 영역 hash 동일. [manual-settled.png](green/manual-settled.png) | + +초기 좁은 자동 캡처 창은 전환이 끝난 뒤만 잡았으므로 경계 RED로 쓰지 않았다. 원본 snapshot을 별도 project source로 명시해 8~12초 창으로 재실행하여 위 경계 RED를 확보했다. production은 되돌리거나 다시 편집하지 않았다: +```bash +SB_RUN_ROOT=/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-touch.n2JrMa \ +SB_RUN_NAME=red-wide-window \ +SB_CAROUSEL_SOURCE="$PWD/docs/20261029_추천_서브_배너/reviews/evidence/native-final/production-source-snapshot/SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift" \ +bash work/tests/run-sub-banner-gesture-tests.sh \ + -only-testing:GestureUITests/SubBannerGestureUITests/testAutomaticFiveSecondWrapFrames +``` +이 테스트는 순환 도달만 assert하므로 원본에서도 exit 0이다. **애니메이션 RED는 위 연속 렌더 샘플의 직접 교체 현상**이며 테스트 PASS를 애니메이션 PASS로 오인하지 않는다. [baseline-replay.log](red/baseline-replay.log)에는 같은 snapshot의 오탭 재실행 실패도 포함한다. + +## 시행 중 발견하고 해결한 검증 문제 +- tap recognizer만 둔 첫 시도는 34pt drag에서 callback이 발생하여 폐기했다. exclusive recognition으로 해결했다. +- exclusive 인식기를 라벨로 옮긴 뒤 로컬 좌표 112pt 이동 회귀를 실제 입력에서 확인했다. 2초 대기 후에도 실패했으며 전역 좌표 수정으로 동일 테스트가 통과했다. +- 부모 scroll의 초기 preference 측정은 실제 화면이 스크롤되어도 0이었다. 실제 row frame 변위를 assert하도록 수정했다. UI-test의 element/query `.count` 컴파일 오류 1건도 수정했으며 이것은 제품 RED가 아니다. +- 캡처가 중단되어도 오래된 manifest를 사용하지 않도록 매 frame 현재 metadata를 저장했다. 좁은 창 누락과 움직임이 거의 끝난 프레임의 hash 차이를 제품 결함으로 세지 않고, 실제 양 끝 이미지 존재와 완전 정착 후 hash를 검사했다. + +## 소스 동결과 정리 +| 단계 | SubBannerCarousel SHA-256 | +|---|---| +| 제품 수정 전 native touch RED 및 원본 replay compiled source | `7e25db3076d545ffc61ea3a1c5585e7b8a617964ef406d7570286d75a75e010a` | +| 최종 native 실행 직전 / 직후 / compiled source / cleanup 후 | `b3e4c5333cf8249ccd7773b5cabd372efb5a5565323bd7778034e110386151fd` | + +[final-confirmed-before.sha256](green/final-confirmed-before.sha256), [after](green/final-confirmed-after.sha256), [compiled](green/final-confirmed-compiled.sha256), [production snapshot](green/production-SubBannerCarousel.swift), [harness.sha256](green/harness.sha256). 최종 native 증거 이후 production 편집 없음. 현 소스와 snapshot `cmp` exit 0. + +[owned-processes.json](owned-processes.json)에 소유 앱 ID와 관찰된 app PID를 기록했다. runner PID 숫자는 xcodebuild 로그에 제공되지 않아 미확인으로 구분한다. 종료 시 launchctl의 해당 app/runner job은 이미 없었고, 두 앱 uninstall exit 0, 소유 root/cache 부재, parent simulator 여전히 Booted, `sub-banner-gesture.Fyv2wD` 존재를 확인했다. [cleanup.json](cleanup.json). + +## 미실행 범위 +- 전체 main-app build, 전체 16개 check suite, 실제 서비스/외부 링크 launch, 실기기는 이번 범위 밖이다. 기존 native/native-final evidence는 수정하지 않았다. +- iOS16.6은 typecheck이며 실제 touch는 iOS18.3.1 simulator 한 기기다. VoiceOver/Switch Control/hardware keyboard 자체 조작, Reduce Motion, 동시 다중 터치는 실행하지 않았다. 접근성 Button 노출과 action은 유지했지만 전체 보조공학 검증으로 확대 주장하지 않는다. +- LSP 도구는 없으며 native compile/test 및 `--ui` typecheck로 대체했다. 독립 리뷰/추가 agent는 사용자 명시 금지에 따라 실행하지 않았다. commit/push/worktree/의존성 설치 없음. diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/owned-processes.json b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/owned-processes.json new file mode 100644 index 00000000..171f5d28 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/owned-processes.json @@ -0,0 +1,97 @@ +{ + "appIDs": [ + "invalid.sodalive.gesturefix.Gesture", + "invalid.sodalive.gesturefix.GestureUITests.xctrunner" + ], + "observedAppPIDs": [ + 21851, + 21916, + 23640, + 23701, + 24930, + 25062, + 25139, + 25194, + 25284, + 25315, + 25378, + 25425, + 25464, + 25511, + 26259, + 26350, + 26454, + 27649, + 27725, + 27783, + 27870, + 27924, + 28018, + 28049, + 28115, + 28139, + 28175, + 28221, + 28244, + 29139, + 29652, + 29722, + 29797, + 29837, + 30488, + 30568, + 30662, + 30730, + 30838, + 30891, + 31026, + 31226, + 31274, + 31298, + 31330, + 31376, + 32123, + 32169, + 32253, + 32294, + 32337, + 32375, + 32435, + 32479, + 32503, + 32525, + 32574, + 32610, + 33668, + 33711, + 33727, + 34228, + 34695, + 34735, + 34767, + 34805, + 34837, + 34870, + 34894, + 34933, + 34951, + 34968, + 34991, + 35015 + ], + "finalAppPIDs": [ + 34695, + 34735, + 34767, + 34805, + 34837, + 34870, + 34894, + 34933, + 34951, + 34968, + 34991, + 35015 + ], + "note": "XCTest runner managed and exited by xcodebuild; its numeric PID is not emitted in logs. No owned launchctl jobs remain before teardown." +} \ No newline at end of file diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/parent-test-summary.json b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/parent-test-summary.json new file mode 100644 index 00000000..aeabfc4f --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/parent-test-summary.json @@ -0,0 +1,42 @@ +{ + "devicesAndConfigurations" : [ + { + "device" : { + "architecture" : "x86_64", + "deviceId" : "B5F2BA0D-327C-4EE3-94EE-39710A0E1ACC", + "deviceName" : "SubBannerGestureQA-20261030", + "modelName" : "iPhone SE (3rd generation)", + "osBuildNumber" : "22D8075", + "osVersion" : "18.3.1", + "platform" : "iOS Simulator" + }, + "expectedFailures" : 0, + "failedTests" : 0, + "passedTests" : 11, + "skippedTests" : 0, + "testPlanConfiguration" : { + "configurationId" : "1", + "configurationName" : "Test Scheme Action" + } + } + ], + "environmentDescription" : "Gesture · Built with macOS 26.0", + "expectedFailures" : 0, + "failedTests" : 0, + "finishTime" : 1793293222.713, + "passedTests" : 11, + "result" : "Passed", + "skippedTests" : 0, + "startTime" : 1793293069.964, + "statistics" : [ + + ], + "testFailures" : [ + + ], + "title" : "Test - Gesture", + "topInsights" : [ + + ], + "totalTestCount" : 11 +} diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/automatic/0000.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/automatic/0000.png new file mode 100644 index 00000000..ad35d71f Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/automatic/0000.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/automatic/0049.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/automatic/0049.png new file mode 100644 index 00000000..ad35d71f Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/automatic/0049.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/automatic/0050.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/automatic/0050.png new file mode 100644 index 00000000..a170a290 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/automatic/0050.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/automatic/0116.png b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/automatic/0116.png new file mode 100644 index 00000000..a170a290 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/automatic/0116.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/automatic/frames.json b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/automatic/frames.json new file mode 100644 index 00000000..d8cfaa4a --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/automatic/frames.json @@ -0,0 +1,470 @@ +[ + { + "file" : "0000.png", + "time" : 913495.54207174003 + }, + { + "file" : "0001.png", + "time" : 913495.5738208961 + }, + { + "file" : "0002.png", + "time" : 913495.61067922611 + }, + { + "time" : 913495.64156974305, + "file" : "0003.png" + }, + { + "time" : 913495.6749411471, + "file" : "0004.png" + }, + { + "file" : "0005.png", + "time" : 913495.70521654701 + }, + { + "time" : 913495.73877406307, + "file" : "0006.png" + }, + { + "file" : "0007.png", + "time" : 913495.7714096501 + }, + { + "time" : 913495.80537242105, + "file" : "0008.png" + }, + { + "time" : 913495.84002024506, + "file" : "0009.png" + }, + { + "file" : "0010.png", + "time" : 913495.87342155003 + }, + { + "file" : "0011.png", + "time" : 913495.91759019205 + }, + { + "time" : 913495.93913621211, + "file" : "0012.png" + }, + { + "file" : "0013.png", + "time" : 913495.9718883941 + }, + { + "time" : 913496.01687527308, + "file" : "0014.png" + }, + { + "file" : "0015.png", + "time" : 913496.04254880711 + }, + { + "file" : "0016.png", + "time" : 913496.07467698609 + }, + { + "file" : "0017.png", + "time" : 913496.10751351411 + }, + { + "file" : "0018.png", + "time" : 913496.14147810102 + }, + { + "time" : 913496.172093114, + "file" : "0019.png" + }, + { + "file" : "0020.png", + "time" : 913496.20843185706 + }, + { + "time" : 913496.2405724331, + "file" : "0021.png" + }, + { + "time" : 913496.27334535203, + "file" : "0022.png" + }, + { + "time" : 913496.30913038005, + "file" : "0023.png" + }, + { + "file" : "0024.png", + "time" : 913496.34009797906 + }, + { + "file" : "0025.png", + "time" : 913496.37281471805 + }, + { + "file" : "0026.png", + "time" : 913496.455701945 + }, + { + "time" : 913496.50706537208, + "file" : "0027.png" + }, + { + "file" : "0028.png", + "time" : 913496.54125389305 + }, + { + "file" : "0029.png", + "time" : 913496.5738092541 + }, + { + "file" : "0030.png", + "time" : 913496.6069761751 + }, + { + "file" : "0031.png", + "time" : 913496.64345990401 + }, + { + "file" : "0032.png", + "time" : 913496.67478215508 + }, + { + "file" : "0033.png", + "time" : 913496.70709522709 + }, + { + "file" : "0034.png", + "time" : 913496.74192454701 + }, + { + "file" : "0035.png", + "time" : 913496.77307740704 + }, + { + "time" : 913496.80924846802, + "file" : "0036.png" + }, + { + "file" : "0037.png", + "time" : 913496.84352450003 + }, + { + "file" : "0038.png", + "time" : 913496.87277250108 + }, + { + "file" : "0039.png", + "time" : 913496.9077881251 + }, + { + "file" : "0040.png", + "time" : 913496.95619421406 + }, + { + "file" : "0041.png", + "time" : 913496.9842686241 + }, + { + "time" : 913497.00723700109, + "file" : "0042.png" + }, + { + "time" : 913497.041060513, + "file" : "0043.png" + }, + { + "time" : 913497.07430782309, + "file" : "0044.png" + }, + { + "file" : "0045.png", + "time" : 913497.10735687101 + }, + { + "file" : "0046.png", + "time" : 913497.14334790211 + }, + { + "time" : 913497.17468008504, + "file" : "0047.png" + }, + { + "time" : 913497.2073035331, + "file" : "0048.png" + }, + { + "file" : "0049.png", + "time" : 913497.2407012051 + }, + { + "file" : "0050.png", + "time" : 913497.27261283004 + }, + { + "file" : "0051.png", + "time" : 913497.30841093604 + }, + { + "time" : 913497.36005954805, + "file" : "0052.png" + }, + { + "file" : "0053.png", + "time" : 913497.37658144103 + }, + { + "file" : "0054.png", + "time" : 913497.40616276604 + }, + { + "file" : "0055.png", + "time" : 913497.44372079405 + }, + { + "time" : 913497.47105816507, + "file" : "0056.png" + }, + { + "file" : "0057.png", + "time" : 913497.50395031006 + }, + { + "time" : 913497.54083171708, + "file" : "0058.png" + }, + { + "file" : "0059.png", + "time" : 913497.573211696 + }, + { + "file" : "0060.png", + "time" : 913497.60865372606 + }, + { + "time" : 913497.64094713808, + "file" : "0061.png" + }, + { + "time" : 913497.67296079407, + "file" : "0062.png" + }, + { + "file" : "0063.png", + "time" : 913497.70777579001 + }, + { + "time" : 913497.74030354002, + "file" : "0064.png" + }, + { + "file" : "0065.png", + "time" : 913497.77367360005 + }, + { + "file" : "0066.png", + "time" : 913497.80816427805 + }, + { + "file" : "0067.png", + "time" : 913497.85291612009 + }, + { + "time" : 913497.8853847651, + "file" : "0068.png" + }, + { + "time" : 913497.9067391901, + "file" : "0069.png" + }, + { + "time" : 913497.95591217605, + "file" : "0070.png" + }, + { + "time" : 913497.98578011105, + "file" : "0071.png" + }, + { + "file" : "0072.png", + "time" : 913498.00512788806 + }, + { + "file" : "0073.png", + "time" : 913498.05205323 + }, + { + "file" : "0074.png", + "time" : 913498.08894538402 + }, + { + "time" : 913498.11515654309, + "file" : "0075.png" + }, + { + "file" : "0076.png", + "time" : 913498.15184456704 + }, + { + "file" : "0077.png", + "time" : 913498.17095839209 + }, + { + "file" : "0078.png", + "time" : 913498.21903711604 + }, + { + "file" : "0079.png", + "time" : 913498.25633848202 + }, + { + "time" : 913498.28123657801, + "file" : "0080.png" + }, + { + "time" : 913498.31629434205, + "file" : "0081.png" + }, + { + "file" : "0082.png", + "time" : 913498.339072 + }, + { + "file" : "0083.png", + "time" : 913498.38532137405 + }, + { + "time" : 913498.41876535304, + "file" : "0084.png" + }, + { + "time" : 913498.4510205501, + "file" : "0085.png" + }, + { + "time" : 913498.48649811209, + "file" : "0086.png" + }, + { + "file" : "0087.png", + "time" : 913498.51935760211 + }, + { + "time" : 913498.55137622508, + "file" : "0088.png" + }, + { + "time" : 913498.58565475, + "file" : "0089.png" + }, + { + "time" : 913498.61504772911, + "file" : "0090.png" + }, + { + "file" : "0091.png", + "time" : 913498.65260026406 + }, + { + "file" : "0092.png", + "time" : 913498.68563959002 + }, + { + "time" : 913498.71734139905, + "file" : "0093.png" + }, + { + "time" : 913498.75264153909, + "file" : "0094.png" + }, + { + "file" : "0095.png", + "time" : 913498.78606353805 + }, + { + "time" : 913498.81542745605, + "file" : "0096.png" + }, + { + "file" : "0097.png", + "time" : 913498.85264388402 + }, + { + "file" : "0098.png", + "time" : 913498.88223846711 + }, + { + "time" : 913498.9185269681, + "file" : "0099.png" + }, + { + "file" : "0100.png", + "time" : 913498.938407565 + }, + { + "time" : 913498.9707252091, + "file" : "0101.png" + }, + { + "time" : 913499.00930571207, + "file" : "0102.png" + }, + { + "time" : 913499.0412945631, + "file" : "0103.png" + }, + { + "file" : "0104.png", + "time" : 913499.07240307203 + }, + { + "time" : 913499.10760145809, + "file" : "0105.png" + }, + { + "file" : "0106.png", + "time" : 913499.14348651504 + }, + { + "file" : "0107.png", + "time" : 913499.17313443404 + }, + { + "file" : "0108.png", + "time" : 913499.20785826608 + }, + { + "time" : 913499.24082741106, + "file" : "0109.png" + }, + { + "time" : 913499.27446252503, + "file" : "0110.png" + }, + { + "file" : "0111.png", + "time" : 913499.3080595911 + }, + { + "time" : 913499.34256857308, + "file" : "0112.png" + }, + { + "file" : "0113.png", + "time" : 913499.37193783605 + }, + { + "time" : 913499.41522673611, + "file" : "0114.png" + }, + { + "time" : 913499.44977159204, + "file" : "0115.png" + }, + { + "time" : 913499.48206619604, + "file" : "0116.png" + } +] \ No newline at end of file diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/baseline-replay.log b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/baseline-replay.log new file mode 100644 index 00000000..fe271042 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/baseline-replay.log @@ -0,0 +1,18 @@ +/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-touch.n2JrMa/red-boundary.log:Test Case '-[GestureUITests.SubBannerGestureUITests testAutomaticFiveSecondWrapFrames]' started. +/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-touch.n2JrMa/red-boundary.log:Test Case '-[GestureUITests.SubBannerGestureUITests testAutomaticFiveSecondWrapFrames]' passed (17.427 seconds). +/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-touch.n2JrMa/red-boundary.log:Test Case '-[GestureUITests.SubBannerGestureUITests testManualBoundaryFrames]' started. +/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-touch.n2JrMa/red-boundary.log:/Users/klaus/Develop/sodalive/iOS/SodaLive/work/tests/SubBannerGestureUITests.swift:21: error: -[GestureUITests.SubBannerGestureUITests testManualBoundaryFrames] : XCTAssertEqual failed: ("taps=1;last=1") is not equal to ("taps=0;last=0") +/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-touch.n2JrMa/red-boundary.log:Test Case '-[GestureUITests.SubBannerGestureUITests testManualBoundaryFrames]' failed (8.351 seconds). +/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-touch.n2JrMa/red-boundary.log:Test Case '-[GestureUITests.SubBannerGestureUITests testSwipeDoesNotTap]' started. +/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-touch.n2JrMa/red-boundary.log:/Users/klaus/Develop/sodalive/iOS/SodaLive/work/tests/SubBannerGestureUITests.swift:21: error: -[GestureUITests.SubBannerGestureUITests testSwipeDoesNotTap] : XCTAssertEqual failed: ("taps=1;last=1") is not equal to ("taps=0;last=0") +/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-touch.n2JrMa/red-boundary.log:Test Case '-[GestureUITests.SubBannerGestureUITests testSwipeDoesNotTap]' failed (7.549 seconds). +/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-touch.n2JrMa/red-boundary.log: Executed 3 tests, with 2 failures (0 unexpected) in 33.327 (33.331) seconds +/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-touch.n2JrMa/red-boundary.log: Executed 3 tests, with 2 failures (0 unexpected) in 33.327 (33.332) seconds +/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-touch.n2JrMa/red-boundary.log: Executed 3 tests, with 2 failures (0 unexpected) in 33.327 (33.334) seconds +/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-touch.n2JrMa/red-boundary.log:** TEST FAILED ** +/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-touch.n2JrMa/red-wide-window.log:Test Case '-[GestureUITests.SubBannerGestureUITests testAutomaticFiveSecondWrapFrames]' started. +/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-touch.n2JrMa/red-wide-window.log:Test Case '-[GestureUITests.SubBannerGestureUITests testAutomaticFiveSecondWrapFrames]' passed (16.185 seconds). +/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-touch.n2JrMa/red-wide-window.log: Executed 1 test, with 0 failures (0 unexpected) in 16.185 (16.187) seconds +/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-touch.n2JrMa/red-wide-window.log: Executed 1 test, with 0 failures (0 unexpected) in 16.185 (16.188) seconds +/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-touch.n2JrMa/red-wide-window.log: Executed 1 test, with 0 failures (0 unexpected) in 16.185 (16.190) seconds +/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-touch.n2JrMa/red-wide-window.log:** TEST SUCCEEDED ** diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/native-tests.log b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/native-tests.log new file mode 100644 index 00000000..68a24d8e --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/native-tests.log @@ -0,0 +1,18 @@ +Test Case '-[GestureUITests.SubBannerGestureUITests testAutomaticFiveSecondWrapFrames]' started. + t = 16.64s Synthesize event +Test Case '-[GestureUITests.SubBannerGestureUITests testAutomaticFiveSecondWrapFrames]' passed (17.854 seconds). +Test Case '-[GestureUITests.SubBannerGestureUITests testManualBoundaryFrames]' started. + t = 8.72s Synthesize event + t = 9.15s Press "1 / 3" Button[0.20, 0.50] for 0.1 seconds, then drag to "1 / 3" Button[0.80, 0.50] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds + t = 9.46s Synthesize event +/Users/klaus/Develop/sodalive/iOS/SodaLive/work/tests/SubBannerGestureUITests.swift:20: error: -[GestureUITests.SubBannerGestureUITests testManualBoundaryFrames] : XCTAssertEqual failed: ("taps=1;last=1") is not equal to ("taps=0;last=0") +Test Case '-[GestureUITests.SubBannerGestureUITests testManualBoundaryFrames]' failed (12.351 seconds). +Test Case '-[GestureUITests.SubBannerGestureUITests testSwipeDoesNotTap]' started. + t = 9.09s Press "1 / 3" Button[0.80, 0.50] for 0.1 seconds, then drag to "1 / 3" Button[0.20, 0.50] with velocity of 500.00 pixels per second, and then hold for 0.0 seconds + t = 9.32s Synthesize event +/Users/klaus/Develop/sodalive/iOS/SodaLive/work/tests/SubBannerGestureUITests.swift:20: error: -[GestureUITests.SubBannerGestureUITests testSwipeDoesNotTap] : XCTAssertEqual failed: ("taps=1;last=1") is not equal to ("taps=0;last=0") +Test Case '-[GestureUITests.SubBannerGestureUITests testSwipeDoesNotTap]' failed (11.294 seconds). + Executed 3 tests, with 2 failures (0 unexpected) in 41.500 (41.508) seconds + Executed 3 tests, with 2 failures (0 unexpected) in 41.500 (41.513) seconds + Executed 3 tests, with 2 failures (0 unexpected) in 41.500 (41.517) seconds +** TEST FAILED ** diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/red-wide-window-after.sha256 b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/red-wide-window-after.sha256 new file mode 100644 index 00000000..54381962 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/red-wide-window-after.sha256 @@ -0,0 +1 @@ +b3e4c5333cf8249ccd7773b5cabd372efb5a5565323bd7778034e110386151fd /Users/klaus/Develop/sodalive/iOS/SodaLive/SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/red-wide-window-before.sha256 b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/red-wide-window-before.sha256 new file mode 100644 index 00000000..54381962 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/red-wide-window-before.sha256 @@ -0,0 +1 @@ +b3e4c5333cf8249ccd7773b5cabd372efb5a5565323bd7778034e110386151fd /Users/klaus/Develop/sodalive/iOS/SodaLive/SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/red-wide-window-compiled.sha256 b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/red-wide-window-compiled.sha256 new file mode 100644 index 00000000..57a79ee7 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/red-wide-window-compiled.sha256 @@ -0,0 +1 @@ +7e25db3076d545ffc61ea3a1c5585e7b8a617964ef406d7570286d75a75e010a /Users/klaus/Develop/sodalive/iOS/SodaLive/docs/20261029_추천_서브_배너/reviews/evidence/native-final/production-source-snapshot/SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/test-summary.json b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/test-summary.json new file mode 100644 index 00000000..88ed1b91 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/test-summary.json @@ -0,0 +1,61 @@ +{ + "devicesAndConfigurations" : [ + { + "device" : { + "architecture" : "x86_64", + "deviceId" : "B5F2BA0D-327C-4EE3-94EE-39710A0E1ACC", + "deviceName" : "SubBannerGestureQA-20261030", + "modelName" : "iPhone SE (3rd generation)", + "osBuildNumber" : "22D8075", + "osVersion" : "18.3.1", + "platform" : "iOS Simulator" + }, + "expectedFailures" : 0, + "failedTests" : 2, + "passedTests" : 1, + "skippedTests" : 0, + "testPlanConfiguration" : { + "configurationId" : "1", + "configurationName" : "Test Scheme Action" + } + } + ], + "environmentDescription" : "Gesture · Built with macOS 26.0", + "expectedFailures" : 0, + "failedTests" : 2, + "finishTime" : 1793289744.783, + "passedTests" : 1, + "result" : "Failed", + "skippedTests" : 0, + "startTime" : 1793289548.885, + "statistics" : [ + + ], + "testFailures" : [ + { + "failureText" : "XCTAssertEqual failed: (\"taps=1;last=1\") is not equal to (\"taps=0;last=0\")", + "targetName" : "GestureUITests", + "testIdentifier" : 2, + "testIdentifierString" : "SubBannerGestureUITests/testManualBoundaryFrames()", + "testIdentifierURL" : "test://com.apple.xcode/Gesture/GestureUITests/SubBannerGestureUITests/testManualBoundaryFrames", + "testName" : "testManualBoundaryFrames()" + }, + { + "failureText" : "XCTAssertEqual failed: (\"taps=1;last=1\") is not equal to (\"taps=0;last=0\")", + "targetName" : "GestureUITests", + "testIdentifier" : 3, + "testIdentifierString" : "SubBannerGestureUITests/testSwipeDoesNotTap()", + "testIdentifierURL" : "test://com.apple.xcode/Gesture/GestureUITests/SubBannerGestureUITests/testSwipeDoesNotTap", + "testName" : "testSwipeDoesNotTap()" + } + ], + "title" : "Test - Gesture", + "topInsights" : [ + { + "category" : "Common Failures", + "impact" : "100% of failures", + "text" : "2 runs of 2 tests failed with XCTAssertEqual failed: (\"taps=1;last=1\") is not equal to (\"taps=0;last=0\")" + } + ], + "totalTestCount" : 3 +} diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/tests-after.sha256 b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/tests-after.sha256 new file mode 100644 index 00000000..932423df --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/tests-after.sha256 @@ -0,0 +1 @@ +7e25db3076d545ffc61ea3a1c5585e7b8a617964ef406d7570286d75a75e010a /Users/klaus/Develop/sodalive/iOS/SodaLive/SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/tests-before.sha256 b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/tests-before.sha256 new file mode 100644 index 00000000..932423df --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/gesture-fix/red/tests-before.sha256 @@ -0,0 +1 @@ +7e25db3076d545ffc61ea3a1c5585e7b8a617964ef406d7570286d75a75e010a /Users/klaus/Develop/sodalive/iOS/SodaLive/SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/implementation.md b/docs/20261029_추천_서브_배너/reviews/evidence/implementation.md new file mode 100644 index 00000000..30150327 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/implementation.md @@ -0,0 +1,82 @@ +# P1-T1 구현 증거 + +작성일: 2026-10-29. 범위: 현재 checkout에서 P1-T1 구현과 집중 검사. worktree/commit/push/하위 에이전트/실서비스 요청 없음. P2 빌드·native QA·독립 리뷰 완료를 주장하지 않는다. + +## 변경 파일 + +| 경로 | 변경 | +|---|---| +| `SodaLive/Sources/V2/Component/Banner/SubBanner.swift` | 6개 필드, lossless `Int`, placement enum, 원문 동일 URL 검증, 이미지·iOS/web 자격 | +| `SodaLive/Sources/V2/Component/Banner/SubBannerLinkOpener.swift` | 주입 가능한 completion opener, 실제 UIKit open, iOS 우선/실패 시 웹 1회, Android 실행 없음 | +| `SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift` | 전체 목록, 현재 이미지 실측 비율/컨테이너 폭, swipe/counter, 5초 자동 순환, 단일·inactive·disappear 중단 | +| `SodaLive/Sources/V2/Main/Home/Recommendation/Models/MainHomeRecommendationResponse.swift` | 필수 `subBanners`, 순서 보존 홈 자격 목록 | +| `SodaLive/Sources/V2/Main/Home/Recommendation/Repository/MainHomeRecommendationApi.swift` | 추천 GET만 익명/공백 토큰 Authorization 생략, 로그인 원문 및 follow 유지 | +| `SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationViewModel.swift` | 디코딩 전 HTTP 200 검증, 기존 실패 정책/데이터 보존 | +| `SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift` | active creators 뒤, 유효 목록 비어 있으면 미삽입, 기존 s24 | +| `SodaLive/Sources/V2/Main/Content/Recommendation/Models/AudioRecommendationsResponse.swift` | 필수 `subBanners`, 콘텐츠 자격 목록, `isEmpty` 반영 | +| `SodaLive/Sources/V2/Main/Content/Recommendation/Repository/MainContentRecommendationApi.swift` | 익명 GET Authorization 생략, 로그인 원문 유지 | +| `SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationViewModel.swift` | 디코딩 전 HTTP 200 검증, 기존 실패 UI/hasLoaded/데이터 보존 | +| `SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationView.swift` | main banner 뒤/original-only 앞, 유효 목록 비어 있으면 미삽입, 기존 s24 | +| `SodaLive.xcodeproj/project.pbxproj` | 신규 3개 파일을 두 앱의 Sources 및 기존 Banner group에 추가 | +| `work/tests/SubBannerCheck.swift`, `work/tests/check-sub-banners.sh` | 실제 프로덕션 API/repository/VM/DTO/opener 실행, native Combine, Moya transport double, 선택적 실제 iOS UI 타입 검사 | +| `docs/20261029_추천_서브_배너/plan-task.md`, 이 문서 | Phase 1 체크·진행·검증 증거만 기록 | + +## RED / GREEN + +구현 전 기존 파일만 컴파일하도록 둔 임시 `--baseline` 모드에서 아래 두 실행이 컴파일 성공 후 assertion으로 실패했다. 미존재 타입의 컴파일 오류를 RED로 간주하지 않았다. 최종 runner에서는 이 임시 모드를 제거하고 전체 계약 검사를 항상 컴파일한다. + +| 실행 | 실제 결과 | +|---|---| +| `bash work/tests/check-sub-banners.sh --baseline` | `FAIL: Anonymous GET must omit Authorization; authenticated/follow must preserve raw token: /api/v2/home/recommendations` | +| `bash work/tests/check-sub-banners.sh --baseline --http` | `FAIL: HTTP 201/http must not become successful recommendations (home=true)` | +| `bash work/tests/check-sub-banners.sh` | 5개 PASS 출력, exit 0 | +| `bash work/tests/check-sub-banners.sh --ui` | `PASS: production carousel/model/UIKit opener iOS16.6 typecheck with installed Kingfisher and real theme/font files`, exit 0 | + +집중 검사의 실행 범위: +- 실제 두 추천 GET의 path/method/requestPlain/요청 횟수, 빈 값·공백 토큰 헤더 생략, 원문 bearer, 인접 follow POST/body/header 보존. +- 두 VM의 HTTP 201/401/503, HTML503, envelope false/data null, malformed JSON, transport 실패 및 기존 오류 메시지/팝업/로딩 종료. 성공 이후 실패가 기존 목록을 빈 데이터로 바꾸지 않음. +- DTO `9_007_199_254_740_993`, `Int.max`, fractional ID 거부, enum/미지원 placement, 필수 배열 누락/null 거부, 23개 역순 목록 보존, placement/Android-only/잘못된 이미지 필터, 기존 메인 배너 보존, sub-banner-only/filtered-empty 콘텐츠. +- HTTP(S)+host, 절대 deep URI, 대소문자 무관 금지 scheme, blank/상대/잘못된 escape/자동 percent encoding을 유발하는 입력 거부, accepted escaped URI 원문 보존. iOS/web/Android 존재 여부 8개 조합 각각 성공·실패 completion으로 열기 순서·횟수 검증, 웹 대체 실패 후 재시도 없음. + +## 회귀·소스 포함·진단 + +실행 shell마다 아래 패턴으로 소유 임시 root와 캐시를 격리했다. 각 root의 EXIT trap 및 내부 runner trap으로 해당 root만 정리했다. + +```bash +TASK_ROOT=$(mktemp -d /var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-verify.XXXXXX) +export TMPDIR="$TASK_ROOT" NODE_COMPILE_CACHE="$TASK_ROOT/node" BUN_RUNTIME_TRANSPILER_CACHE_PATH="$TASK_ROOT/bun" +trap 'rm -rf "$TASK_ROOT"' EXIT +bash work/tests/check-sub-banners.sh +``` + +- 전체 회귀 명령: `for script in work/tests/check-*.sh; do bash "$script"; done`와 같은 순서로 각 결과를 수집하는 루프를 600000ms 제한으로 실행했다. `check-app-deep-link-gift-detail`, `check-auth-content-settings-exit`, `check-auth-verification-mock`, `check-event-deep-link`, `check-event-detail`, `check-gift-address-search`, `check-gift-contact-input`, `check-gift-decoding-contract`, `check-gift-detail-cancel-loading`, `check-gift-detail-talk-inquiry`, `check-gift-phase11`, `check-gift-recipient-address`, `check-my-page-notices`, `check-received-gift-detail`, `check-sub-banners` 총 15개 PASS 후 마지막 채팅 날짜 검사에서 tool timeout/`Terminated: 15`. 테스트 assertion 실패가 아니다. +- 마지막 검사 분리 실행: `bash work/tests/check-user-creator-chat-date-presentation.sh` → `PASS: 113 assertions`, exit 0. 총 16개 검사 개별 PASS, 단일 전체 루프 완주 아님. +- 아래 membership 명령: 두 타깃 `SodaLive-dev`, `SodaLive`에서 신규 파일 3개가 각각 정확히 한 번 Sources에 존재하고 동일 Banner group에 포함됨을 assertion으로 확인, exit 0. +- `GIT_MASTER=1 git diff --check`: 출력 없음, exit 0. diff에서 기존 `BannerCarousel.swift`, 메인 배너 section, 공유 `AuthPlugin`, endpoint 및 기타 추천 필드의 미변경 확인. +- UI 검사: `xcrun swiftc -swift-version 5 -typecheck -D SB_UI_CHECK -sdk "$(xcrun --sdk iphonesimulator --show-sdk-path)" -target x86_64-apple-ios16.6-simulator ...`를 runner에서 실행. 실제 설치된 Kingfisher 모듈과 production 신규 3개 파일/Color/Spacing/ColorExtension/FontModifier 사용. test 파일의 LanguageOption만 최소 double이며 SwiftUI/Kingfisher/UIKit은 double 아님. 로컬 캐시 경로는 `KINGFISHER_MODULE_PATH`로 지정 가능. +- 최종 `xcrun swiftc -swift-version 5 -parse`에 변경 production Swift 11개와 `work/tests/SubBannerCheck.swift`를 전달: 출력 없음, exit 0. 집중 검사와 `--ui`도 runner 정리 후 다시 실행해 위 PASS를 재확인했다. + +```bash +plutil -convert json -o - SodaLive.xcodeproj/project.pbxproj | python3 -c ' +import json,sys +objects=json.load(sys.stdin)["objects"] +names={"SubBanner.swift","SubBannerLinkOpener.swift","SubBannerCarousel.swift"} +refs={key for key,value in objects.items() if value.get("isa")=="PBXFileReference" and value.get("path") in names} +assert len(refs)==3 +assert sum(value.get("isa")=="PBXGroup" and value.get("path")=="Banner" and refs.issubset(value.get("children",[])) for value in objects.values())==1 +targets=[value for value in objects.values() if value.get("isa")=="PBXNativeTarget" and value.get("name") in {"SodaLive","SodaLive-dev"}] +assert len(targets)==2 +for target in targets: + paths=[objects[objects[file]["fileRef"]]["path"] for phase in target["buildPhases"] if objects[phase]["isa"]=="PBXSourcesBuildPhase" for file in objects[phase]["files"]] + assert all(paths.count(name)==1 for name in names) + print("PASS:",target["name"],"three files exactly once; Banner group verified")' +``` + +## 구현 결정과 남은 검증 + +- 새 state/controller 계층 없이 View 내부 index·실측 URL별 ratio와 SwiftUI `.task(id:)`를 사용한다. task ID에 활성 scene과 전체 items가 반영되어 비활성/목록 변경 시 취소·재시작하며 disappear는 SwiftUI가 취소한다. 단일 항목은 sleep 루프가 없다. +- HStack의 KFImage는 높이 0에서도 `startLoadingBeforeViewAppear()`로 body 평가 시 로딩한다. `onSuccess`의 실제 image.size만 URL별로 저장하며 현재 URL의 ratio로 높이를 계산하므로 다른 항목의 늦은 성공이 현재 높이를 덮지 않는다. 최초 임의 ratio 없음, 다음 이미지 미로드 시 마지막 실측 비율 유지. 개별 실패는 Kingfisher placeholder에만 남는다. +- 실제 화면 렌더, 스와이프와 세로 scroll 공존, 서로 다른 원본 비율, empty gap, 정확한 5초/순환/scene 생명주기, 실제 UIApplication completion은 이 작업에서 native 실행하지 않았다. parent의 P2 native QA 대상이다. 이 문서는 렌더 PASS 증거가 아니다. +- full app build/독립 리뷰는 요청에 따라 parent 소유다. `lsp_diagnostics` 도구는 제공되지 않아 수행하지 않았으며 CLI 컴파일·타입 검사·parse만 별도 근거로 삼는다. 실서비스 계약 검증이나 외부 네트워크 호출도 하지 않았다. +- 소유 root만 정리한다. parent의 `sub-banner-ios-run.x6GLEo`, shared DerivedData/cache/device를 삭제·변경하지 않는다. +- 정리 영수증: timeout 후 남은 소유 `sub-banner-regression.GWjNjC`는 Read로 빈 디렉터리임을 확인하고 `rmdir` 및 `test ! -e` PASS. 마지막 승인된 temp 부모 디렉터리 Read에서 이번 실행의 `sub-banner-red.*`, `red-http.*`, `green.*`, `ui-typecheck.*`, `regression.*`, `membership.*`, `membership-final.*`, `diff.*`, `process-check.*`, `chat-rerun.*`, `final-check.*`, `final-ui.*`, `final-parse.*`, `final-diff.*`, `cleanup-verify.*` root 부재를 확인했다. parent의 `sub-banner-ios-run.x6GLEo`는 그대로 존재한다. diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/README.md b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/README.md new file mode 100644 index 00000000..6aae595e --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/README.md @@ -0,0 +1,74 @@ +# 최종 소스 Native QA 재실행 + +**현재 production 캐러셀 SHA256 `7e25db3076d545ffc61ea3a1c5585e7b8a617964ef406d7570286d75a75e010a`로 기존 native matrix 재실행과 평가·정리를 완료했다. 실행한 시나리오는 PASS이며 physical tap/swipe와 Reduce Motion ON은 NOT RUN이다.** REV-P1-001의 현재 소스와 캡처 연결을 보완하는 증거이며 독립 reviewer의 종료 판정을 대신하지 않는다. + +## 출처와 변경 범위 + +- 실행일: 2026-10-30 KST. 부모 소유 iPhone SE3/iOS 18.3 `482F7827-2E3A-4559-BAF4-48B68CC5725B`, 375×667pt, 2x를 재사용했다. Xcode 26 SDK, Swift 5, `x86_64-apple-ios16.6-simulator` compile이다. iOS 16.6 런타임 실행은 아니다. +- 기존 [native/source](../native/source)의 `run.py`, `assess.py`, `cleanup.py`, `Doubles.swift`, `Harness.swift`를 그대로 재사용했다. 변경은 evidence root, own temporary root, own bundle뿐이다. [provenance-before.json](provenance-before.json)과 [provenance-after.json](provenance-after.json)의 `harnessCloneChecks`가 경로 정규화 후 5개 파일 동일성을 assert했다. 시나리오, 대기 시간, 입력 fixture, geometry 검사는 변경하지 않았다. +- 실제 production 9개 파일의 원본 경로를 `swiftc` 입력으로 사용했다. [production-source-snapshot](production-source-snapshot/)은 전체 입력 9개의 byte-identical 보존본이며 컴파일 입력은 이 보존본이 아니다. [commands.json](commands.json)에 원본 경로와 실제 native 명령·stdout/stderr·exit가 있다. +- [source-hashes-before.json](source-hashes-before.json), [source-hashes-after.json](source-hashes-after.json), [source-hashes-final.json](source-hashes-final.json), teardown 이후 provenance까지 9개 입력과 snapshot의 해시가 일치한다. [source-stability.json](source-stability.json)은 `unchanged=true`, `changed=[]`이다. +- 기존 native 최종 해시 `1bf03b8b1e05310acce09f12b2f09b35af3587f4a7d486b22945ec502cfb506d`와 비교하면 9개 입력 중 캐러셀만 다르고 나머지 8개는 같다. 전달받은 delta는 `@GestureState isSwiping`, tap guard, `highPriorityGesture` → `simultaneousGesture`, swiping 중 자동 전환 생략, 수평 우세 drag 및 `onEnded` guard이다. 현재 전체 snapshot에서 이 처리를 확인했다. 과거 캐러셀 전체 소스는 기존 evidence에 없으므로 과거 byte 단위 diff를 재구성했다고 주장하지 않는다. 배치·비율 변경은 요청되지 않았고 이번 실행에서 제품 수정은 없었다. + +기존 `native/` 62개 파일과 PRD·plan·기존 리뷰 3개, 합계 65개 보호 파일의 해시 및 파일 목록이 전후 동일하다. 기존 증거·해시를 덮어쓰지 않았다. 추가한 `source/provenance.py`는 snapshot·보호 파일·의존성 해시와 소유권을 기록하는 증거 보조 도구이며 UI 하네스 동작은 바꾸지 않는다. + +## 실제 실행 Surface와 명시적 대역 + +production `SubBanner.swift`, `SubBannerLinkOpener.swift`, `SubBannerCarousel.swift`, 두 recommendation View composition, 실제 spacing/color/hex/font 소스를 원본 경로에서 컴파일했다. 기존 prebuilt Kingfisher object/module을 읽기 전용으로 링크했고 전후 해시 동일성을 확인했다. 실제 `KFImage`와 downloader를 사용한다. [dependency-hashes.json](dependency-hashes.json) 참조. + +기존 `FixtureProtocol`을 실제 Kingfisher의 ephemeral URLSession에 연결했다. `subbanner.qa.invalid`만 로컬 생성 PNG로 응답하고 다른 요청은 로컬에서 실패한다. 인터넷·계정·서버는 사용하지 않았다. A=1200×400 teal, B=1200×600 orange이다. + +[source/Doubles.swift](source/Doubles.swift)의 하위 추천 섹션은 48pt `QA ...` bar 또는 `EmptyView`, VM/DTO는 fixture, refresh wrapper는 `ScrollView`, toast/purchase/media player는 무효 대역이다. production 두 View의 조건부 삽입과 `VStack(s24)`는 실행하지만 실제 추천 카드·기존 메인 배너 carousel·API/인증/filter/콘텐츠 isEmpty·구매·refresh 전체를 검증하지 않는다. 오류 격리는 주변 **대역 bar 유지**에 한정한다. + +## 매트릭스와 실제 관측 + +[captures/actions.json](captures/actions.json)의 `finished`는 launch 기준 monotonic 210.953초에 기록됐다. 41개 앱 window `drawHierarchy` PNG와 별도 [native-os-final.png](native-os-final.png), 총 42개 native 원본 모두 750×1334이고 production 소스보다 신선하다. [verification.json](verification.json)에 PNG별 hash·크기·freshness와 geometry가 있다. [contact-1.png](contact-1.png)~[contact-4.png](contact-4.png)는 41개 앱 캡처 전체를 포함하는 검토용 축소 배치이며 새로운 UI가 아니다. + +| 시나리오 | 판정 | 이번 실행에서 확인한 증거 | +|---|---|---| +| 홈 지정 위치 | PASS | [home-first](captures/home-first.png): QA active creators 뒤, debut 앞 | +| 콘텐츠 지정 위치 | PASS | [content-first](captures/content-first.png): QA main banner 뒤, exclusive 앞 | +| 전체 폭·원본 비율 | PASS | 양 화면 A: x0/w375/h125pt, B: x0/w375/h187.5pt. Home y280, Content y136 | +| s24·empty 추가 gap0 | PASS | 이미지 위아래 24pt. [home-empty](captures/home-empty.png), [content-empty](captures/content-empty.png)는 일반 섹션 간격 24pt만 유지 | +| 실제 5초 자동 전환·2항목 wrap | PASS, 정밀도 한정 | Home 시작 후 4.523초 A → 5.374초 transition → 6.118초 B. Content 4.791초 A → 5.640초 transition → 6.387초 B. 두 wrap 캡처는 A/01/02 | +| 단일 고정 | PASS | 양 화면 one과 one-after-6s의 PNG SHA 동일, 01/01. 실제 관측 간격 Home 6.048초, Content 6.069초 | +| 21개·20개 제한 없음 | PASS | [many-first](captures/many-first.png) 01/21 → [many-twenty-first](captures/many-twenty-first.png) **20/21** → [many-wrap](captures/many-wrap.png) **21/21** → OS final **02/21**. timer/index를 직접 바꾸지 않음 | +| 컨테이너 resize | PASS | 양 화면 부모 frame 375→320pt, 배너 x0/w320/h106.6667pt. 실제 회전/기기 resize는 아님 | +| 지연 이미지 비율 격리 | PASS | 초기 direct 높이0 → 유효 B 높이187.5 → 지연된 A 도착 뒤에도 B/187.5 유지 | +| 실패 이미지 국소 처리 | PASS | [failure-placeholder-other-content](captures/failure-placeholder-other-content.png): 회색125pt/02/02와 주변 QA bar 유지 → [failure-wrap-valid](captures/failure-wrap-valid.png) A | +| inactive/active | PASS, 주입 환경 | 실제 scenePhase probe inactive 확인, 6.512초 간격 PNG SHA 동일 B. active 주입 후 4.837초 B, 6.345초 A | +| unmount/remount·목록 축소 | PASS | unmounted 캡처는 하네스 제목만 유지. remount-first A → remount-next B → shrink-one A/01/01 | +| light/dark | PASS, 범위 한정 | 두 화면 light 캡처가 dark first와 동일한 검정 배경·읽을 수 있는 indicator 유지. 하네스 window override이며 전체 앱 system theme 검증 아님 | +| 링크 fallback driver | PASS, 탭 아님 | production opener에 false completion 주입, `sodaqa://banner/900` 뒤 `https://subbanner.qa.invalid/destination` 각 1회 | +| physical tap/swipe·세로 scroll 공존 | NOT RUN | 좌표 입력/hit testing 미실행. gesture 소스 존재와 compile을 실제 제스처 성공으로 취급하지 않음 | +| Reduce Motion ON | NOT RUN | OS 관측값 false, 부모 접근성 설정 변경 없음. `reduce-*` 파일명은 ON 증거가 아님 | + +파일명은 예정 촬영 시점명이다. 현재 원본을 직접 열어 many-twenty-first=20/21, many-wrap=21/21, native-os-final=02/21 및 `many-2-wide.png`를 확인했다. 기존 `assess.py`에는 이전 실행 관측 `native-os-final=01/21`이 하드코딩되어 있으므로 script 복제 자체는 유지하고 **이번 `verification.json`의 해당 설명만 실제 캡처에 맞게 수동 정정했다.** OS final은 복사와 screenshot 사이에도 timer가 계속 동작한 뒤의 상태다. 21→1 순간 프레임은 없고 21 이후 2가 확인된 범위에서 wrap을 판단한다. + +모든 contact sheet와 주요 full-size PNG(두 화면 first/second/transition, 21개 후반 2장, OS final)를 직접 열어 배너·주변 bar 배치, clipping/겹침, fixture 순서와 페이지 번호를 확인했다. 전환 프레임은 A 일부와 B가 함께 보이는 이동 중 상태이며 결함이나 정착 프레임으로 오인하지 않았다. timestamp는 screenshot 완료 후 기록이므로 5.000초 정확도·프레임률·매끄러움·인터럽트·중복 timer 부재를 보증하지 않는다. + +## 명령과 평가 + +아래 순서로 각 명령 exit0을 확인했다. `provenance.py`는 Python AST와 clone 동일성도 검사한다. native compile stderr는 비어 있었다. LSP 도구는 제공되지 않았으며 native `swiftc` 진단으로 대체했다. 전체 앱 build, 집중 CLI checks 재실행, 독립 reviewer 호출은 이번 위임 범위에 따라 하지 않았다. + +```bash +export TMPDIR=/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-native-final.97cQKj +export NODE_COMPILE_CACHE="$TMPDIR" +export BUN_RUNTIME_TRANSPILER_CACHE_PATH="$TMPDIR" +export PYTHONDONTWRITEBYTECODE=1 +python3 docs/20261029_추천_서브_배너/reviews/evidence/native-final/source/provenance.py +python3 docs/20261029_추천_서브_배너/reviews/evidence/native-final/source/run.py +python3 docs/20261029_추천_서브_배너/reviews/evidence/native-final/source/assess.py +python3 docs/20261029_추천_서브_배너/reviews/evidence/native-final/source/cleanup.py +python3 docs/20261029_추천_서브_배너/reviews/evidence/native-final/source/provenance.py --after +``` + +`assess.py`의 assertions PASS: 41 PNG와 OS PNG signature/750×1334/freshness, 원본 소스 해시, 양 화면 전체 폭·A/B 비율·s24·empty·320pt, single 고정, inactive 고정, 지연 이미지 높이 격리, 21개 input, opener 순서. 자동 geometry와 양끝 fixture pixel scan을 교차 검증한다. 페이지 번호 관측은 위 직접 검사와 구분했다. + +## 소유권과 정리 + +[ownership.json](ownership.json)에 own bundle `qa.sodalive.subbanner.native.final.97cQKj`, PID `10884`, own root, 부모 device 소유권을 launch 직후 기록했다. cache는 own root 아래 `module-cache`; shell TMPDIR/NODE/BUN cache 환경은 provenance에 기록했다. + +[cleanup.json](cleanup.json): own app terminate/uninstall exit0, app container 조회 exit2, PID 조회 exit1로 부재 확인, own root와 module-cache 제거, 부모 simulator Booted 유지, production 해시 불변, shared cache 삭제 없음. [provenance-after.json](provenance-after.json)은 teardown 이후에도 source/snapshot·기존 native 62개 파일·PRD/plan/review 3개·Kingfisher 입력 해시 불변과 root 부재를 다시 확인했다. device shutdown/delete/reset, 공유 cache 삭제, worktree, commit/push, 의존성 변경은 하지 않았다. + +남은 한계: physical tap/swipe/세로 scroll 공존, Reduce Motion ON, 실제 UIApplication.open, OS background/foreground, 전체 SodaLive 앱·실서비스, 실기기/iOS16.6/Dynamic Type/VoiceOver는 미검증이다. 현재 증거 범위의 실행 차단 문제는 없으며 REV-P1-001의 공식 해소 판정은 상위 실행자와 기존 reviewer의 후속 범위다. diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/actions.json b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/actions.json new file mode 100644 index 00000000..c9d1c836 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/actions.json @@ -0,0 +1,3305 @@ +[ + { + "event" : "environment", + "phase" : "active", + "reduceMotion" : false, + "scenario" : "preparing", + "time" : 0.19889656302984804 + }, + { + "context" : "home", + "count" : 2, + "event" : "scenario-start", + "scenario" : "home-pair", + "time" : 1.3198033419903368 + }, + { + "event" : "environment", + "phase" : "active", + "reduceMotion" : false, + "scenario" : "home-pair", + "time" : 1.3483353960327804 + }, + { + "event" : "home-first", + "frames" : { + "QA active creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 208 + }, + "QA debut creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 429 + }, + "QA home live" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA home main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 136 + }, + "QA other home content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 501 + } + }, + "inputIDs" : [ + 1, + 2 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "home-first.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + } + ], + "scenario" : "home-pair", + "time" : 2.1974317739950493, + "width" : 375 + }, + { + "event" : "home-before-5s", + "frames" : { + "QA active creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 208 + }, + "QA debut creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 429 + }, + "QA home live" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA home main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 136 + }, + "QA other home content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 501 + } + }, + "inputIDs" : [ + 1, + 2 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "home-before-5s.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + } + ], + "scenario" : "home-pair", + "time" : 5.8432150949956849, + "width" : 375 + }, + { + "event" : "home-transition", + "frames" : { + "QA active creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 208 + }, + "QA debut creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 491.5 + }, + "QA home live" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA home main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 136 + }, + "QA other home content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 563.5 + } + }, + "inputIDs" : [ + 1, + 2 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "home-transition.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + } + ], + "scenario" : "home-pair", + "time" : 6.6938178039854392, + "width" : 375 + }, + { + "event" : "home-second", + "frames" : { + "QA active creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 208 + }, + "QA debut creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 491.5 + }, + "QA home live" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA home main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 136 + }, + "QA other home content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 563.5 + } + }, + "inputIDs" : [ + 1, + 2 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "home-second.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + } + ], + "scenario" : "home-pair", + "time" : 7.437485438073054, + "width" : 375 + }, + { + "event" : "home-wrap", + "frames" : { + "QA active creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 208 + }, + "QA debut creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 429 + }, + "QA home live" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA home main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 136 + }, + "QA other home content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 501 + } + }, + "inputIDs" : [ + 1, + 2 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "home-wrap.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + } + ], + "scenario" : "home-pair", + "time" : 12.2769136010902, + "width" : 375 + }, + { + "event" : "home-light", + "frames" : { + "QA active creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 208 + }, + "QA debut creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 429 + }, + "QA home live" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA home main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 136 + }, + "QA other home content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 501 + } + }, + "inputIDs" : [ + 1, + 2 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "home-light.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + } + ], + "scenario" : "home-pair", + "time" : 12.641967206029221, + "width" : 375 + }, + { + "event" : "home-empty", + "frames" : { + "QA active creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 208 + }, + "QA debut creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 280 + }, + "QA home live" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA home main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 136 + }, + "QA other home content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 352 + } + }, + "inputIDs" : [ + + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "home-empty.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + } + ], + "scenario" : "home-pair", + "time" : 13.107157259015366, + "width" : 375 + }, + { + "event" : "home-one", + "frames" : { + "QA active creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 208 + }, + "QA debut creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 429 + }, + "QA home live" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA home main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 136 + }, + "QA other home content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 501 + } + }, + "inputIDs" : [ + 3 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "home-one.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + } + ], + "scenario" : "home-pair", + "time" : 13.776895610033534, + "width" : 375 + }, + { + "event" : "home-one-after-6s", + "frames" : { + "QA active creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 208 + }, + "QA debut creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 429 + }, + "QA home live" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA home main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 136 + }, + "QA other home content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 501 + } + }, + "inputIDs" : [ + 3 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "home-one-after-6s.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + } + ], + "scenario" : "home-pair", + "time" : 19.824525946984068, + "width" : 375 + }, + { + "event" : "home-resize-320", + "frames" : { + "QA active creators" : { + "height" : 48, + "width" : 320, + "x" : 0, + "y" : 208 + }, + "QA debut creators" : { + "height" : 48, + "width" : 320, + "x" : 0, + "y" : 410.66666666666669 + }, + "QA home live" : { + "height" : 48, + "width" : 320, + "x" : 0, + "y" : 64 + }, + "QA home main banner" : { + "height" : 48, + "width" : 320, + "x" : 0, + "y" : 136 + }, + "QA other home content" : { + "height" : 48.000000000000057, + "width" : 320, + "x" : 0, + "y" : 482.66666666666669 + } + }, + "inputIDs" : [ + 3 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "home-resize-320.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + } + ], + "scenario" : "home-pair", + "time" : 20.416839624056593, + "width" : 320 + }, + { + "context" : "content", + "count" : 2, + "event" : "scenario-start", + "scenario" : "content-pair", + "time" : 20.895267048035748 + }, + { + "event" : "environment", + "phase" : "active", + "reduceMotion" : false, + "scenario" : "content-pair", + "time" : 20.912882879027165 + }, + { + "event" : "content-first", + "frames" : { + "QA content main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA exclusive series" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 285 + }, + "QA other content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 357 + } + }, + "inputIDs" : [ + 1, + 2 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "content-first.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 908685.84615382203, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 908685.85791977204, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + } + ], + "scenario" : "content-pair", + "time" : 21.797844958025962, + "width" : 375 + }, + { + "event" : "content-before-5s", + "frames" : { + "QA content main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA exclusive series" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 285 + }, + "QA other content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 357 + } + }, + "inputIDs" : [ + 1, + 2 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "content-before-5s.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 908685.84615382203, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 908685.85791977204, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + } + ], + "scenario" : "content-pair", + "time" : 25.686531829996966, + "width" : 375 + }, + { + "event" : "content-transition", + "frames" : { + "QA content main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA exclusive series" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 347.5 + }, + "QA other content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 419.5 + } + }, + "inputIDs" : [ + 1, + 2 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "content-transition.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 908685.84615382203, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 908685.85791977204, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + } + ], + "scenario" : "content-pair", + "time" : 26.535493124043569, + "width" : 375 + }, + { + "event" : "content-second", + "frames" : { + "QA content main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA exclusive series" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 347.5 + }, + "QA other content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 419.5 + } + }, + "inputIDs" : [ + 1, + 2 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "content-second.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 908685.84615382203, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 908685.85791977204, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + } + ], + "scenario" : "content-pair", + "time" : 27.282738522044383, + "width" : 375 + }, + { + "event" : "content-wrap", + "frames" : { + "QA content main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA exclusive series" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 285 + }, + "QA other content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 357 + } + }, + "inputIDs" : [ + 1, + 2 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "content-wrap.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 908685.84615382203, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 908685.85791977204, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + } + ], + "scenario" : "content-pair", + "time" : 32.456335691036656, + "width" : 375 + }, + { + "event" : "content-light", + "frames" : { + "QA content main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA exclusive series" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 285 + }, + "QA other content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 357 + } + }, + "inputIDs" : [ + 1, + 2 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "content-light.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 908685.84615382203, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 908685.85791977204, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + } + ], + "scenario" : "content-pair", + "time" : 32.838776194024831, + "width" : 375 + }, + { + "event" : "content-empty", + "frames" : { + "QA content main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA exclusive series" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 136 + }, + "QA other content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 208 + } + }, + "inputIDs" : [ + + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "content-empty.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 908685.84615382203, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 908685.85791977204, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + } + ], + "scenario" : "content-pair", + "time" : 33.321386013063602, + "width" : 375 + }, + { + "event" : "content-one", + "frames" : { + "QA content main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA exclusive series" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 285 + }, + "QA other content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 357 + } + }, + "inputIDs" : [ + 3 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "content-one.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 908685.84615382203, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 908685.85791977204, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 908698.26122485602, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + } + ], + "scenario" : "content-pair", + "time" : 34.027152971015312, + "width" : 375 + }, + { + "event" : "content-one-after-6s", + "frames" : { + "QA content main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA exclusive series" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 285 + }, + "QA other content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 357 + } + }, + "inputIDs" : [ + 3 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "content-one-after-6s.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 908685.84615382203, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 908685.85791977204, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 908698.26122485602, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + } + ], + "scenario" : "content-pair", + "time" : 40.096118816058151, + "width" : 375 + }, + { + "event" : "content-resize-320", + "frames" : { + "QA content main banner" : { + "height" : 48, + "width" : 320, + "x" : 0, + "y" : 64 + }, + "QA exclusive series" : { + "height" : 48, + "width" : 320, + "x" : 0, + "y" : 266.66666666666669 + }, + "QA other content" : { + "height" : 48, + "width" : 320, + "x" : 0, + "y" : 338.66666666666669 + } + }, + "inputIDs" : [ + 3 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "content-resize-320.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 908685.84615382203, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 908685.85791977204, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 908698.26122485602, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + } + ], + "scenario" : "content-pair", + "time" : 40.695159611059353, + "width" : 320 + }, + { + "context" : "direct", + "count" : 2, + "event" : "scenario-start", + "scenario" : "delayed-current", + "time" : 41.169721193029545 + }, + { + "event" : "environment", + "phase" : "active", + "reduceMotion" : false, + "scenario" : "delayed-current", + "time" : 41.184685329091735 + }, + { + "event" : "delayed-no-invented-height", + "frames" : { + "carousel" : { + "height" : 0, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 140 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 10, + 11 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "delayed-no-invented-height.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 908685.84615382203, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 908685.85791977204, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 908698.26122485602, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 908706.11938407004, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 908706.12294394802, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + } + ], + "scenario" : "delayed-current", + "time" : 41.880507880006917, + "width" : 375 + }, + { + "event" : "delayed-next-valid", + "frames" : { + "carousel" : { + "height" : 187.5, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 327.5 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 10, + 11 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "delayed-next-valid.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 908685.84615382203, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 908685.85791977204, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 908698.26122485602, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 908706.11938407004, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 908706.12294394802, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + } + ], + "scenario" : "delayed-current", + "time" : 47.617577839060687, + "width" : 375 + }, + { + "event" : "delayed-old-image-arrived", + "frames" : { + "carousel" : { + "height" : 187.5, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 327.5 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 10, + 11 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "delayed-old-image-arrived.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 908685.84615382203, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 908685.85791977204, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 908698.26122485602, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 908706.11938407004, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 908706.12294394802, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + } + ], + "scenario" : "delayed-current", + "time" : 50.58794983709231, + "width" : 375 + }, + { + "context" : "content", + "count" : 2, + "event" : "scenario-start", + "scenario" : "failure-isolation", + "time" : 50.744479567976668 + }, + { + "event" : "environment", + "phase" : "active", + "reduceMotion" : false, + "scenario" : "failure-isolation", + "time" : 50.766854675021023 + }, + { + "event" : "failure-valid-first", + "frames" : { + "QA content main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA exclusive series" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 285 + }, + "QA other content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 357 + } + }, + "inputIDs" : [ + 20, + 21 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "failure-valid-first.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 908685.84615382203, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 908685.85791977204, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 908698.26122485602, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 908706.11938407004, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 908706.12294394802, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 908715.70961248607, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 908715.72577189608, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + } + ], + "scenario" : "failure-isolation", + "time" : 51.685883636004291, + "width" : 375 + }, + { + "event" : "failure-placeholder-other-content", + "frames" : { + "QA content main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA exclusive series" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 285 + }, + "QA other content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 357 + } + }, + "inputIDs" : [ + 20, + 21 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "failure-placeholder-other-content.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 908685.84615382203, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 908685.85791977204, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 908698.26122485602, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 908706.11938407004, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 908706.12294394802, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 908715.70961248607, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 908715.72577189608, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + } + ], + "scenario" : "failure-isolation", + "time" : 57.009656295995228, + "width" : 375 + }, + { + "event" : "failure-wrap-valid", + "frames" : { + "QA content main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA exclusive series" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 285 + }, + "QA other content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 357 + } + }, + "inputIDs" : [ + 20, + 21 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "failure-wrap-valid.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 908685.84615382203, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 908685.85791977204, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 908698.26122485602, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 908706.11938407004, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 908706.12294394802, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 908715.70961248607, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 908715.72577189608, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + } + ], + "scenario" : "failure-isolation", + "time" : 62.447507189004682, + "width" : 375 + }, + { + "context" : "direct", + "count" : 2, + "event" : "scenario-start", + "scenario" : "inactive-remount", + "time" : 62.611872237059288 + }, + { + "event" : "environment", + "phase" : "active", + "reduceMotion" : false, + "scenario" : "inactive-remount", + "time" : 62.637133894022554 + }, + { + "event" : "inactive-before", + "frames" : { + "carousel" : { + "height" : 187.5, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 327.5 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 30, + 31 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "inactive-before.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 908685.84615382203, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 908685.85791977204, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 908698.26122485602, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 908706.11938407004, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 908706.12294394802, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 908715.70961248607, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 908715.72577189608, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + }, + { + "uptime" : 908727.58220777905, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-wide.png" + }, + { + "uptime" : 908727.60921089805, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-tall.png" + } + ], + "scenario" : "inactive-remount", + "time" : 68.770060950075276, + "width" : 375 + }, + { + "event" : "inject-inactive", + "scenario" : "inactive-remount", + "time" : 68.778873589006253 + }, + { + "event" : "phase", + "scenario" : "inactive-remount", + "time" : 68.797315728035755, + "value" : "inactive" + }, + { + "event" : "inactive-start", + "frames" : { + "carousel" : { + "height" : 187.5, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 327.5 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 30, + 31 + ], + "osReduceMotion" : false, + "phase" : "inactive", + "png" : "inactive-start.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 908685.84615382203, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 908685.85791977204, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 908698.26122485602, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 908706.11938407004, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 908706.12294394802, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 908715.70961248607, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 908715.72577189608, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + }, + { + "uptime" : 908727.58220777905, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-wide.png" + }, + { + "uptime" : 908727.60921089805, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-tall.png" + } + ], + "scenario" : "inactive-remount", + "time" : 69.218511526007205, + "width" : 375 + }, + { + "event" : "inactive-after-6s", + "frames" : { + "carousel" : { + "height" : 187.5, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 327.5 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 30, + 31 + ], + "osReduceMotion" : false, + "phase" : "inactive", + "png" : "inactive-after-6s.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 908685.84615382203, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 908685.85791977204, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 908698.26122485602, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 908706.11938407004, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 908706.12294394802, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 908715.70961248607, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 908715.72577189608, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + }, + { + "uptime" : 908727.58220777905, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-wide.png" + }, + { + "uptime" : 908727.60921089805, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-tall.png" + } + ], + "scenario" : "inactive-remount", + "time" : 75.730925703071989, + "width" : 375 + }, + { + "event" : "inject-active", + "scenario" : "inactive-remount", + "time" : 75.743529628030956 + }, + { + "event" : "phase", + "scenario" : "inactive-remount", + "time" : 75.766396642080508, + "value" : "active" + }, + { + "event" : "active-before-5s", + "frames" : { + "carousel" : { + "height" : 187.5, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 327.5 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 30, + 31 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "active-before-5s.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 908685.84615382203, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 908685.85791977204, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 908698.26122485602, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 908706.11938407004, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 908706.12294394802, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 908715.70961248607, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 908715.72577189608, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + }, + { + "uptime" : 908727.58220777905, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-wide.png" + }, + { + "uptime" : 908727.60921089805, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-tall.png" + } + ], + "scenario" : "inactive-remount", + "time" : 80.580623895977624, + "width" : 375 + }, + { + "event" : "active-next", + "frames" : { + "carousel" : { + "height" : 125, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 265 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 30, + 31 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "active-next.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 908685.84615382203, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 908685.85791977204, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 908698.26122485602, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 908706.11938407004, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 908706.12294394802, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 908715.70961248607, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 908715.72577189608, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + }, + { + "uptime" : 908727.58220777905, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-wide.png" + }, + { + "uptime" : 908727.60921089805, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-tall.png" + } + ], + "scenario" : "inactive-remount", + "time" : 82.088875408051535, + "width" : 375 + }, + { + "event" : "unmount", + "scenario" : "inactive-remount", + "time" : 82.101702969055623 + }, + { + "event" : "unmounted-after-6s", + "frames" : { + + }, + "inputIDs" : [ + 30, + 31 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "unmounted-after-6s.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 908685.84615382203, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 908685.85791977204, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 908698.26122485602, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 908706.11938407004, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 908706.12294394802, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 908715.70961248607, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 908715.72577189608, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + }, + { + "uptime" : 908727.58220777905, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-wide.png" + }, + { + "uptime" : 908727.60921089805, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-tall.png" + } + ], + "scenario" : "inactive-remount", + "time" : 88.621842972002923, + "width" : 375 + }, + { + "event" : "remount", + "scenario" : "inactive-remount", + "time" : 88.63599985500332 + }, + { + "event" : "environment", + "phase" : "active", + "reduceMotion" : false, + "scenario" : "inactive-remount", + "time" : 88.666512762079947 + }, + { + "event" : "remount-first", + "frames" : { + "carousel" : { + "height" : 125, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 265 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 30, + 31 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "remount-first.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 908685.84615382203, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 908685.85791977204, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 908698.26122485602, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 908706.11938407004, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 908706.12294394802, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 908715.70961248607, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 908715.72577189608, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + }, + { + "uptime" : 908727.58220777905, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-wide.png" + }, + { + "uptime" : 908727.60921089805, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-tall.png" + } + ], + "scenario" : "inactive-remount", + "time" : 89.398839220986702, + "width" : 375 + }, + { + "event" : "remount-next", + "frames" : { + "carousel" : { + "height" : 187.5, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 327.5 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 30, + 31 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "remount-next.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 908685.84615382203, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 908685.85791977204, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 908698.26122485602, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 908706.11938407004, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 908706.12294394802, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 908715.70961248607, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 908715.72577189608, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + }, + { + "uptime" : 908727.58220777905, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-wide.png" + }, + { + "uptime" : 908727.60921089805, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-tall.png" + } + ], + "scenario" : "inactive-remount", + "time" : 94.61704468599055, + "width" : 375 + }, + { + "event" : "shrink-one", + "frames" : { + "carousel" : { + "height" : 125, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 265 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 32 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "shrink-one.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 908685.84615382203, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 908685.85791977204, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 908698.26122485602, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 908706.11938407004, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 908706.12294394802, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 908715.70961248607, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 908715.72577189608, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + }, + { + "uptime" : 908727.58220777905, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-wide.png" + }, + { + "uptime" : 908727.60921089805, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-tall.png" + }, + { + "uptime" : 908759.57740870106, + "url" : "https:\/\/subbanner.qa.invalid\/shrink-wide.png" + } + ], + "scenario" : "inactive-remount", + "time" : 95.369454623083584, + "width" : 375 + }, + { + "context" : "direct", + "count" : 2, + "event" : "scenario-start", + "scenario" : "reduced-motion", + "time" : 95.539546909043565 + }, + { + "enabledTest" : "NOT RUN: read-only SwiftUI environment; parent device preferences unchanged", + "event" : "reduce-motion-observation-only", + "osValue" : false, + "scenario" : "reduced-motion", + "time" : 95.549644447048195 + }, + { + "event" : "environment", + "phase" : "active", + "reduceMotion" : false, + "scenario" : "reduced-motion", + "time" : 95.577387058991008 + }, + { + "event" : "reduce-first", + "frames" : { + "carousel" : { + "height" : 125, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 265 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 40, + 41 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "reduce-first.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 908685.84615382203, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 908685.85791977204, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 908698.26122485602, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 908706.11938407004, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 908706.12294394802, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 908715.70961248607, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 908715.72577189608, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + }, + { + "uptime" : 908727.58220777905, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-wide.png" + }, + { + "uptime" : 908727.60921089805, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-tall.png" + }, + { + "uptime" : 908759.57740870106, + "url" : "https:\/\/subbanner.qa.invalid\/shrink-wide.png" + }, + { + "uptime" : 908760.52388543508, + "url" : "https:\/\/subbanner.qa.invalid\/reduce-wide.png" + }, + { + "uptime" : 908760.55114763207, + "url" : "https:\/\/subbanner.qa.invalid\/reduce-tall.png" + } + ], + "scenario" : "reduced-motion", + "time" : 96.410552200046368, + "width" : 375 + }, + { + "event" : "reduce-transition", + "frames" : { + "carousel" : { + "height" : 125, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 265 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 40, + 41 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "reduce-transition.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 908685.84615382203, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 908685.85791977204, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 908698.26122485602, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 908706.11938407004, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 908706.12294394802, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 908715.70961248607, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 908715.72577189608, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + }, + { + "uptime" : 908727.58220777905, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-wide.png" + }, + { + "uptime" : 908727.60921089805, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-tall.png" + }, + { + "uptime" : 908759.57740870106, + "url" : "https:\/\/subbanner.qa.invalid\/shrink-wide.png" + }, + { + "uptime" : 908760.52388543508, + "url" : "https:\/\/subbanner.qa.invalid\/reduce-wide.png" + }, + { + "uptime" : 908760.55114763207, + "url" : "https:\/\/subbanner.qa.invalid\/reduce-tall.png" + } + ], + "scenario" : "reduced-motion", + "time" : 101.02749455801677, + "width" : 375 + }, + { + "event" : "reduce-second", + "frames" : { + "carousel" : { + "height" : 187.5, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 327.5 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 40, + 41 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "reduce-second.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 908685.84615382203, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 908685.85791977204, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 908698.26122485602, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 908706.11938407004, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 908706.12294394802, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 908715.70961248607, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 908715.72577189608, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + }, + { + "uptime" : 908727.58220777905, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-wide.png" + }, + { + "uptime" : 908727.60921089805, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-tall.png" + }, + { + "uptime" : 908759.57740870106, + "url" : "https:\/\/subbanner.qa.invalid\/shrink-wide.png" + }, + { + "uptime" : 908760.52388543508, + "url" : "https:\/\/subbanner.qa.invalid\/reduce-wide.png" + }, + { + "uptime" : 908760.55114763207, + "url" : "https:\/\/subbanner.qa.invalid\/reduce-tall.png" + } + ], + "scenario" : "reduced-motion", + "time" : 101.90480845898855, + "width" : 375 + }, + { + "context" : "direct", + "count" : 21, + "event" : "scenario-start", + "scenario" : "twenty-one", + "time" : 102.07294997002464 + }, + { + "event" : "environment", + "phase" : "active", + "reduceMotion" : false, + "scenario" : "twenty-one", + "time" : 102.20261308399495 + }, + { + "event" : "many-first", + "frames" : { + "carousel" : { + "height" : 125, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 265 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "many-first.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 908685.84615382203, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 908685.85791977204, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 908698.26122485602, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 908706.11938407004, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 908706.12294394802, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 908715.70961248607, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 908715.72577189608, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + }, + { + "uptime" : 908727.58220777905, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-wide.png" + }, + { + "uptime" : 908727.60921089805, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-tall.png" + }, + { + "uptime" : 908759.57740870106, + "url" : "https:\/\/subbanner.qa.invalid\/shrink-wide.png" + }, + { + "uptime" : 908760.52388543508, + "url" : "https:\/\/subbanner.qa.invalid\/reduce-wide.png" + }, + { + "uptime" : 908760.55114763207, + "url" : "https:\/\/subbanner.qa.invalid\/reduce-tall.png" + }, + { + "uptime" : 908767.1791987851, + "url" : "https:\/\/subbanner.qa.invalid\/many-1-wide.png" + }, + { + "uptime" : 908767.18303027609, + "url" : "https:\/\/subbanner.qa.invalid\/many-2-wide.png" + }, + { + "uptime" : 908767.25183598604, + "url" : "https:\/\/subbanner.qa.invalid\/many-3-wide.png" + }, + { + "uptime" : 908767.26066636702, + "url" : "https:\/\/subbanner.qa.invalid\/many-4-wide.png" + }, + { + "uptime" : 908767.32109598711, + "url" : "https:\/\/subbanner.qa.invalid\/many-5-wide.png" + }, + { + "uptime" : 908767.35179803101, + "url" : "https:\/\/subbanner.qa.invalid\/many-6-wide.png" + }, + { + "uptime" : 908767.37824751809, + "url" : "https:\/\/subbanner.qa.invalid\/many-7-wide.png" + }, + { + "uptime" : 908767.41250117309, + "url" : "https:\/\/subbanner.qa.invalid\/many-8-wide.png" + }, + { + "uptime" : 908767.45490139804, + "url" : "https:\/\/subbanner.qa.invalid\/many-9-wide.png" + }, + { + "uptime" : 908767.49606595107, + "url" : "https:\/\/subbanner.qa.invalid\/many-10-wide.png" + }, + { + "uptime" : 908767.5383930651, + "url" : "https:\/\/subbanner.qa.invalid\/many-11-wide.png" + }, + { + "uptime" : 908767.57563595707, + "url" : "https:\/\/subbanner.qa.invalid\/many-12-wide.png" + }, + { + "uptime" : 908767.61077967007, + "url" : "https:\/\/subbanner.qa.invalid\/many-13-wide.png" + }, + { + "uptime" : 908767.65223795804, + "url" : "https:\/\/subbanner.qa.invalid\/many-14-wide.png" + }, + { + "uptime" : 908767.70145239402, + "url" : "https:\/\/subbanner.qa.invalid\/many-15-wide.png" + }, + { + "uptime" : 908767.77031225711, + "url" : "https:\/\/subbanner.qa.invalid\/many-16-wide.png" + }, + { + "uptime" : 908767.82830621605, + "url" : "https:\/\/subbanner.qa.invalid\/many-17-wide.png" + } + ], + "scenario" : "twenty-one", + "time" : 103.03679088607896, + "width" : 375 + }, + { + "event" : "many-twenty-first", + "frames" : { + "carousel" : { + "height" : 125, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 265 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "many-twenty-first.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 908685.84615382203, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 908685.85791977204, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 908698.26122485602, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 908706.11938407004, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 908706.12294394802, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 908715.70961248607, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 908715.72577189608, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + }, + { + "uptime" : 908727.58220777905, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-wide.png" + }, + { + "uptime" : 908727.60921089805, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-tall.png" + }, + { + "uptime" : 908759.57740870106, + "url" : "https:\/\/subbanner.qa.invalid\/shrink-wide.png" + }, + { + "uptime" : 908760.52388543508, + "url" : "https:\/\/subbanner.qa.invalid\/reduce-wide.png" + }, + { + "uptime" : 908760.55114763207, + "url" : "https:\/\/subbanner.qa.invalid\/reduce-tall.png" + }, + { + "uptime" : 908767.1791987851, + "url" : "https:\/\/subbanner.qa.invalid\/many-1-wide.png" + }, + { + "uptime" : 908767.18303027609, + "url" : "https:\/\/subbanner.qa.invalid\/many-2-wide.png" + }, + { + "uptime" : 908767.25183598604, + "url" : "https:\/\/subbanner.qa.invalid\/many-3-wide.png" + }, + { + "uptime" : 908767.26066636702, + "url" : "https:\/\/subbanner.qa.invalid\/many-4-wide.png" + }, + { + "uptime" : 908767.32109598711, + "url" : "https:\/\/subbanner.qa.invalid\/many-5-wide.png" + }, + { + "uptime" : 908767.35179803101, + "url" : "https:\/\/subbanner.qa.invalid\/many-6-wide.png" + }, + { + "uptime" : 908767.37824751809, + "url" : "https:\/\/subbanner.qa.invalid\/many-7-wide.png" + }, + { + "uptime" : 908767.41250117309, + "url" : "https:\/\/subbanner.qa.invalid\/many-8-wide.png" + }, + { + "uptime" : 908767.45490139804, + "url" : "https:\/\/subbanner.qa.invalid\/many-9-wide.png" + }, + { + "uptime" : 908767.49606595107, + "url" : "https:\/\/subbanner.qa.invalid\/many-10-wide.png" + }, + { + "uptime" : 908767.5383930651, + "url" : "https:\/\/subbanner.qa.invalid\/many-11-wide.png" + }, + { + "uptime" : 908767.57563595707, + "url" : "https:\/\/subbanner.qa.invalid\/many-12-wide.png" + }, + { + "uptime" : 908767.61077967007, + "url" : "https:\/\/subbanner.qa.invalid\/many-13-wide.png" + }, + { + "uptime" : 908767.65223795804, + "url" : "https:\/\/subbanner.qa.invalid\/many-14-wide.png" + }, + { + "uptime" : 908767.70145239402, + "url" : "https:\/\/subbanner.qa.invalid\/many-15-wide.png" + }, + { + "uptime" : 908767.77031225711, + "url" : "https:\/\/subbanner.qa.invalid\/many-16-wide.png" + }, + { + "uptime" : 908767.82830621605, + "url" : "https:\/\/subbanner.qa.invalid\/many-17-wide.png" + }, + { + "uptime" : 908767.98828955006, + "url" : "https:\/\/subbanner.qa.invalid\/many-18-wide.png" + }, + { + "uptime" : 908767.98892772105, + "url" : "https:\/\/subbanner.qa.invalid\/many-19-wide.png" + }, + { + "uptime" : 908767.98947732104, + "url" : "https:\/\/subbanner.qa.invalid\/many-20-wide.png" + }, + { + "uptime" : 908768.02456418809, + "url" : "https:\/\/subbanner.qa.invalid\/many-21-wide.png" + } + ], + "scenario" : "twenty-one", + "time" : 205.85914246598259, + "width" : 375 + }, + { + "event" : "many-wrap", + "frames" : { + "carousel" : { + "height" : 125, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 265 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "many-wrap.png", + "requests" : [ + { + "uptime" : 908666.28223367606, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 908666.29665209609, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 908678.04099624709, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 908685.84615382203, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 908685.85791977204, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 908698.26122485602, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 908706.11938407004, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 908706.12294394802, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 908715.70961248607, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 908715.72577189608, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + }, + { + "uptime" : 908727.58220777905, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-wide.png" + }, + { + "uptime" : 908727.60921089805, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-tall.png" + }, + { + "uptime" : 908759.57740870106, + "url" : "https:\/\/subbanner.qa.invalid\/shrink-wide.png" + }, + { + "uptime" : 908760.52388543508, + "url" : "https:\/\/subbanner.qa.invalid\/reduce-wide.png" + }, + { + "uptime" : 908760.55114763207, + "url" : "https:\/\/subbanner.qa.invalid\/reduce-tall.png" + }, + { + "uptime" : 908767.1791987851, + "url" : "https:\/\/subbanner.qa.invalid\/many-1-wide.png" + }, + { + "uptime" : 908767.18303027609, + "url" : "https:\/\/subbanner.qa.invalid\/many-2-wide.png" + }, + { + "uptime" : 908767.25183598604, + "url" : "https:\/\/subbanner.qa.invalid\/many-3-wide.png" + }, + { + "uptime" : 908767.26066636702, + "url" : "https:\/\/subbanner.qa.invalid\/many-4-wide.png" + }, + { + "uptime" : 908767.32109598711, + "url" : "https:\/\/subbanner.qa.invalid\/many-5-wide.png" + }, + { + "uptime" : 908767.35179803101, + "url" : "https:\/\/subbanner.qa.invalid\/many-6-wide.png" + }, + { + "uptime" : 908767.37824751809, + "url" : "https:\/\/subbanner.qa.invalid\/many-7-wide.png" + }, + { + "uptime" : 908767.41250117309, + "url" : "https:\/\/subbanner.qa.invalid\/many-8-wide.png" + }, + { + "uptime" : 908767.45490139804, + "url" : "https:\/\/subbanner.qa.invalid\/many-9-wide.png" + }, + { + "uptime" : 908767.49606595107, + "url" : "https:\/\/subbanner.qa.invalid\/many-10-wide.png" + }, + { + "uptime" : 908767.5383930651, + "url" : "https:\/\/subbanner.qa.invalid\/many-11-wide.png" + }, + { + "uptime" : 908767.57563595707, + "url" : "https:\/\/subbanner.qa.invalid\/many-12-wide.png" + }, + { + "uptime" : 908767.61077967007, + "url" : "https:\/\/subbanner.qa.invalid\/many-13-wide.png" + }, + { + "uptime" : 908767.65223795804, + "url" : "https:\/\/subbanner.qa.invalid\/many-14-wide.png" + }, + { + "uptime" : 908767.70145239402, + "url" : "https:\/\/subbanner.qa.invalid\/many-15-wide.png" + }, + { + "uptime" : 908767.77031225711, + "url" : "https:\/\/subbanner.qa.invalid\/many-16-wide.png" + }, + { + "uptime" : 908767.82830621605, + "url" : "https:\/\/subbanner.qa.invalid\/many-17-wide.png" + }, + { + "uptime" : 908767.98828955006, + "url" : "https:\/\/subbanner.qa.invalid\/many-18-wide.png" + }, + { + "uptime" : 908767.98892772105, + "url" : "https:\/\/subbanner.qa.invalid\/many-19-wide.png" + }, + { + "uptime" : 908767.98947732104, + "url" : "https:\/\/subbanner.qa.invalid\/many-20-wide.png" + }, + { + "uptime" : 908768.02456418809, + "url" : "https:\/\/subbanner.qa.invalid\/many-21-wide.png" + } + ], + "scenario" : "twenty-one", + "time" : 210.93713529105298, + "width" : 375 + }, + { + "attempts" : [ + "sodaqa:\/\/banner\/900", + "https:\/\/subbanner.qa.invalid\/destination" + ], + "event" : "opener-driver-not-physical-tap", + "scenario" : "twenty-one", + "time" : 210.94510213204194 + }, + { + "event" : "finished", + "scenario" : "twenty-one", + "time" : 210.95250056800433 + } +] \ No newline at end of file diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/active-before-5s.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/active-before-5s.png new file mode 100644 index 00000000..cf168108 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/active-before-5s.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/active-next.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/active-next.png new file mode 100644 index 00000000..96c0c986 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/active-next.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/content-before-5s.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/content-before-5s.png new file mode 100644 index 00000000..dd8b8726 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/content-before-5s.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/content-empty.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/content-empty.png new file mode 100644 index 00000000..d5059328 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/content-empty.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/content-first.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/content-first.png new file mode 100644 index 00000000..dd8b8726 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/content-first.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/content-light.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/content-light.png new file mode 100644 index 00000000..dd8b8726 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/content-light.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/content-one-after-6s.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/content-one-after-6s.png new file mode 100644 index 00000000..c33788ac Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/content-one-after-6s.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/content-one.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/content-one.png new file mode 100644 index 00000000..c33788ac Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/content-one.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/content-resize-320.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/content-resize-320.png new file mode 100644 index 00000000..678c3402 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/content-resize-320.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/content-second.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/content-second.png new file mode 100644 index 00000000..688c60f3 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/content-second.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/content-transition.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/content-transition.png new file mode 100644 index 00000000..e34f2558 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/content-transition.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/content-wrap.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/content-wrap.png new file mode 100644 index 00000000..dd8b8726 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/content-wrap.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/delayed-next-valid.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/delayed-next-valid.png new file mode 100644 index 00000000..62de0ec7 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/delayed-next-valid.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/delayed-no-invented-height.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/delayed-no-invented-height.png new file mode 100644 index 00000000..a710199b Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/delayed-no-invented-height.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/delayed-old-image-arrived.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/delayed-old-image-arrived.png new file mode 100644 index 00000000..62de0ec7 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/delayed-old-image-arrived.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/failure-placeholder-other-content.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/failure-placeholder-other-content.png new file mode 100644 index 00000000..34a17e61 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/failure-placeholder-other-content.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/failure-valid-first.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/failure-valid-first.png new file mode 100644 index 00000000..2c6fd408 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/failure-valid-first.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/failure-wrap-valid.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/failure-wrap-valid.png new file mode 100644 index 00000000..2c6fd408 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/failure-wrap-valid.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/home-before-5s.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/home-before-5s.png new file mode 100644 index 00000000..6f972ff3 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/home-before-5s.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/home-empty.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/home-empty.png new file mode 100644 index 00000000..b10e190d Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/home-empty.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/home-first.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/home-first.png new file mode 100644 index 00000000..6f972ff3 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/home-first.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/home-light.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/home-light.png new file mode 100644 index 00000000..6f972ff3 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/home-light.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/home-one-after-6s.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/home-one-after-6s.png new file mode 100644 index 00000000..885209f1 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/home-one-after-6s.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/home-one.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/home-one.png new file mode 100644 index 00000000..885209f1 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/home-one.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/home-resize-320.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/home-resize-320.png new file mode 100644 index 00000000..23868198 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/home-resize-320.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/home-second.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/home-second.png new file mode 100644 index 00000000..f96f82b8 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/home-second.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/home-transition.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/home-transition.png new file mode 100644 index 00000000..2175ffd2 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/home-transition.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/home-wrap.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/home-wrap.png new file mode 100644 index 00000000..6f972ff3 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/home-wrap.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/inactive-after-6s.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/inactive-after-6s.png new file mode 100644 index 00000000..cf168108 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/inactive-after-6s.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/inactive-before.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/inactive-before.png new file mode 100644 index 00000000..cf168108 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/inactive-before.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/inactive-start.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/inactive-start.png new file mode 100644 index 00000000..cf168108 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/inactive-start.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/many-first.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/many-first.png new file mode 100644 index 00000000..2be3d4d5 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/many-first.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/many-twenty-first.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/many-twenty-first.png new file mode 100644 index 00000000..fc8745ee Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/many-twenty-first.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/many-wrap.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/many-wrap.png new file mode 100644 index 00000000..8fb62b1e Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/many-wrap.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/reduce-first.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/reduce-first.png new file mode 100644 index 00000000..635a7d03 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/reduce-first.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/reduce-second.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/reduce-second.png new file mode 100644 index 00000000..bbe0cee2 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/reduce-second.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/reduce-transition.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/reduce-transition.png new file mode 100644 index 00000000..635a7d03 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/reduce-transition.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/remount-first.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/remount-first.png new file mode 100644 index 00000000..96c0c986 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/remount-first.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/remount-next.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/remount-next.png new file mode 100644 index 00000000..cf168108 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/remount-next.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/shrink-one.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/shrink-one.png new file mode 100644 index 00000000..325bbad4 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/shrink-one.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/unmounted-after-6s.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/unmounted-after-6s.png new file mode 100644 index 00000000..576d5ee5 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/captures/unmounted-after-6s.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/cleanup.json b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/cleanup.json new file mode 100644 index 00000000..a15a019b --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/cleanup.json @@ -0,0 +1,72 @@ +{ + "commands": [ + { + "args": [ + "xcrun", + "simctl", + "terminate", + "482F7827-2E3A-4559-BAF4-48B68CC5725B", + "qa.sodalive.subbanner.native.final.97cQKj" + ], + "exit": 0, + "stdout": "", + "stderr": "" + }, + { + "args": [ + "xcrun", + "simctl", + "uninstall", + "482F7827-2E3A-4559-BAF4-48B68CC5725B", + "qa.sodalive.subbanner.native.final.97cQKj" + ], + "exit": 0, + "stdout": "", + "stderr": "" + }, + { + "args": [ + "xcrun", + "simctl", + "get_app_container", + "482F7827-2E3A-4559-BAF4-48B68CC5725B", + "qa.sodalive.subbanner.native.final.97cQKj", + "data" + ], + "exit": 2, + "stdout": "", + "stderr": "An error was encountered processing the command (domain=NSPOSIXErrorDomain, code=2):\nThe operation couldn\u2019t be completed. No such file or directory\nNo such file or directory\n" + }, + { + "args": [ + "ps", + "-p", + "10884", + "-o", + "pid=,comm=" + ], + "exit": 1, + "stdout": "", + "stderr": "" + }, + { + "args": [ + "xcrun", + "simctl", + "list", + "devices", + "booted", + "-j" + ], + "exit": 0, + "stdout": "{\n \"devices\" : {\n \"com.apple.CoreSimulator.SimRuntime.iOS-17-2\" : [\n\n ],\n \"com.apple.CoreSimulator.SimRuntime.iOS-26-0\" : [\n\n ],\n \"com.apple.CoreSimulator.SimRuntime.iOS-18-3\" : [\n {\n \"lastBootedAt\" : \"2026-10-29T14:27:43Z\",\n \"dataPath\" : \"\\/Users\\/klaus\\/Library\\/Developer\\/CoreSimulator\\/Devices\\/482F7827-2E3A-4559-BAF4-48B68CC5725B\\/data\",\n \"dataPathSize\" : 1322459136,\n \"logPath\" : \"\\/Users\\/klaus\\/Library\\/Logs\\/CoreSimulator\\/482F7827-2E3A-4559-BAF4-48B68CC5725B\",\n \"udid\" : \"482F7827-2E3A-4559-BAF4-48B68CC5725B\",\n \"isAvailable\" : true,\n \"logPathSize\" : 258048,\n \"deviceTypeIdentifier\" : \"com.apple.CoreSimulator.SimDeviceType.iPhone-SE-3rd-generation\",\n \"state\" : \"Booted\",\n \"name\" : \"SubBannerQA-20261029\"\n }\n ],\n \"com.apple.CoreSimulator.SimRuntime.xrOS-1-0\" : [\n\n ],\n \"com.apple.CoreSimulator.SimRuntime.iOS-14-4\" : [\n\n ],\n \"com.apple.CoreSimulator.SimRuntime.iOS-15-0\" : [\n\n ],\n \"com.apple.CoreSimulator.SimRuntime.iOS-15-5\" : [\n\n ],\n \"com.apple.CoreSimulator.SimRuntime.iOS-17-5\" : [\n\n ],\n \"com.apple.CoreSimulator.SimRuntime.iOS-13-7\" : [\n\n ]\n }\n}\n", + "stderr": "" + } + ], + "appAbsent": true, + "pidAbsent": true, + "rootAbsent": true, + "parentDeviceStillBooted": true, + "productHashesUnchanged": true, + "sharedCacheDeleted": false +} \ No newline at end of file diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/commands.json b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/commands.json new file mode 100644 index 00000000..f54e78ab --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/commands.json @@ -0,0 +1,98 @@ +[ + { + "args": [ + "xcrun", + "--sdk", + "iphonesimulator", + "--show-sdk-path" + ], + "exit": 0, + "stdout": "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.0.sdk\n", + "stderr": "" + }, + { + "args": [ + "xcrun", + "--sdk", + "iphonesimulator", + "swiftc", + "-swift-version", + "5", + "-target", + "x86_64-apple-ios16.6-simulator", + "-sdk", + "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.0.sdk", + "-I", + "/Users/klaus/Library/Developer/Xcode/DerivedData/SodaLive-clzdtvqrsfonoxcijatzkrqdzrsu/Build/Products/Debug-iphonesimulator", + "-module-cache-path", + "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-native-final.97cQKj/module-cache", + "SodaLive/Sources/V2/Component/Banner/SubBanner.swift", + "SodaLive/Sources/V2/Component/Banner/SubBannerLinkOpener.swift", + "SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift", + "SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift", + "SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationView.swift", + "SodaLive/Sources/UI/Theme/Spacing.swift", + "SodaLive/Sources/UI/Theme/Color.swift", + "SodaLive/Sources/Extensions/ColorExtension.swift", + "SodaLive/Sources/Extensions/FontModifier.swift", + "/Users/klaus/Develop/sodalive/iOS/SodaLive/docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native-final/source/Doubles.swift", + "/Users/klaus/Develop/sodalive/iOS/SodaLive/docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native-final/source/Harness.swift", + "/Users/klaus/Library/Developer/Xcode/DerivedData/SodaLive-clzdtvqrsfonoxcijatzkrqdzrsu/Build/Products/Debug-iphonesimulator/Kingfisher.o", + "-o", + "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-native-final.97cQKj/SubBannerQA.app/SubBannerQA" + ], + "exit": 0, + "stdout": "", + "stderr": "" + }, + { + "args": [ + "xcrun", + "simctl", + "install", + "482F7827-2E3A-4559-BAF4-48B68CC5725B", + "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-native-final.97cQKj/SubBannerQA.app" + ], + "exit": 0, + "stdout": "", + "stderr": "" + }, + { + "args": [ + "xcrun", + "simctl", + "launch", + "482F7827-2E3A-4559-BAF4-48B68CC5725B", + "qa.sodalive.subbanner.native.final.97cQKj" + ], + "exit": 0, + "stdout": "qa.sodalive.subbanner.native.final.97cQKj: 10884\n", + "stderr": "" + }, + { + "args": [ + "xcrun", + "simctl", + "get_app_container", + "482F7827-2E3A-4559-BAF4-48B68CC5725B", + "qa.sodalive.subbanner.native.final.97cQKj", + "data" + ], + "exit": 0, + "stdout": "/Users/klaus/Library/Developer/CoreSimulator/Devices/482F7827-2E3A-4559-BAF4-48B68CC5725B/data/Containers/Data/Application/5DBF90FA-A107-4B83-B182-48D78D5F081E\n", + "stderr": "" + }, + { + "args": [ + "xcrun", + "simctl", + "io", + "482F7827-2E3A-4559-BAF4-48B68CC5725B", + "screenshot", + "/Users/klaus/Develop/sodalive/iOS/SodaLive/docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native-final/native-os-final.png" + ], + "exit": 0, + "stdout": "", + "stderr": "Detected file type 'PNG' from extension\nWrote screenshot to: /Users/klaus/Develop/sodalive/iOS/SodaLive/docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native-final/native-os-final.png\n" + } +] \ No newline at end of file diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/contact-1.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/contact-1.png new file mode 100644 index 00000000..00a07d4e Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/contact-1.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/contact-2.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/contact-2.png new file mode 100644 index 00000000..62ff1df1 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/contact-2.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/contact-3.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/contact-3.png new file mode 100644 index 00000000..0c7efa01 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/contact-3.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/contact-4.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/contact-4.png new file mode 100644 index 00000000..0b143272 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/contact-4.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/dependency-hashes.json b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/dependency-hashes.json new file mode 100644 index 00000000..45b4f5c7 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/dependency-hashes.json @@ -0,0 +1,4 @@ +{ + "/Users/klaus/Library/Developer/Xcode/DerivedData/SodaLive-clzdtvqrsfonoxcijatzkrqdzrsu/Build/Products/Debug-iphonesimulator/Kingfisher.o": "ab9481b5935132edf5744692d88ff4c3e34f1a68f4014f88267a5ad5d95f31b8", + "/Users/klaus/Library/Developer/Xcode/DerivedData/SodaLive-clzdtvqrsfonoxcijatzkrqdzrsu/Build/Products/Debug-iphonesimulator/Kingfisher.swiftmodule/x86_64-apple-ios-simulator.swiftmodule": "b155d956bfaa735f7c72f4630b5be0e2dc82fcb768c2c0ce930ab17faff22fdc" +} \ No newline at end of file diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/native-os-final.png b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/native-os-final.png new file mode 100644 index 00000000..5aa6148a Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/native-os-final.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/ownership.json b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/ownership.json new file mode 100644 index 00000000..e9d8ac1b --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/ownership.json @@ -0,0 +1,7 @@ +{ + "root": "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-native-final.97cQKj", + "bundle": "qa.sodalive.subbanner.native.final.97cQKj", + "pid": 10884, + "device": "482F7827-2E3A-4559-BAF4-48B68CC5725B", + "deviceOwner": "parent" +} \ No newline at end of file diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/production-source-snapshot/SodaLive/Sources/Extensions/ColorExtension.swift b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/production-source-snapshot/SodaLive/Sources/Extensions/ColorExtension.swift new file mode 100644 index 00000000..742e4ad8 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/production-source-snapshot/SodaLive/Sources/Extensions/ColorExtension.swift @@ -0,0 +1,40 @@ +// +// ColorExtension.swift +// SodaLive +// +// Created by klaus on 2023/08/09. +// +// https://seons-dev.tistory.com/174 +// + +import SwiftUI + +extension Color { + init(hex: String) { + let scanner = Scanner(string: hex) + _ = scanner.scanString("#") + + var rgb: UInt64 = 0 + scanner.scanHexInt64(&rgb) + + let r = Double((rgb >> 16) & 0xFF) / 255.0 + let g = Double((rgb >> 8) & 0xFF) / 255.0 + let b = Double((rgb >> 0) & 0xFF) / 255.0 + self.init(red: r, green: g, blue: b) + } +} + +extension UIColor { + convenience init(hex: String, alpha: CGFloat = 1.0) { + let scanner = Scanner(string: hex) + _ = scanner.scanString("#") + + var rgb: UInt64 = 0 + scanner.scanHexInt64(&rgb) + + let r = Double((rgb >> 16) & 0xFF) / 255.0 + let g = Double((rgb >> 8) & 0xFF) / 255.0 + let b = Double((rgb >> 0) & 0xFF) / 255.0 + self.init(red: r, green: g, blue: b, alpha: alpha) + } +} diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/production-source-snapshot/SodaLive/Sources/Extensions/FontModifier.swift b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/production-source-snapshot/SodaLive/Sources/Extensions/FontModifier.swift new file mode 100644 index 00000000..33ba84e5 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/production-source-snapshot/SodaLive/Sources/Extensions/FontModifier.swift @@ -0,0 +1,164 @@ +// +// FontModifier.swift +// SodaLive +// +// Created by klaus on 1/23/26. +// + +import SwiftUI + +enum SodaTypography { + case heading1 + case heading2 + case heading3 + case heading4 + case body1 + case body2 + case body3 + case body4 + case body5 + case body6 + case caption1 + case caption2 + case caption3 + + var size: CGFloat { + switch self { + case .heading1: + return 24 + case .heading2: + return 22 + case .heading3: + return 20 + case .heading4: + return 18 + case .body1, .body2, .body3: + return 16 + case .body4, .body5, .body6: + return 14 + case .caption1, .caption2, .caption3: + return 12 + } + } + + func localizedFontName(locale: Locale = .autoupdatingCurrent) -> String? { + fontName(for: .localized, weight: weight, locale: locale) + } + + var weight: SwiftUI.Font.Weight { + switch self { + case .heading1, .heading2, .heading3, .heading4, .body1, .body4, .caption1: + return .bold + case .body2, .body5, .caption2: + return .medium + case .body3, .body6, .caption3: + return .regular + } + } +} + +enum SodaFontFamily { + case localized + case pattaya + case phosphate +} + +private func resolvedLanguageCode(currentLocale: Locale) -> String? { + if let raw = UserDefaults.standard.string(forKey: "app.language"), + let option = LanguageOption(rawValue: raw), + option != .system { + return option.rawValue + } + return currentLocale.language.languageCode?.identifier +} + +private func koreanFontName(for weight: SwiftUI.Font.Weight) -> String { + switch weight { + case .bold: + return "Pretendard-Bold" + case .medium: + return "Pretendard-Medium" + case .light: + return "Pretendard-Light" + default: + return "Pretendard-Regular" + } +} + +private func japaneseFontName(for weight: SwiftUI.Font.Weight) -> String { + switch weight { + case .bold: + return "NotoSansJP-Bold" + case .medium: + return "NotoSansJP-Medium" + case .light: + return "NotoSansJP-Light" + default: + return "NotoSansJP-Regular" + } +} + +private func fontName(for family: SodaFontFamily, weight: SwiftUI.Font.Weight, locale: Locale) -> String? { + switch family { + case .localized: + switch resolvedLanguageCode(currentLocale: locale) { + case "ko": + return koreanFontName(for: weight) + case "ja": + return japaneseFontName(for: weight) + default: + return nil + } + case .pattaya: + return "Pattaya-Regular" + case .phosphate: + return "PhosphateSolid" + } +} + +struct FontModifier: ViewModifier { + @Environment(\.locale) private var locale + let size: CGFloat + let weight: SwiftUI.Font.Weight + let family: SodaFontFamily + + func body(content: Content) -> some View { + if let fontName = fontName(for: family, weight: weight, locale: locale) { + content.font(.custom(fontName, size: size)) + } else { + content.font(.system(size: size, weight: weight)) + } + } +} + +extension View { + func appFont( + size: CGFloat, + weight: SwiftUI.Font.Weight = .regular, + family: SodaFontFamily = .localized + ) -> some View { + self.modifier(FontModifier(size: size, weight: weight, family: family)) + } + + func appFont(_ typography: SodaTypography, family: SodaFontFamily = .localized) -> some View { + appFont(size: typography.size, weight: typography.weight, family: family) + } +} + +extension Text { + func appFont( + size: CGFloat, + weight: SwiftUI.Font.Weight = .regular, + family: SodaFontFamily = .localized + ) -> Text { + if let fontName = fontName(for: family, weight: weight, locale: .autoupdatingCurrent) { + return self.font(.custom(fontName, size: size)) + } + + return self.font(.system(size: size, weight: weight)) + } + + func appFont(_ typography: SodaTypography, family: SodaFontFamily = .localized) -> Text { + appFont(size: typography.size, weight: typography.weight, family: family) + } +} diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/production-source-snapshot/SodaLive/Sources/UI/Theme/Color.swift b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/production-source-snapshot/SodaLive/Sources/UI/Theme/Color.swift new file mode 100644 index 00000000..b64dea57 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/production-source-snapshot/SodaLive/Sources/UI/Theme/Color.swift @@ -0,0 +1,72 @@ +// +// Color.swift +// SodaLive +// +// Created by klaus on 2024/01/17. +// + +import Foundation +import SwiftUI + +extension Color { + static let main = Color(hex: "80D8FF") + static let sub = Color(hex: "1313BC") + static let button = Color(hex: "3bb9f1") + static let bg = Color(hex: "13181B") + static let gray11 = Color(hex: "111111") + static let gray16 = Color(hex: "161616") + static let gray22 = Color(hex: "222222") + static let gray23 = Color(hex: "232323") + static let gray30 = Color(hex: "303030") + static let gray33 = Color(hex: "333333") + static let gray52 = Color(hex: "525252") + static let gray55 = Color(hex: "555555") + static let gray59 = Color(hex: "595959") + static let gray77 = Color(hex: "777777") + static let gray90 = Color(hex: "909090") + static let gray97 = Color(hex: "979797") + static let graybb = Color(hex: "bbbbbb") + static let grayd2 = Color(hex: "d2d2d2") + static let grayd8 = Color(hex: "d8d8d8") + static let graye2 = Color(hex: "e2e2e2") + static let grayee = Color(hex: "eeeeee") + + static let mainRed = Color(hex: "ff5c49") + static let mainRed2 = Color(hex: "ea3a25") + static let mainRed3 = Color(hex: "dd4500") + static let mainYellow = Color(hex: "ffdc00") + + static let soda50 = Color(hex: "E6F9FF") + static let soda100 = Color(hex: "B8EEFD") + static let soda200 = Color(hex: "85E1FB") + static let soda300 = Color(hex: "4FD2F9") + static let soda400 = Color(hex: "00BDF7") + static let soda500 = Color(hex: "00A0DF") + static let soda600 = Color(hex: "0081C2") + static let soda700 = Color(hex: "006BA4") + static let soda800 = Color(hex: "004F7F") + static let soda900 = Color(hex: "052742") + + static let red50 = Color(hex: "FFF1F2") + static let red100 = Color(hex: "FFD7D9") + static let red200 = Color(hex: "FFB3B7") + static let red300 = Color(hex: "FF858B") + static let red400 = Color(hex: "FF4C3C") + + static let green50 = Color(hex: "F5FBEF") + static let green100 = Color(hex: "E9FADB") + static let green200 = Color(hex: "C0F595") + static let green300 = Color(hex: "9FFB56") + static let green400 = Color(hex: "73FF01") + + static let gray50 = Color(hex: "FAFAFA") + static let gray100 = Color(hex: "F2F2F2") + static let gray200 = Color(hex: "E2E2E2") + static let gray300 = Color(hex: "CCCCCC") + static let gray400 = Color(hex: "B5B5B5") + static let gray500 = Color(hex: "959595") + static let gray600 = Color(hex: "787878") + static let gray700 = Color(hex: "494949") + static let gray800 = Color(hex: "343434") + static let gray900 = Color(hex: "202020") +} diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/production-source-snapshot/SodaLive/Sources/UI/Theme/Spacing.swift b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/production-source-snapshot/SodaLive/Sources/UI/Theme/Spacing.swift new file mode 100644 index 00000000..d3d65ea3 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/production-source-snapshot/SodaLive/Sources/UI/Theme/Spacing.swift @@ -0,0 +1,20 @@ +// +// Spacing.swift +// SodaLive +// + +import SwiftUI + +enum SodaSpacing { + static let s4: CGFloat = 4 + static let s6: CGFloat = 6 + static let s8: CGFloat = 8 + static let s12: CGFloat = 12 + static let s14: CGFloat = 14 + static let s16: CGFloat = 16 + static let s20: CGFloat = 20 + static let s24: CGFloat = 24 + static let s28: CGFloat = 28 + static let s32: CGFloat = 32 + static let s48: CGFloat = 48 +} diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/production-source-snapshot/SodaLive/Sources/V2/Component/Banner/SubBanner.swift b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/production-source-snapshot/SodaLive/Sources/V2/Component/Banner/SubBanner.swift new file mode 100644 index 00000000..e9f1cd2f --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/production-source-snapshot/SodaLive/Sources/V2/Component/Banner/SubBanner.swift @@ -0,0 +1,42 @@ +import Foundation + +struct SubBanner: Decodable, Hashable, Identifiable { + enum Placement: String, Decodable { + case homeRecommendation = "HOME_RECOMMENDATION" + case contentRecommendation = "CONTENT_RECOMMENDATION" + } + + let id: Int + let placement: Placement + let imageUrl: String + let webUrl: String? + let androidDeepLink: String? + let iosDeepLink: String? + + var imageURL: URL? { Self.webURL(imageUrl) } + var webURL: URL? { Self.webURL(webUrl) } + var iosURL: URL? { + guard let url = Self.absoluteURL(iosDeepLink), + let scheme = url.scheme?.lowercased(), + !["javascript", "data", "file", "content"].contains(scheme) else { return nil } + return url + } + + var isEligible: Bool { + imageURL != nil && (iosURL != nil || webURL != nil) + } + + private static func absoluteURL(_ raw: String?) -> URL? { + guard let raw, let url = URL(string: raw), + url.scheme != nil, url.absoluteString == raw else { return nil } + return url + } + + private static func webURL(_ raw: String?) -> URL? { + guard let url = absoluteURL(raw), + let scheme = url.scheme?.lowercased(), + scheme == "http" || scheme == "https", + let host = url.host, !host.isEmpty else { return nil } + return url + } +} diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/production-source-snapshot/SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/production-source-snapshot/SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift new file mode 100644 index 00000000..2430afda --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/production-source-snapshot/SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift @@ -0,0 +1,147 @@ +import SwiftUI + +import Kingfisher + +struct SubBannerCarousel: View { + let items: [SubBanner] + let onTap: (SubBanner) -> Void + + @Environment(\.scenePhase) private var scenePhase + @State private var currentIndex = 0 + @State private var containerWidth: CGFloat = 0 + @State private var imageRatios: [String: CGFloat] = [:] + @State private var lastKnownRatio: CGFloat? + @GestureState private var isSwiping = false + @GestureState private var dragOffset: CGFloat = 0 + + private static let autoScrollNanoseconds: UInt64 = 5_000_000_000 + private static let transitionDuration: TimeInterval = 0.35 + + private var safeIndex: Int { min(currentIndex, max(0, items.count - 1)) } + + private var currentRatio: CGFloat? { + guard !items.isEmpty else { return nil } + return imageRatios[items[safeIndex].imageUrl] + } + + private var height: CGFloat { + guard !items.isEmpty, let ratio = currentRatio ?? lastKnownRatio else { return 0 } + return containerWidth / ratio + } + + var body: some View { + GeometryReader { geometry in + HStack(spacing: 0) { + ForEach(items) { item in + Button { + guard !isSwiping else { return } + onTap(item) + } label: { + KFImage(item.imageURL) + .placeholder { Color.gray800 } + .onSuccess { result in + let size = result.image.size + guard size.width > 0, size.height > 0 else { return } + imageRatios[item.imageUrl] = size.width / size.height + } + .startLoadingBeforeViewAppear() + .resizable() + .scaledToFit() + .frame(width: geometry.size.width, height: height) + .background(Color.gray800) + } + .buttonStyle(.plain) + .accessibilityLabel(Text("\(safeIndex + 1) / \(items.count)")) + .accessibilityHidden(items[safeIndex].id != item.id) + } + } + .offset(x: -CGFloat(safeIndex) * geometry.size.width + dragOffset) + .frame(width: geometry.size.width, height: height, alignment: .leading) + .contentShape(Rectangle()) + .simultaneousGesture(dragGesture(width: geometry.size.width)) + .clipped() + .overlay(alignment: .topTrailing) { + if !items.isEmpty && height > 0 { + pageIndicator + .padding(SodaSpacing.s14) + } + } + .onAppear { containerWidth = geometry.size.width } + .onChange(of: geometry.size.width) { containerWidth = $0 } + } + .frame(height: height) + .onChange(of: currentRatio) { ratio in + if let ratio { lastKnownRatio = ratio } + } + .onChange(of: items) { _ in + currentIndex = 0 + if items.isEmpty { lastKnownRatio = nil } + } + .task(id: scenePhase == .active && items.count > 1 ? items : []) { + guard scenePhase == .active, items.count > 1 else { return } + while !Task.isCancelled { + do { + try await Task.sleep(nanoseconds: Self.autoScrollNanoseconds) + } catch { + return + } + guard !Task.isCancelled else { return } + if !isSwiping { move(by: 1) } + } + } + .accessibilityAdjustableAction { direction in + switch direction { + case .increment: move(by: 1) + case .decrement: move(by: -1) + @unknown default: break + } + } + } + + private var pageIndicator: some View { + HStack(spacing: 0) { + Text(String(format: "%02d", safeIndex + 1)) + .foregroundColor(.white) + Text("/" + String(format: "%02d", items.count)) + .foregroundColor(.gray400) + } + .appFont(.body3) + .lineLimit(1) + .fixedSize(horizontal: true, vertical: false) + .padding(.horizontal, SodaSpacing.s6) + .padding(.vertical, SodaSpacing.s4) + .background(Color.gray900) + .clipShape(Capsule()) + .accessibilityHidden(true) + } + + private func dragGesture(width: CGFloat) -> some Gesture { + DragGesture(minimumDistance: SodaSpacing.s12) + .updating($dragOffset) { value, offset, _ in + if items.count > 1, abs(value.translation.width) > abs(value.translation.height) { + offset = value.translation.width + } + } + .updating($isSwiping) { _, swiping, _ in swiping = true } + .onEnded { value in + guard abs(value.translation.width) > abs(value.translation.height) else { return } + if value.translation.width < -width * 0.2 { + move(by: 1) + } else if value.translation.width > width * 0.2 { + move(by: -1) + } + } + } + + private func move(by step: Int) { + guard items.count > 1 else { return } + let next = safeIndex + step + if items.indices.contains(next) { + withAnimation(.easeOut(duration: Self.transitionDuration)) { currentIndex = next } + } else { + var transaction = Transaction() + transaction.disablesAnimations = true + withTransaction(transaction) { currentIndex = next < 0 ? items.count - 1 : 0 } + } + } +} diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/production-source-snapshot/SodaLive/Sources/V2/Component/Banner/SubBannerLinkOpener.swift b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/production-source-snapshot/SodaLive/Sources/V2/Component/Banner/SubBannerLinkOpener.swift new file mode 100644 index 00000000..666d52f4 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/production-source-snapshot/SodaLive/Sources/V2/Component/Banner/SubBannerLinkOpener.swift @@ -0,0 +1,28 @@ +import Foundation + +#if canImport(UIKit) +import UIKit +#endif + +enum SubBannerLinkOpener { + static func open(_ banner: SubBanner, using opener: @escaping (URL, @escaping (Bool) -> Void) -> Void) { + let webURL = banner.webURL + if let iosURL = banner.iosURL { + opener(iosURL) { opened in + if !opened, let webURL { + opener(webURL) { _ in } + } + } + } else if let webURL { + opener(webURL) { _ in } + } + } + + #if canImport(UIKit) + static func open(_ banner: SubBanner) { + open(banner) { url, completion in + UIApplication.shared.open(url, options: [:], completionHandler: completion) + } + } + #endif +} diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/production-source-snapshot/SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationView.swift b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/production-source-snapshot/SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationView.swift new file mode 100644 index 00000000..bf3950a0 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/production-source-snapshot/SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationView.swift @@ -0,0 +1,128 @@ +import SwiftUI + +struct MainContentRecommendationView: View { + let onTapContent: (Int) -> Void + let onTapBanner: (AudioBannerResponse) -> Void + let onTapSeries: (Int) -> Void + let onTapLatestAudioAll: () -> Void + let onTapNewAndHotAll: () -> Void + let onTapSodaLiveOnlyAll: () -> Void + let onTapFreeAudioAll: () -> Void + let onTapPointAudioAll: () -> Void + + @StateObject private var viewModel = MainContentRecommendationViewModel() + + var body: some View { + ZStack { + Color.black + .ignoresSafeArea() + + if viewModel.isLoading && !viewModel.hasLoaded { + loadingContent + } else if viewModel.shouldShowEmptyState { + emptyContent + } else { + LottieRefreshableScrollView(action: { + await viewModel.refresh() + }) { + VStack(alignment: .leading, spacing: SodaSpacing.s24) { + if let recommendations = viewModel.recommendations { + MainContentAudioBannerSection( + items: recommendations.banners, + onTapBanner: onTapBanner + ) + + if !recommendations.eligibleSubBanners.isEmpty { + SubBannerCarousel(items: recommendations.eligibleSubBanners) { + SubBannerLinkOpener.open($0) + } + } + + MainContentVoiceOnOnlySection( + title: I18n.MainContentRecommendation.sodaLiveOnlySectionTitle, + items: recommendations.originalSeries, + action: onTapSodaLiveOnlyAll, + onTapSeries: onTapSeries + ) + + MainContentAudioListCarouselSection( + title: I18n.MainContentRecommendation.newAndHotSectionTitle, + items: recommendations.newAndHotAudios, + action: onTapNewAndHotAll, + onTapContent: onTapContent + ) + + MainContentAudioHorizontalCardSection( + title: I18n.MainContentRecommendation.latestAudioSectionTitle, + items: recommendations.latestAudios, + action: onTapLatestAudioAll, + onTapContent: onTapContent + ) + + MainContentAudioHorizontalCardSection( + title: I18n.MainContentRecommendation.freeAudioSectionTitle, + items: recommendations.freeAudios, + action: onTapFreeAudioAll, + onTapContent: onTapContent + ) + + MainContentAudioHorizontalCardSection( + title: I18n.MainContentRecommendation.pointAudioSectionTitle, + items: recommendations.pointAudios, + action: onTapPointAudioAll, + onTapContent: onTapContent + ) + + MainContentMostCommentedAudioSection( + title: I18n.MainContentRecommendation.mostCommentedAudioSectionTitle, + items: recommendations.mostCommentedAudios, + onTapContent: onTapContent + ) + + MainContentRecommendedAudioGridSection( + title: I18n.MainContentRecommendation.recommendedAudioSectionTitle, + items: recommendations.recommendedAudios, + onTapContent: onTapContent + ) + } + } + .padding(.vertical, SodaSpacing.s20) + } + } + } + .onAppear { + if !viewModel.hasLoaded { + viewModel.fetchRecommendations() + } + } + .sodaToast( + isPresented: $viewModel.isShowPopup, + message: viewModel.errorMessage, + autohideIn: 2 + ) + } + + private var loadingContent: some View { + GeometryReader { geometry in + LottieRefreshableScrollView(action: { + await viewModel.refresh() + }) { + ProgressView() + .progressViewStyle(CircularProgressViewStyle(tint: .white)) + .frame(maxWidth: .infinity) + .frame(minHeight: geometry.size.height) + } + } + } + + private var emptyContent: some View { + GeometryReader { geometry in + LottieRefreshableScrollView(action: { + await viewModel.refresh() + }) { + MainContentAudioEmptyStateView(message: viewModel.emptyStateMessage) + .frame(minHeight: geometry.size.height) + } + } + } +} diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/production-source-snapshot/SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/production-source-snapshot/SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift new file mode 100644 index 00000000..51b75146 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/production-source-snapshot/SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift @@ -0,0 +1,150 @@ +import SwiftUI + +struct MainHomeRecommendationView: View { + let onTapLive: (Int) -> Void + let onTapCreator: (Int) -> Void + let onTapContent: (Int) -> Void + let onTapCharacter: (Int) -> Void + let onTapCommunityPost: (Int) -> Void + let onTapBanner: (RecommendationBannerResponse) -> Void + let onTapLiveAll: () -> Void + let onTapFollowAll: (@escaping () -> Void) -> Void + + @StateObject private var viewModel = MainHomeRecommendationViewModel() + @StateObject private var communityMediaPlayer = CreatorCommunityMediaPlayerManager.shared + + var body: some View { + ZStack { + Color.black + .ignoresSafeArea() + + if viewModel.isLoading && viewModel.recommendations == nil && viewModel.errorMessage.isEmpty { + loadingContent + } else { + LottieRefreshableScrollView(action: { + await viewModel.refresh() + }) { + VStack(alignment: .leading, spacing: SodaSpacing.s24) { + MainHomeLiveSection( + items: viewModel.recommendations?.lives ?? [], + onTapLive: onTapLive, + onTapAll: onTapLiveAll + ) + + MainHomeBannerSection( + items: viewModel.recommendations?.banners ?? [], + onTapBanner: onTapBanner + ) + + MainHomeActiveCreatorSection( + items: viewModel.recommendations?.recentlyActiveCreators ?? [], + onTapLive: onTapLive, + onTapCreator: onTapCreator, + onTapContent: onTapContent, + onTapCommunityPost: onTapCommunityPost + ) + + if let items = viewModel.recommendations?.eligibleSubBanners, !items.isEmpty { + SubBannerCarousel(items: items) { SubBannerLinkOpener.open($0) } + } + + MainHomeRecentDebutCreatorSection( + items: viewModel.recommendations?.recentDebutCreators ?? [], + onTapCreator: onTapCreator + ) + + MainHomeAiCharacterSection( + items: viewModel.recommendations?.aiCharacters ?? [], + onTapCharacter: onTapCharacter + ) + + MainHomeCheerCreatorSection( + items: viewModel.recommendations?.cheerCreators ?? [], + isFollowAllCompleted: viewModel.isFollowAllCompleted("cheer"), + isFollowAllLoading: viewModel.isFollowAllLoading("cheer"), + onTapCreator: onTapCreator, + onTapFollowAll: { + onTapFollowAll { + viewModel.followAll( + creatorIds: viewModel.recommendations?.cheerCreators.map(\.creatorId) ?? [], + completionKey: "cheer" + ) + } + } + ) + + MainHomePopularCommunitySection( + items: viewModel.recommendations?.popularCommunityPosts ?? [], + onTapCommunityPost: onTapCommunityPost, + onTapLike: { postId in + viewModel.likeCommunityPost(postId: postId) + }, + onTapPurchase: { item in + viewModel.openCommunityPostPurchase(postId: item.postId, price: item.price) + } + ) + + MainHomeBusinessInfoSection() + } + .padding(.vertical, SodaSpacing.s20) + } + } + } + .onAppear { + if viewModel.recommendations == nil { + viewModel.fetchRecommendations() + } + } + .sodaToast( + isPresented: $viewModel.isShowPopup, + message: viewModel.errorMessage, + autohideIn: 2 + ) + .sodaToast( + isPresented: $communityMediaPlayer.isShowPopup, + message: communityMediaPlayer.errorMessage, + autohideIn: 2 + ) + .overlay { + if viewModel.isShowCommunityPostPurchaseDialog { + CommunityPostPurchaseDialog( + isShowing: $viewModel.isShowCommunityPostPurchaseDialog, + can: viewModel.purchasingCommunityPostPrice, + confirmAction: viewModel.purchaseCommunityPost + ) + } + + if communityMediaPlayer.isLoading { + LoadingView() + } + } + } + + private var loadingContent: some View { + GeometryReader { geometry in + LottieRefreshableScrollView(action: { + await viewModel.refresh() + }) { + ProgressView() + .progressViewStyle(CircularProgressViewStyle(tint: .white)) + .frame(maxWidth: .infinity) + .frame(minHeight: geometry.size.height) + } + } + } +} + +struct MainHomeRecommendationView_Previews: PreviewProvider { + static var previews: some View { + MainHomeRecommendationView( + onTapLive: { _ in }, + onTapCreator: { _ in }, + onTapContent: { _ in }, + onTapCharacter: { _ in }, + onTapCommunityPost: { _ in }, + onTapBanner: { _ in }, + onTapLiveAll: {}, + onTapFollowAll: { action in action() } + ) + } +} diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/provenance-after.json b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/provenance-after.json new file mode 100644 index 00000000..def14533 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/provenance-after.json @@ -0,0 +1,137 @@ +{ + "recordedAtUTC": "2026-10-29T15:27:41.981415+00:00", + "expectedCarouselSHA256": "7e25db3076d545ffc61ea3a1c5585e7b8a617964ef406d7570286d75a75e010a", + "sourceHashes": { + "SodaLive/Sources/V2/Component/Banner/SubBanner.swift": "875bcdb953e47e9c3e157a7d5adc8b39c271691af2b02437fc738ac727d5b7e9", + "SodaLive/Sources/V2/Component/Banner/SubBannerLinkOpener.swift": "0aa860b206d0dff737cb5847741a2b531dbe633728b5fa5f2eeea5410834dab3", + "SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift": "7e25db3076d545ffc61ea3a1c5585e7b8a617964ef406d7570286d75a75e010a", + "SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift": "c49150d7b504b711c249458a3e2fb96c1f6a75d2b83d97ff29f674d9b928b48f", + "SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationView.swift": "2d0240fb8a150e7e365cc0daff67fa027c31ce709e12f518c1b52f0cec56a0b8", + "SodaLive/Sources/UI/Theme/Spacing.swift": "372a73b821236c15449fa022cd628f61556f1381a16f234a4b77e833c2e588c1", + "SodaLive/Sources/UI/Theme/Color.swift": "0f50ec7d3e77e0813fd1a806440c01baf96f8fdfa2a41faccd6dc4df52cf647a", + "SodaLive/Sources/Extensions/ColorExtension.swift": "747347d86ade888fe9c1d30629285b4a71991bc0f21189a0d4b76ad314c32364", + "SodaLive/Sources/Extensions/FontModifier.swift": "086fb0a19d884124a4cca638f7c8483de724ee31a61603b2cbd489aabf26a6c6" + }, + "snapshotHashes": { + "SodaLive/Sources/V2/Component/Banner/SubBanner.swift": "875bcdb953e47e9c3e157a7d5adc8b39c271691af2b02437fc738ac727d5b7e9", + "SodaLive/Sources/V2/Component/Banner/SubBannerLinkOpener.swift": "0aa860b206d0dff737cb5847741a2b531dbe633728b5fa5f2eeea5410834dab3", + "SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift": "7e25db3076d545ffc61ea3a1c5585e7b8a617964ef406d7570286d75a75e010a", + "SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift": "c49150d7b504b711c249458a3e2fb96c1f6a75d2b83d97ff29f674d9b928b48f", + "SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationView.swift": "2d0240fb8a150e7e365cc0daff67fa027c31ce709e12f518c1b52f0cec56a0b8", + "SodaLive/Sources/UI/Theme/Spacing.swift": "372a73b821236c15449fa022cd628f61556f1381a16f234a4b77e833c2e588c1", + "SodaLive/Sources/UI/Theme/Color.swift": "0f50ec7d3e77e0813fd1a806440c01baf96f8fdfa2a41faccd6dc4df52cf647a", + "SodaLive/Sources/Extensions/ColorExtension.swift": "747347d86ade888fe9c1d30629285b4a71991bc0f21189a0d4b76ad314c32364", + "SodaLive/Sources/Extensions/FontModifier.swift": "086fb0a19d884124a4cca638f7c8483de724ee31a61603b2cbd489aabf26a6c6" + }, + "sourceMtimes": { + "SodaLive/Sources/V2/Component/Banner/SubBanner.swift": 1793283861.7506907, + "SodaLive/Sources/V2/Component/Banner/SubBannerLinkOpener.swift": 1793283861.7525754, + "SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift": 1793285589.998333, + "SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift": 1793284151.8665268, + "SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationView.swift": 1793284151.9246447, + "SodaLive/Sources/UI/Theme/Spacing.swift": 1778841466.1905165, + "SodaLive/Sources/UI/Theme/Color.swift": 1778841466.1895542, + "SodaLive/Sources/Extensions/ColorExtension.swift": 1691563626.4298327, + "SodaLive/Sources/Extensions/FontModifier.swift": 1791429315.4954295 + }, + "protectedHashes": { + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/README.md": "ddfee9078ffe8ae973b461b4616abe38b58a9eff13b1d55b072e930a3ddfcec5", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/actions.json": "22e801ceb991e92820654416c221da99dbfb4892afc28d8e3646c52e32847263", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/active-before-5s.png": "f7bf598d63d31ebffac09bcb5d3cd57183fe3f9da9f4120e14ccd22983c27ca5", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/active-next.png": "4880e28aeae451cddbda8f2ba58ec147b60440f26cfa86d75dbf0e6fa381b01f", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/content-before-5s.png": "e03bdd3d19802929920672f7f39d26df4bef465d9b13981250cc5fb02f2111a9", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/content-empty.png": "ee561f7ce93a7cd7c719dfb32eda4b40e6d0d7998bbb71cfdfa356cdc60e7bcb", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/content-first.png": "e03bdd3d19802929920672f7f39d26df4bef465d9b13981250cc5fb02f2111a9", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/content-light.png": "e03bdd3d19802929920672f7f39d26df4bef465d9b13981250cc5fb02f2111a9", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/content-one-after-6s.png": "c8c2eeee836d90b3422fe1cf7e8646f094e0db78e7bde73880e499317ca1c84d", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/content-one.png": "c8c2eeee836d90b3422fe1cf7e8646f094e0db78e7bde73880e499317ca1c84d", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/content-resize-320.png": "a6313fb5db500af6e6880ed4f62d42ed2f8e3d2c75f2adaa7d85d8186fffa0cc", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/content-second.png": "3558fe0a407f41360c69b241dc5c7bb63d5e2a5b73450db5918b2c87dcfaff13", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/content-transition.png": "22e35cf56b149fa74b0cc5c602a64fbb738ffde4ef933acc83ba01be4e04ab6d", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/content-wrap.png": "e03bdd3d19802929920672f7f39d26df4bef465d9b13981250cc5fb02f2111a9", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/delayed-next-valid.png": "6c20a5f54dc012c9a7d9259c9a7d0628f250262291bced052ab92652e98342c3", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/delayed-no-invented-height.png": "60643fc2ef02e26153272782cc4a97422d6ab0b08a0ca5422a94906c1fa4ff5a", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/delayed-old-image-arrived.png": "6c20a5f54dc012c9a7d9259c9a7d0628f250262291bced052ab92652e98342c3", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/failure-placeholder-other-content.png": "217c258f4a1b83f9c9bf3564cf615a5ef2d0600f1a62819cc7fc2a7becedaa9e", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/failure-valid-first.png": "96b2051aedb2733a5620b2de5305cac9532cf9f266b3292d91f550c63550f60b", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/failure-wrap-valid.png": "96b2051aedb2733a5620b2de5305cac9532cf9f266b3292d91f550c63550f60b", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/home-before-5s.png": "8fc8502f06140d7f04bfb6defea3c235639e874172014611ead622d1ac5306ca", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/home-empty.png": "34af8cfecd132f08666cdabd7972a44a536977e5ebee4244a61d1c697b7184a0", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/home-first.png": "8fc8502f06140d7f04bfb6defea3c235639e874172014611ead622d1ac5306ca", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/home-light.png": "8fc8502f06140d7f04bfb6defea3c235639e874172014611ead622d1ac5306ca", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/home-one-after-6s.png": "6dd493128e266ec896e9f5bc2fec1756fad5f5673e88235a7dc342b989515448", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/home-one.png": "6dd493128e266ec896e9f5bc2fec1756fad5f5673e88235a7dc342b989515448", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/home-resize-320.png": "78760d7c64bcaa360e7147f3f9fefba038ed177e223cbbd4c0c4377ab6e91399", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/home-second.png": "8ce24e8f55ea474bd74822282cf180571e6142bfc0942614acc2c6f4d4ac8ac0", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/home-transition.png": "765df410019493e09f234a9b92cc2e6d358280938c0cdd28624a8f3ac11d2981", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/home-wrap.png": "8fc8502f06140d7f04bfb6defea3c235639e874172014611ead622d1ac5306ca", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/inactive-after-6s.png": "f7bf598d63d31ebffac09bcb5d3cd57183fe3f9da9f4120e14ccd22983c27ca5", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/inactive-before.png": "f7bf598d63d31ebffac09bcb5d3cd57183fe3f9da9f4120e14ccd22983c27ca5", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/inactive-start.png": "f7bf598d63d31ebffac09bcb5d3cd57183fe3f9da9f4120e14ccd22983c27ca5", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/many-first.png": "44a42bfedbf994063463e774e4c6d397ea33a71e1443489e3e5a2a0f7f48a1a9", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/many-twenty-first.png": "c4173138625345685ed9a945fbd359c2cae59b9265cba158f9c563722680133d", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/many-wrap.png": "3844369f5cf4ea0fa2a2a148c06dac9ada3b3838664f5d4a97fbdaa9c3bdbfdb", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/reduce-first.png": "c88339572c7bee2dc6c952958ce148331782f28d4b455d2ba9c4a746c64a4ba9", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/reduce-second.png": "77c8bcbcb1c693bc5d713fc3e97b7c81c9ed83999eecb3f6858966acf1cbe4dd", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/reduce-transition.png": "c88339572c7bee2dc6c952958ce148331782f28d4b455d2ba9c4a746c64a4ba9", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/remount-first.png": "4880e28aeae451cddbda8f2ba58ec147b60440f26cfa86d75dbf0e6fa381b01f", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/remount-next.png": "f7bf598d63d31ebffac09bcb5d3cd57183fe3f9da9f4120e14ccd22983c27ca5", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/shrink-one.png": "23ba00bf778494937ecd3f4f15dd36007127cdf412cb33d5c1f54f2f3a7b95e0", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/unmounted-after-6s.png": "8fc8aa79c74ce769ce96965823337521634feb015d742ecf1b5a21cabd275e8b", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/cleanup.json": "b29b5a87a0856c6dd475152c6bb6af51c7cd49845b090952e6b3195f4391daa9", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/commands.json": "e1908abc656cf06a80593f26ed5411e9d3964fc1a2459927931b4e5feffff9c6", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/contact-1.png": "d608d75ea336e80d7a301c32f3b3c3d750aa55aee6fe66f7850e24d60a675c86", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/contact-2.png": "24b85be00ac82c378dcc8e5941e48aa3b326ec973267839d99e7288afbd18079", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/contact-3.png": "24836a87d48450dd827640e42873e1c921df2dbe485f634d663aff7bdaa42126", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/contact-4.png": "ec0524b1e3a6ee30a4f7b312a63edef699a69f43a56deb3dfe79ac06e8c6ec13", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/dependency-hashes.json": "40be04494df3dc3a6230b54039acf080c943ba200de43819ae8db54a1b19d8fd", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/native-os-final.png": "a79e5e44bbee7a123266c9391c8bf2138d52028a9674c0aa8633cc8ce2d84dd4", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/ownership.json": "c5f8ba7a2fd5aaacf3d7a7ffb41f75691665cbea291975a54d27eb59cebf7931", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/source/Doubles.swift": "eb98fd62f2f76e456d29d3931b7378e9b68b2e9ad0f76bea64f597e1a9a5fb9f", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/source/Harness.swift": "4e3bbde7fe363b9f1f436abb1ffff3d39dbdf17db002ab3b5f9093fe70bbcfa4", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/source/assess.py": "45d594396785738545c30228bc7ef46b09f4d0a69847614ec7396277c1cecbe2", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/source/cleanup.py": "ba973e9805092bc46a0e032a992826e20b36b3730d7140a3f3d5c05bdde93caa", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/source/run.py": "c0324005ce5efed32f07f422e84f2e066153667f327a4157691d7edd64eaccd2", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/source-hashes-after.json": "b85d19121fbd459858290f7dee83cd4d3989abf3e65098824402fdf760ee056c", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/source-hashes-before.json": "b85d19121fbd459858290f7dee83cd4d3989abf3e65098824402fdf760ee056c", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/source-hashes-final.json": "b85d19121fbd459858290f7dee83cd4d3989abf3e65098824402fdf760ee056c", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/source-stability.json": "edc3fbbc77c5dcf8fcabdd54cf0ec79b77afaa703d28afcf5faf8b8e6fe5fd1d", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/verification.json": "35cec56ab6f4d53bb2ed2ff4306ebd5fa962734ac3a4a794c59dc9332e310573", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/phase1-sub-banner-integration.md": "883a770b7d1b293805231d6779d3a3d2f51d04e102358dd3cf7ac25c8d7bf12f", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/plan-task.md": "81aa9ab6349449ebc46379118cdcf08af8fa3f88f9a77ef2ece721451101eb0b", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/prd.md": "3ab2aeb2f12224b68c7838f9948949a846d1f5bb24a8918ff68df2b1e4e75872" + }, + "dependencyHashes": { + "/Users/klaus/Library/Developer/Xcode/DerivedData/SodaLive-clzdtvqrsfonoxcijatzkrqdzrsu/Build/Products/Debug-iphonesimulator/Kingfisher.o": "ab9481b5935132edf5744692d88ff4c3e34f1a68f4014f88267a5ad5d95f31b8", + "/Users/klaus/Library/Developer/Xcode/DerivedData/SodaLive-clzdtvqrsfonoxcijatzkrqdzrsu/Build/Products/Debug-iphonesimulator/Kingfisher.swiftmodule/x86_64-apple-ios-simulator.swiftmodule": "b155d956bfaa735f7c72f4630b5be0e2dc82fcb768c2c0ce930ab17faff22fdc" + }, + "harnessCloneChecks": { + "run.py": "IDENTICAL after evidence root/temp root/bundle normalization", + "assess.py": "IDENTICAL after evidence root/temp root/bundle normalization", + "cleanup.py": "IDENTICAL after evidence root/temp root/bundle normalization", + "Doubles.swift": "IDENTICAL after evidence root/temp root/bundle normalization", + "Harness.swift": "IDENTICAL after evidence root/temp root/bundle normalization" + }, + "ownedRoot": "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-native-final.97cQKj", + "ownedBundle": "qa.sodalive.subbanner.native.final.97cQKj", + "ownedCache": "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-native-final.97cQKj/module-cache", + "cacheEnvironment": { + "TMPDIR": "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-native-final.97cQKj", + "NODE_COMPILE_CACHE": "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-native-final.97cQKj", + "BUN_RUNTIME_TRANSPILER_CACHE_PATH": "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-native-final.97cQKj" + }, + "parentDevice": { + "lastBootedAt": "2026-10-29T14:27:43Z", + "dataPath": "/Users/klaus/Library/Developer/CoreSimulator/Devices/482F7827-2E3A-4559-BAF4-48B68CC5725B/data", + "dataPathSize": 1324466176, + "logPath": "/Users/klaus/Library/Logs/CoreSimulator/482F7827-2E3A-4559-BAF4-48B68CC5725B", + "udid": "482F7827-2E3A-4559-BAF4-48B68CC5725B", + "isAvailable": true, + "logPathSize": 258048, + "deviceTypeIdentifier": "com.apple.CoreSimulator.SimDeviceType.iPhone-SE-3rd-generation", + "state": "Booted", + "name": "SubBannerQA-20261029" + }, + "afterCleanup": true, + "assertions": "PASS" +} \ No newline at end of file diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/provenance-before.json b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/provenance-before.json new file mode 100644 index 00000000..3c867f77 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/provenance-before.json @@ -0,0 +1,137 @@ +{ + "recordedAtUTC": "2026-10-29T15:20:09.044875+00:00", + "expectedCarouselSHA256": "7e25db3076d545ffc61ea3a1c5585e7b8a617964ef406d7570286d75a75e010a", + "sourceHashes": { + "SodaLive/Sources/V2/Component/Banner/SubBanner.swift": "875bcdb953e47e9c3e157a7d5adc8b39c271691af2b02437fc738ac727d5b7e9", + "SodaLive/Sources/V2/Component/Banner/SubBannerLinkOpener.swift": "0aa860b206d0dff737cb5847741a2b531dbe633728b5fa5f2eeea5410834dab3", + "SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift": "7e25db3076d545ffc61ea3a1c5585e7b8a617964ef406d7570286d75a75e010a", + "SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift": "c49150d7b504b711c249458a3e2fb96c1f6a75d2b83d97ff29f674d9b928b48f", + "SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationView.swift": "2d0240fb8a150e7e365cc0daff67fa027c31ce709e12f518c1b52f0cec56a0b8", + "SodaLive/Sources/UI/Theme/Spacing.swift": "372a73b821236c15449fa022cd628f61556f1381a16f234a4b77e833c2e588c1", + "SodaLive/Sources/UI/Theme/Color.swift": "0f50ec7d3e77e0813fd1a806440c01baf96f8fdfa2a41faccd6dc4df52cf647a", + "SodaLive/Sources/Extensions/ColorExtension.swift": "747347d86ade888fe9c1d30629285b4a71991bc0f21189a0d4b76ad314c32364", + "SodaLive/Sources/Extensions/FontModifier.swift": "086fb0a19d884124a4cca638f7c8483de724ee31a61603b2cbd489aabf26a6c6" + }, + "snapshotHashes": { + "SodaLive/Sources/V2/Component/Banner/SubBanner.swift": "875bcdb953e47e9c3e157a7d5adc8b39c271691af2b02437fc738ac727d5b7e9", + "SodaLive/Sources/V2/Component/Banner/SubBannerLinkOpener.swift": "0aa860b206d0dff737cb5847741a2b531dbe633728b5fa5f2eeea5410834dab3", + "SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift": "7e25db3076d545ffc61ea3a1c5585e7b8a617964ef406d7570286d75a75e010a", + "SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift": "c49150d7b504b711c249458a3e2fb96c1f6a75d2b83d97ff29f674d9b928b48f", + "SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationView.swift": "2d0240fb8a150e7e365cc0daff67fa027c31ce709e12f518c1b52f0cec56a0b8", + "SodaLive/Sources/UI/Theme/Spacing.swift": "372a73b821236c15449fa022cd628f61556f1381a16f234a4b77e833c2e588c1", + "SodaLive/Sources/UI/Theme/Color.swift": "0f50ec7d3e77e0813fd1a806440c01baf96f8fdfa2a41faccd6dc4df52cf647a", + "SodaLive/Sources/Extensions/ColorExtension.swift": "747347d86ade888fe9c1d30629285b4a71991bc0f21189a0d4b76ad314c32364", + "SodaLive/Sources/Extensions/FontModifier.swift": "086fb0a19d884124a4cca638f7c8483de724ee31a61603b2cbd489aabf26a6c6" + }, + "sourceMtimes": { + "SodaLive/Sources/V2/Component/Banner/SubBanner.swift": 1793283861.7506907, + "SodaLive/Sources/V2/Component/Banner/SubBannerLinkOpener.swift": 1793283861.7525754, + "SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift": 1793285589.998333, + "SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift": 1793284151.8665268, + "SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationView.swift": 1793284151.9246447, + "SodaLive/Sources/UI/Theme/Spacing.swift": 1778841466.1905165, + "SodaLive/Sources/UI/Theme/Color.swift": 1778841466.1895542, + "SodaLive/Sources/Extensions/ColorExtension.swift": 1691563626.4298327, + "SodaLive/Sources/Extensions/FontModifier.swift": 1791429315.4954295 + }, + "protectedHashes": { + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/README.md": "ddfee9078ffe8ae973b461b4616abe38b58a9eff13b1d55b072e930a3ddfcec5", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/actions.json": "22e801ceb991e92820654416c221da99dbfb4892afc28d8e3646c52e32847263", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/active-before-5s.png": "f7bf598d63d31ebffac09bcb5d3cd57183fe3f9da9f4120e14ccd22983c27ca5", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/active-next.png": "4880e28aeae451cddbda8f2ba58ec147b60440f26cfa86d75dbf0e6fa381b01f", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/content-before-5s.png": "e03bdd3d19802929920672f7f39d26df4bef465d9b13981250cc5fb02f2111a9", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/content-empty.png": "ee561f7ce93a7cd7c719dfb32eda4b40e6d0d7998bbb71cfdfa356cdc60e7bcb", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/content-first.png": "e03bdd3d19802929920672f7f39d26df4bef465d9b13981250cc5fb02f2111a9", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/content-light.png": "e03bdd3d19802929920672f7f39d26df4bef465d9b13981250cc5fb02f2111a9", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/content-one-after-6s.png": "c8c2eeee836d90b3422fe1cf7e8646f094e0db78e7bde73880e499317ca1c84d", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/content-one.png": "c8c2eeee836d90b3422fe1cf7e8646f094e0db78e7bde73880e499317ca1c84d", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/content-resize-320.png": "a6313fb5db500af6e6880ed4f62d42ed2f8e3d2c75f2adaa7d85d8186fffa0cc", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/content-second.png": "3558fe0a407f41360c69b241dc5c7bb63d5e2a5b73450db5918b2c87dcfaff13", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/content-transition.png": "22e35cf56b149fa74b0cc5c602a64fbb738ffde4ef933acc83ba01be4e04ab6d", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/content-wrap.png": "e03bdd3d19802929920672f7f39d26df4bef465d9b13981250cc5fb02f2111a9", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/delayed-next-valid.png": "6c20a5f54dc012c9a7d9259c9a7d0628f250262291bced052ab92652e98342c3", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/delayed-no-invented-height.png": "60643fc2ef02e26153272782cc4a97422d6ab0b08a0ca5422a94906c1fa4ff5a", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/delayed-old-image-arrived.png": "6c20a5f54dc012c9a7d9259c9a7d0628f250262291bced052ab92652e98342c3", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/failure-placeholder-other-content.png": "217c258f4a1b83f9c9bf3564cf615a5ef2d0600f1a62819cc7fc2a7becedaa9e", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/failure-valid-first.png": "96b2051aedb2733a5620b2de5305cac9532cf9f266b3292d91f550c63550f60b", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/failure-wrap-valid.png": "96b2051aedb2733a5620b2de5305cac9532cf9f266b3292d91f550c63550f60b", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/home-before-5s.png": "8fc8502f06140d7f04bfb6defea3c235639e874172014611ead622d1ac5306ca", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/home-empty.png": "34af8cfecd132f08666cdabd7972a44a536977e5ebee4244a61d1c697b7184a0", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/home-first.png": "8fc8502f06140d7f04bfb6defea3c235639e874172014611ead622d1ac5306ca", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/home-light.png": "8fc8502f06140d7f04bfb6defea3c235639e874172014611ead622d1ac5306ca", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/home-one-after-6s.png": "6dd493128e266ec896e9f5bc2fec1756fad5f5673e88235a7dc342b989515448", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/home-one.png": "6dd493128e266ec896e9f5bc2fec1756fad5f5673e88235a7dc342b989515448", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/home-resize-320.png": "78760d7c64bcaa360e7147f3f9fefba038ed177e223cbbd4c0c4377ab6e91399", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/home-second.png": "8ce24e8f55ea474bd74822282cf180571e6142bfc0942614acc2c6f4d4ac8ac0", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/home-transition.png": "765df410019493e09f234a9b92cc2e6d358280938c0cdd28624a8f3ac11d2981", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/home-wrap.png": "8fc8502f06140d7f04bfb6defea3c235639e874172014611ead622d1ac5306ca", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/inactive-after-6s.png": "f7bf598d63d31ebffac09bcb5d3cd57183fe3f9da9f4120e14ccd22983c27ca5", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/inactive-before.png": "f7bf598d63d31ebffac09bcb5d3cd57183fe3f9da9f4120e14ccd22983c27ca5", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/inactive-start.png": "f7bf598d63d31ebffac09bcb5d3cd57183fe3f9da9f4120e14ccd22983c27ca5", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/many-first.png": "44a42bfedbf994063463e774e4c6d397ea33a71e1443489e3e5a2a0f7f48a1a9", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/many-twenty-first.png": "c4173138625345685ed9a945fbd359c2cae59b9265cba158f9c563722680133d", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/many-wrap.png": "3844369f5cf4ea0fa2a2a148c06dac9ada3b3838664f5d4a97fbdaa9c3bdbfdb", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/reduce-first.png": "c88339572c7bee2dc6c952958ce148331782f28d4b455d2ba9c4a746c64a4ba9", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/reduce-second.png": "77c8bcbcb1c693bc5d713fc3e97b7c81c9ed83999eecb3f6858966acf1cbe4dd", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/reduce-transition.png": "c88339572c7bee2dc6c952958ce148331782f28d4b455d2ba9c4a746c64a4ba9", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/remount-first.png": "4880e28aeae451cddbda8f2ba58ec147b60440f26cfa86d75dbf0e6fa381b01f", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/remount-next.png": "f7bf598d63d31ebffac09bcb5d3cd57183fe3f9da9f4120e14ccd22983c27ca5", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/shrink-one.png": "23ba00bf778494937ecd3f4f15dd36007127cdf412cb33d5c1f54f2f3a7b95e0", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/captures/unmounted-after-6s.png": "8fc8aa79c74ce769ce96965823337521634feb015d742ecf1b5a21cabd275e8b", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/cleanup.json": "b29b5a87a0856c6dd475152c6bb6af51c7cd49845b090952e6b3195f4391daa9", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/commands.json": "e1908abc656cf06a80593f26ed5411e9d3964fc1a2459927931b4e5feffff9c6", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/contact-1.png": "d608d75ea336e80d7a301c32f3b3c3d750aa55aee6fe66f7850e24d60a675c86", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/contact-2.png": "24b85be00ac82c378dcc8e5941e48aa3b326ec973267839d99e7288afbd18079", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/contact-3.png": "24836a87d48450dd827640e42873e1c921df2dbe485f634d663aff7bdaa42126", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/contact-4.png": "ec0524b1e3a6ee30a4f7b312a63edef699a69f43a56deb3dfe79ac06e8c6ec13", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/dependency-hashes.json": "40be04494df3dc3a6230b54039acf080c943ba200de43819ae8db54a1b19d8fd", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/native-os-final.png": "a79e5e44bbee7a123266c9391c8bf2138d52028a9674c0aa8633cc8ce2d84dd4", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/ownership.json": "c5f8ba7a2fd5aaacf3d7a7ffb41f75691665cbea291975a54d27eb59cebf7931", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/source/Doubles.swift": "eb98fd62f2f76e456d29d3931b7378e9b68b2e9ad0f76bea64f597e1a9a5fb9f", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/source/Harness.swift": "4e3bbde7fe363b9f1f436abb1ffff3d39dbdf17db002ab3b5f9093fe70bbcfa4", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/source/assess.py": "45d594396785738545c30228bc7ef46b09f4d0a69847614ec7396277c1cecbe2", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/source/cleanup.py": "ba973e9805092bc46a0e032a992826e20b36b3730d7140a3f3d5c05bdde93caa", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/source/run.py": "c0324005ce5efed32f07f422e84f2e066153667f327a4157691d7edd64eaccd2", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/source-hashes-after.json": "b85d19121fbd459858290f7dee83cd4d3989abf3e65098824402fdf760ee056c", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/source-hashes-before.json": "b85d19121fbd459858290f7dee83cd4d3989abf3e65098824402fdf760ee056c", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/source-hashes-final.json": "b85d19121fbd459858290f7dee83cd4d3989abf3e65098824402fdf760ee056c", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/source-stability.json": "edc3fbbc77c5dcf8fcabdd54cf0ec79b77afaa703d28afcf5faf8b8e6fe5fd1d", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/verification.json": "35cec56ab6f4d53bb2ed2ff4306ebd5fa962734ac3a4a794c59dc9332e310573", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/phase1-sub-banner-integration.md": "883a770b7d1b293805231d6779d3a3d2f51d04e102358dd3cf7ac25c8d7bf12f", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/plan-task.md": "81aa9ab6349449ebc46379118cdcf08af8fa3f88f9a77ef2ece721451101eb0b", + "docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/prd.md": "3ab2aeb2f12224b68c7838f9948949a846d1f5bb24a8918ff68df2b1e4e75872" + }, + "dependencyHashes": { + "/Users/klaus/Library/Developer/Xcode/DerivedData/SodaLive-clzdtvqrsfonoxcijatzkrqdzrsu/Build/Products/Debug-iphonesimulator/Kingfisher.o": "ab9481b5935132edf5744692d88ff4c3e34f1a68f4014f88267a5ad5d95f31b8", + "/Users/klaus/Library/Developer/Xcode/DerivedData/SodaLive-clzdtvqrsfonoxcijatzkrqdzrsu/Build/Products/Debug-iphonesimulator/Kingfisher.swiftmodule/x86_64-apple-ios-simulator.swiftmodule": "b155d956bfaa735f7c72f4630b5be0e2dc82fcb768c2c0ce930ab17faff22fdc" + }, + "harnessCloneChecks": { + "run.py": "IDENTICAL after evidence root/temp root/bundle normalization", + "assess.py": "IDENTICAL after evidence root/temp root/bundle normalization", + "cleanup.py": "IDENTICAL after evidence root/temp root/bundle normalization", + "Doubles.swift": "IDENTICAL after evidence root/temp root/bundle normalization", + "Harness.swift": "IDENTICAL after evidence root/temp root/bundle normalization" + }, + "ownedRoot": "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-native-final.97cQKj", + "ownedBundle": "qa.sodalive.subbanner.native.final.97cQKj", + "ownedCache": "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-native-final.97cQKj/module-cache", + "cacheEnvironment": { + "TMPDIR": "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-native-final.97cQKj", + "NODE_COMPILE_CACHE": "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-native-final.97cQKj", + "BUN_RUNTIME_TRANSPILER_CACHE_PATH": "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-native-final.97cQKj" + }, + "parentDevice": { + "lastBootedAt": "2026-10-29T14:27:43Z", + "dataPath": "/Users/klaus/Library/Developer/CoreSimulator/Devices/482F7827-2E3A-4559-BAF4-48B68CC5725B/data", + "dataPathSize": 1322061824, + "logPath": "/Users/klaus/Library/Logs/CoreSimulator/482F7827-2E3A-4559-BAF4-48B68CC5725B", + "udid": "482F7827-2E3A-4559-BAF4-48B68CC5725B", + "isAvailable": true, + "logPathSize": 258048, + "deviceTypeIdentifier": "com.apple.CoreSimulator.SimDeviceType.iPhone-SE-3rd-generation", + "state": "Booted", + "name": "SubBannerQA-20261029" + }, + "afterCleanup": false, + "assertions": "PASS" +} \ No newline at end of file diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/source-hashes-after.json b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/source-hashes-after.json new file mode 100644 index 00000000..2c60b956 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/source-hashes-after.json @@ -0,0 +1,11 @@ +{ + "SodaLive/Sources/V2/Component/Banner/SubBanner.swift": "875bcdb953e47e9c3e157a7d5adc8b39c271691af2b02437fc738ac727d5b7e9", + "SodaLive/Sources/V2/Component/Banner/SubBannerLinkOpener.swift": "0aa860b206d0dff737cb5847741a2b531dbe633728b5fa5f2eeea5410834dab3", + "SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift": "7e25db3076d545ffc61ea3a1c5585e7b8a617964ef406d7570286d75a75e010a", + "SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift": "c49150d7b504b711c249458a3e2fb96c1f6a75d2b83d97ff29f674d9b928b48f", + "SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationView.swift": "2d0240fb8a150e7e365cc0daff67fa027c31ce709e12f518c1b52f0cec56a0b8", + "SodaLive/Sources/UI/Theme/Spacing.swift": "372a73b821236c15449fa022cd628f61556f1381a16f234a4b77e833c2e588c1", + "SodaLive/Sources/UI/Theme/Color.swift": "0f50ec7d3e77e0813fd1a806440c01baf96f8fdfa2a41faccd6dc4df52cf647a", + "SodaLive/Sources/Extensions/ColorExtension.swift": "747347d86ade888fe9c1d30629285b4a71991bc0f21189a0d4b76ad314c32364", + "SodaLive/Sources/Extensions/FontModifier.swift": "086fb0a19d884124a4cca638f7c8483de724ee31a61603b2cbd489aabf26a6c6" +} \ No newline at end of file diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/source-hashes-before.json b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/source-hashes-before.json new file mode 100644 index 00000000..2c60b956 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/source-hashes-before.json @@ -0,0 +1,11 @@ +{ + "SodaLive/Sources/V2/Component/Banner/SubBanner.swift": "875bcdb953e47e9c3e157a7d5adc8b39c271691af2b02437fc738ac727d5b7e9", + "SodaLive/Sources/V2/Component/Banner/SubBannerLinkOpener.swift": "0aa860b206d0dff737cb5847741a2b531dbe633728b5fa5f2eeea5410834dab3", + "SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift": "7e25db3076d545ffc61ea3a1c5585e7b8a617964ef406d7570286d75a75e010a", + "SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift": "c49150d7b504b711c249458a3e2fb96c1f6a75d2b83d97ff29f674d9b928b48f", + "SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationView.swift": "2d0240fb8a150e7e365cc0daff67fa027c31ce709e12f518c1b52f0cec56a0b8", + "SodaLive/Sources/UI/Theme/Spacing.swift": "372a73b821236c15449fa022cd628f61556f1381a16f234a4b77e833c2e588c1", + "SodaLive/Sources/UI/Theme/Color.swift": "0f50ec7d3e77e0813fd1a806440c01baf96f8fdfa2a41faccd6dc4df52cf647a", + "SodaLive/Sources/Extensions/ColorExtension.swift": "747347d86ade888fe9c1d30629285b4a71991bc0f21189a0d4b76ad314c32364", + "SodaLive/Sources/Extensions/FontModifier.swift": "086fb0a19d884124a4cca638f7c8483de724ee31a61603b2cbd489aabf26a6c6" +} \ No newline at end of file diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/source-hashes-final.json b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/source-hashes-final.json new file mode 100644 index 00000000..2c60b956 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/source-hashes-final.json @@ -0,0 +1,11 @@ +{ + "SodaLive/Sources/V2/Component/Banner/SubBanner.swift": "875bcdb953e47e9c3e157a7d5adc8b39c271691af2b02437fc738ac727d5b7e9", + "SodaLive/Sources/V2/Component/Banner/SubBannerLinkOpener.swift": "0aa860b206d0dff737cb5847741a2b531dbe633728b5fa5f2eeea5410834dab3", + "SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift": "7e25db3076d545ffc61ea3a1c5585e7b8a617964ef406d7570286d75a75e010a", + "SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift": "c49150d7b504b711c249458a3e2fb96c1f6a75d2b83d97ff29f674d9b928b48f", + "SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationView.swift": "2d0240fb8a150e7e365cc0daff67fa027c31ce709e12f518c1b52f0cec56a0b8", + "SodaLive/Sources/UI/Theme/Spacing.swift": "372a73b821236c15449fa022cd628f61556f1381a16f234a4b77e833c2e588c1", + "SodaLive/Sources/UI/Theme/Color.swift": "0f50ec7d3e77e0813fd1a806440c01baf96f8fdfa2a41faccd6dc4df52cf647a", + "SodaLive/Sources/Extensions/ColorExtension.swift": "747347d86ade888fe9c1d30629285b4a71991bc0f21189a0d4b76ad314c32364", + "SodaLive/Sources/Extensions/FontModifier.swift": "086fb0a19d884124a4cca638f7c8483de724ee31a61603b2cbd489aabf26a6c6" +} \ No newline at end of file diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/source-stability.json b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/source-stability.json new file mode 100644 index 00000000..cc72526d --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/source-stability.json @@ -0,0 +1,4 @@ +{ + "unchanged": true, + "changed": [] +} \ No newline at end of file diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/source/Doubles.swift b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/source/Doubles.swift new file mode 100644 index 00000000..6cbd8a9d --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/source/Doubles.swift @@ -0,0 +1,160 @@ +import SwiftUI +import Combine + +enum LanguageOption: String { case system, ko, en, ja } +struct QAItem { let creatorId = 1; let postId = 1; let price = 1 } +struct RecommendationBannerResponse {} +struct AudioBannerResponse {} +struct FixtureResponse { + var subBanners: [SubBanner] + var eligibleSubBanners: [SubBanner] { subBanners.filter(\.isEligible) } + let lives: [QAItem] = [.init()] + let banners: [QAItem] = [.init()] + let recentlyActiveCreators: [QAItem] = [.init()] + let recentDebutCreators: [QAItem] = [.init()] + let aiCharacters: [QAItem] = [] + let cheerCreators: [QAItem] = [] + let popularCommunityPosts: [QAItem] = [] + let originalSeries: [QAItem] = [.init()] + let newAndHotAudios: [QAItem] = [] + let latestAudios: [QAItem] = [] + let freeAudios: [QAItem] = [] + let pointAudios: [QAItem] = [] + let mostCommentedAudios: [QAItem] = [] + let recommendedAudios: [QAItem] = [] +} +final class MainHomeRecommendationViewModel: ObservableObject { + static weak var current: MainHomeRecommendationViewModel? + @Published var recommendations: FixtureResponse? = .init(subBanners: []) + @Published var isLoading = false + @Published var errorMessage = "" + @Published var isShowPopup = false + @Published var isShowCommunityPostPurchaseDialog = false + var purchasingCommunityPostPrice = 0 + init() { Self.current = self; recommendations = .init(subBanners: HarnessState.shared.items) } + func refresh() async {} + func fetchRecommendations() {} + func isFollowAllCompleted(_ key: String) -> Bool { false } + func isFollowAllLoading(_ key: String) -> Bool { false } + func followAll(creatorIds: [Int], completionKey: String) {} + func likeCommunityPost(postId: Int) {} + func openCommunityPostPurchase(postId: Int, price: Int) {} + func purchaseCommunityPost() {} +} +final class MainContentRecommendationViewModel: ObservableObject { + static weak var current: MainContentRecommendationViewModel? + @Published var recommendations: FixtureResponse? = .init(subBanners: []) + @Published var isLoading = false + @Published var isShowPopup = false + var errorMessage = "" + let hasLoaded = true + let shouldShowEmptyState = false + let emptyStateMessage = "QA empty" + init() { Self.current = self; recommendations = .init(subBanners: HarnessState.shared.items) } + func refresh() async {} + func fetchRecommendations() {} +} +final class CreatorCommunityMediaPlayerManager: ObservableObject { + static let shared = CreatorCommunityMediaPlayerManager() + @Published var isShowPopup = false + let errorMessage = "" + let isLoading = false +} +enum I18n { + enum MainContentRecommendation { + static let sodaLiveOnlySectionTitle = "QA exclusive series" + static let newAndHotSectionTitle = "QA new and hot" + static let latestAudioSectionTitle = "QA latest" + static let freeAudioSectionTitle = "QA free" + static let pointAudioSectionTitle = "QA point" + static let mostCommentedAudioSectionTitle = "QA comments" + static let recommendedAudioSectionTitle = "QA recommended" + } +} +struct Rectangles: PreferenceKey { + static var defaultValue: [String: CGRect] = [:] + static func reduce(value: inout [String: CGRect], nextValue: () -> [String: CGRect]) { value.merge(nextValue(), uniquingKeysWith: { _, new in new }) } +} +extension View { + func probe(_ name: String) -> some View { + background(GeometryReader { proxy in Color.clear.preference(key: Rectangles.self, value: [name: proxy.frame(in: .global)]) }) + } + func sodaToast(isPresented: Binding, message: String, autohideIn: Double) -> some View { self } +} +struct QABar: View { + let name: String + var body: some View { + Text(name).appFont(.body3).foregroundColor(.white) + .frame(maxWidth: .infinity).frame(height: SodaSpacing.s48) + .background(Color.gray900).probe(name) + } +} +struct LottieRefreshableScrollView: View { + let action: () async -> Void + @ViewBuilder let content: () -> Content + var body: some View { ScrollView { content() } } +} +struct MainHomeLiveSection: View { + let items: [QAItem]; let onTapLive: (Int) -> Void; let onTapAll: () -> Void + var body: some View { QABar(name: "QA home live") } +} +struct MainHomeBannerSection: View { + let items: [QAItem]; let onTapBanner: (RecommendationBannerResponse) -> Void + var body: some View { QABar(name: "QA home main banner") } +} +struct MainHomeActiveCreatorSection: View { + let items: [QAItem]; let onTapLive: (Int) -> Void; let onTapCreator: (Int) -> Void + let onTapContent: (Int) -> Void; let onTapCommunityPost: (Int) -> Void + var body: some View { QABar(name: "QA active creators") } +} +struct MainHomeRecentDebutCreatorSection: View { + let items: [QAItem]; let onTapCreator: (Int) -> Void + var body: some View { QABar(name: "QA debut creators") } +} +struct MainHomeAiCharacterSection: View { + let items: [QAItem]; let onTapCharacter: (Int) -> Void + var body: some View { EmptyView() } +} +struct MainHomeCheerCreatorSection: View { + let items: [QAItem]; let isFollowAllCompleted: Bool; let isFollowAllLoading: Bool + let onTapCreator: (Int) -> Void; let onTapFollowAll: () -> Void + var body: some View { EmptyView() } +} +struct MainHomePopularCommunitySection: View { + let items: [QAItem]; let onTapCommunityPost: (Int) -> Void; let onTapLike: (Int) -> Void; let onTapPurchase: (QAItem) -> Void + var body: some View { EmptyView() } +} +struct MainHomeBusinessInfoSection: View { var body: some View { QABar(name: "QA other home content") } } +struct CommunityPostPurchaseDialog: View { + @Binding var isShowing: Bool; let can: Int; let confirmAction: () -> Void + var body: some View { EmptyView() } +} +struct LoadingView: View { var body: some View { ProgressView() } } +struct MainContentAudioBannerSection: View { + let items: [QAItem]; let onTapBanner: (AudioBannerResponse) -> Void + var body: some View { QABar(name: "QA content main banner") } +} +struct MainContentVoiceOnOnlySection: View { + let title: String; let items: [QAItem]; let action: () -> Void; let onTapSeries: (Int) -> Void + var body: some View { QABar(name: "QA exclusive series") } +} +struct MainContentAudioListCarouselSection: View { + let title: String; let items: [QAItem]; let action: () -> Void; let onTapContent: (Int) -> Void + var body: some View { QABar(name: "QA other content") } +} +struct MainContentAudioHorizontalCardSection: View { + let title: String; let items: [QAItem]; let action: () -> Void; let onTapContent: (Int) -> Void + var body: some View { EmptyView() } +} +struct MainContentMostCommentedAudioSection: View { + let title: String; let items: [QAItem]; let onTapContent: (Int) -> Void + var body: some View { EmptyView() } +} +struct MainContentRecommendedAudioGridSection: View { + let title: String; let items: [QAItem]; let onTapContent: (Int) -> Void + var body: some View { EmptyView() } +} +struct MainContentAudioEmptyStateView: View { + let message: String + var body: some View { Text(message) } +} diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/source/Harness.swift b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/source/Harness.swift new file mode 100644 index 00000000..8a84d623 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/source/Harness.swift @@ -0,0 +1,207 @@ +import SwiftUI +import UIKit +import Kingfisher + +final class HarnessState: ObservableObject { + static let shared = HarnessState() + @Published var items: [SubBanner] = [] + @Published var context = "home" + @Published var mounted = true + @Published var width: CGFloat = 375 + @Published var phase = ScenePhase.active + @Published var reduceMotion = false + var frames: [String: CGRect] = [:] + var scenario = "preparing" + func update(_ items: [SubBanner]) { + self.items = items + MainHomeRecommendationViewModel.current?.recommendations = .init(subBanners: items) + MainContentRecommendationViewModel.current?.recommendations = .init(subBanners: items) + } +} + +final class FixtureProtocol: URLProtocol { + static var requests: [[String: Any]] = [] + override class func canInit(with request: URLRequest) -> Bool { true } + override class func canonicalRequest(for request: URLRequest) -> URLRequest { request } + override func startLoading() { + let url = request.url! + DispatchQueue.main.async { + Self.requests.append(["url": url.absoluteString, "uptime": ProcessInfo.processInfo.systemUptime]) + } + guard url.host == "subbanner.qa.invalid" else { + client?.urlProtocol(self, didFailWithError: URLError(.notConnectedToInternet)); return + } + if url.path.contains("fail") { + client?.urlProtocol(self, didFailWithError: URLError(.cannotDecodeContentData)); return + } + let tall = url.path.contains("tall") + let size = CGSize(width: 1200, height: tall ? 600 : 400) + let format = UIGraphicsImageRendererFormat(); format.scale = 1 + let image = UIGraphicsImageRenderer(size: size, format: format).image { context in + (tall ? UIColor.systemOrange : UIColor.systemTeal).setFill() + context.fill(CGRect(origin: .zero, size: size)) + let title = tall ? "B 1200 x 600" : "A 1200 x 400" + title.draw(at: CGPoint(x: 64, y: 96), withAttributes: [.font: UIFont.boldSystemFont(ofSize: 70), .foregroundColor: UIColor.black]) + url.lastPathComponent.draw(at: CGPoint(x: 64, y: 210), withAttributes: [.font: UIFont.systemFont(ofSize: 42), .foregroundColor: UIColor.black]) + } + let deliver = { + self.client?.urlProtocol(self, didReceive: HTTPURLResponse(url: url, statusCode: 200, httpVersion: nil, headerFields: ["Content-Type": "image/png"])!, cacheStoragePolicy: .notAllowed) + self.client?.urlProtocol(self, didLoad: image.pngData()!) + self.client?.urlProtocolDidFinishLoading(self) + } + if url.path.contains("delay") { DispatchQueue.main.asyncAfter(deadline: .now() + 8, execute: deliver) } + else { deliver() } + } + override func stopLoading() {} +} + +struct PhaseProbe: View { + @Environment(\.scenePhase) private var phase + @Environment(\.accessibilityReduceMotion) private var reduce + var body: some View { + Color.clear.onAppear { HarnessDelegate.current?.record("environment", ["phase": String(describing: phase), "reduceMotion": reduce]) } + .onChange(of: phase) { HarnessDelegate.current?.record("phase", ["value": String(describing: $0)]) } + .onChange(of: reduce) { HarnessDelegate.current?.record("reduceMotion", ["value": $0]) } + } +} +struct HarnessRoot: View { + @ObservedObject var state = HarnessState.shared + var body: some View { + VStack(spacing: 0) { + Text("NATIVE QA / " + state.context).appFont(.caption2).foregroundColor(.white).frame(height: SodaSpacing.s24) + if state.mounted { + Group { + if state.context == "home" { + MainHomeRecommendationView(onTapLive: { _ in }, onTapCreator: { _ in }, onTapContent: { _ in }, onTapCharacter: { _ in }, onTapCommunityPost: { _ in }, onTapBanner: { _ in }, onTapLiveAll: {}, onTapFollowAll: { $0() }) + } else if state.context == "content" { + MainContentRecommendationView(onTapContent: { _ in }, onTapBanner: { _ in }, onTapSeries: { _ in }, onTapLatestAudioAll: {}, onTapNewAndHotAll: {}, onTapSodaLiveOnlyAll: {}, onTapFreeAudioAll: {}, onTapPointAudioAll: {}) + } else { + VStack(spacing: SodaSpacing.s24) { + QABar(name: "QA direct before") + if !state.items.isEmpty { + SubBannerCarousel(items: state.items) { item in HarnessDelegate.current?.record("carousel-onTap", ["id": item.id]) }.probe("carousel") + } + QABar(name: "QA direct after") + Spacer() + } + } + } + .frame(width: state.width) + .frame(maxWidth: .infinity, alignment: .leading) + .background(PhaseProbe()) + .environment(\.scenePhase, state.phase) + } + Spacer(minLength: 0) + } + .background(Color.black) + .onPreferenceChange(Rectangles.self) { state.frames = $0 } + } +} + +final class HarnessDelegate: NSObject, UIApplicationDelegate { + static weak var current: HarnessDelegate? + var window: UIWindow? + let state = HarnessState.shared + let started = ProcessInfo.processInfo.systemUptime + var events: [[String: Any]] = [] + let output = URL.documentsDirectory.appendingPathComponent("native", isDirectory: true) + func application(_ application: UIApplication, didFinishLaunchingWithOptions options: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { + Self.current = self + UserDefaults.standard.set("en", forKey: "app.language") + let config = URLSessionConfiguration.ephemeral + config.protocolClasses = [FixtureProtocol.self] + ImageDownloader.default.sessionConfiguration = config + try! FileManager.default.createDirectory(at: output, withIntermediateDirectories: true) + let window = UIWindow(frame: UIScreen.main.bounds) + window.rootViewController = UIHostingController(rootView: HarnessRoot()) + window.overrideUserInterfaceStyle = .dark + self.window = window + window.makeKeyAndVisible() + after(1) { Task { @MainActor in await self.run() } } + return true + } + func after(_ seconds: Double, _ action: @escaping () -> Void) { DispatchQueue.main.asyncAfter(deadline: .now() + seconds, execute: action) } + @MainActor func wait(_ seconds: Double) async { try? await Task.sleep(nanoseconds: UInt64(seconds * 1_000_000_000)) } + func record(_ event: String, _ details: [String: Any] = [:]) { + var entry = details + entry["event"] = event; entry["time"] = ProcessInfo.processInfo.systemUptime - started; entry["scenario"] = state.scenario + events.append(entry) + try! JSONSerialization.data(withJSONObject: events, options: [.prettyPrinted, .sortedKeys]).write(to: output.appendingPathComponent("actions.json")) + } + func sample(_ name: String) { + guard let window else { return } + let image = UIGraphicsImageRenderer(bounds: window.bounds).image { _ in window.drawHierarchy(in: window.bounds, afterScreenUpdates: true) } + try! image.pngData()!.write(to: output.appendingPathComponent(name + ".png")) + let frames = state.frames.mapValues { ["x": $0.minX, "y": $0.minY, "width": $0.width, "height": $0.height] } + record(name, ["frames": frames, "inputIDs": state.items.map(\.id), "width": state.width, "phase": String(describing: state.phase), "osReduceMotion": UIAccessibility.isReduceMotionEnabled, "requests": FixtureProtocol.requests, "png": name + ".png"]) + } + func banner(_ id: Int, _ path: String, _ context: String = "home") -> SubBanner { + SubBanner(id: id, placement: context == "content" ? .contentRecommendation : .homeRecommendation, imageUrl: "https://subbanner.qa.invalid/" + path, webUrl: "https://subbanner.qa.invalid/destination", androidDeepLink: nil, iosDeepLink: "sodaqa://banner/\(id)") + } + @MainActor func setup(_ name: String, context: String, items: [SubBanner]) async { + state.mounted = false + await wait(0.15) + state.scenario = name; state.context = context; state.width = 375; state.phase = .active; state.reduceMotion = false + state.update(items); state.mounted = true + record("scenario-start", ["context": context, "count": items.count]) + } + @MainActor func run() async { + for context in ["home", "content"] { + let pair = [banner(1, context + "-wide.png", context), banner(2, context + "-tall.png", context)] + await setup(context + "-pair", context: context, items: pair) + await wait(0.8); sample(context + "-first") + await wait(3.6); sample(context + "-before-5s") + await wait(0.75); sample(context + "-transition") + await wait(0.65); sample(context + "-second") + await wait(4.8); sample(context + "-wrap") + window?.overrideUserInterfaceStyle = .light + await wait(0.3); sample(context + "-light") + window?.overrideUserInterfaceStyle = .dark + state.update([]); await wait(0.4); sample(context + "-empty") + state.update([banner(3, context + "-single-wide.png", context)]) + await wait(0.6); sample(context + "-one") + await wait(6); sample(context + "-one-after-6s") + state.width = 320; await wait(0.5); sample(context + "-resize-320") + state.width = 375; await wait(0.3) + } + await setup("delayed-current", context: "direct", items: [banner(10, "delay-wide.png"), banner(11, "valid-tall.png")]) + await wait(0.6); sample("delayed-no-invented-height") + await wait(5.3); sample("delayed-next-valid") + await wait(2.7); sample("delayed-old-image-arrived") + await setup("failure-isolation", context: "content", items: [banner(20, "valid-wide.png"), banner(21, "fail.png")]) + await wait(0.8); sample("failure-valid-first") + await wait(5); sample("failure-placeholder-other-content") + await wait(5); sample("failure-wrap-valid") + await setup("inactive-remount", context: "direct", items: [banner(30, "lifecycle-wide.png"), banner(31, "lifecycle-tall.png")]) + await wait(5.8); sample("inactive-before") + state.phase = .inactive; record("inject-inactive") + await wait(0.3); sample("inactive-start") + await wait(6); sample("inactive-after-6s") + state.phase = .active; record("inject-active") + await wait(4.4); sample("active-before-5s") + await wait(1.3); sample("active-next") + state.mounted = false; record("unmount") + await wait(6); sample("unmounted-after-6s") + state.mounted = true; record("remount") + await wait(0.6); sample("remount-first") + await wait(5.1); sample("remount-next") + state.update([banner(32, "shrink-wide.png")]); await wait(0.6); sample("shrink-one") + await setup("reduced-motion", context: "direct", items: [banner(40, "reduce-wide.png"), banner(41, "reduce-tall.png")]) + record("reduce-motion-observation-only", ["osValue": UIAccessibility.isReduceMotionEnabled, "enabledTest": "NOT RUN: read-only SwiftUI environment; parent device preferences unchanged"]) + await wait(0.7); sample("reduce-first") + await wait(4.45); sample("reduce-transition") + await wait(0.7); sample("reduce-second") + await setup("twenty-one", context: "direct", items: (1...21).map { banner(100 + $0, "many-\($0)-wide.png") }) + await wait(0.8); sample("many-first") + await wait(99.8); sample("many-twenty-first") + await wait(5); sample("many-wrap") + let target = banner(900, "action-wide.png") + var attempts: [String] = [] + SubBannerLinkOpener.open(target) { url, completion in attempts.append(url.absoluteString); completion(false) } + record("opener-driver-not-physical-tap", ["attempts": attempts]) + record("finished") + } +} +@main struct Entry { + static func main() { UIApplicationMain(CommandLine.argc, CommandLine.unsafeArgv, nil, NSStringFromClass(HarnessDelegate.self)) } +} diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/source/assess.py b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/source/assess.py new file mode 100644 index 00000000..ed3e8970 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/source/assess.py @@ -0,0 +1,96 @@ +import hashlib +import json +from pathlib import Path +from PIL import Image, ImageDraw + +evidence = Path('docs/20261029_추천_서브_배너/reviews/evidence/native-final') +capture = evidence / 'captures' +events = json.loads((capture / 'actions.json').read_text()) +samples = {event['event']: event for event in events if 'png' in event} +before = json.loads((evidence / 'source-hashes-before.json').read_text()) +current = {path: hashlib.sha256(Path(path).read_bytes()).hexdigest() for path in before} +assert current == before, 'Product source changed; rerun native capture' +assert events[-1]['event'] == 'finished' +image_info = {} +for path in sorted(capture.glob('*.png')): + assert path.read_bytes().startswith(b'\x89PNG\r\n\x1a\n') + image = Image.open(path).convert('RGB') + assert image.size == (750, 1334) + assert path.stat().st_mtime > max(Path(source).stat().st_mtime for source in before) + image_info[path.name] = {'sha256': hashlib.sha256(path.read_bytes()).hexdigest(), 'width': image.width, 'height': image.height, 'fresh': True} +os_capture = evidence / 'native-os-final.png' +assert os_capture.read_bytes().startswith(b'\x89PNG\r\n\x1a\n') +assert Image.open(os_capture).size == (750, 1334) +assert os_capture.stat().st_mtime > max(Path(source).stat().st_mtime for source in before) + +def frame(name, key): + return samples[name]['frames'][key] + +def pixels(name): + image = Image.open(capture / (name + '.png')).convert('RGB') + rows = [] + for position in range(image.height): + left, right = image.getpixel((0, position)), image.getpixel((image.width - 1, position)) + def colored(value): + red, green, blue = value + return (blue > 140 and green > 110 and red < 100) or (red > 200 and 80 < green < 200 and blue < 60) + if colored(left) and colored(right): + rows.append(position) + if not rows: + return None + return {'x': 0, 'width': image.width / 2, 'y': min(rows) / 2, 'height': len(rows) / 2} + +geometry = {} +for context, preceding, following in [('home', 'QA active creators', 'QA debut creators'), ('content', 'QA content main banner', 'QA exclusive series')]: + for suffix, expected in [('first', 125), ('second', 187.5)]: + name = context + '-' + suffix + previous = frame(name, preceding) + next_frame = frame(name, following) + image = pixels(name) + derived_height = next_frame['y'] - previous['y'] - previous['height'] - 48 + assert derived_height == expected + assert image['height'] == expected and image['width'] == 375 and image['x'] == 0 + assert image['y'] - previous['y'] - previous['height'] == 24 + assert next_frame['y'] - image['y'] - image['height'] == 24 + geometry[name] = {'pixelFramePt': image, 'precedingGapPt': 24, 'followingGapPt': 24, 'derivedHeightPt': derived_height} + empty = context + '-empty' + previous, next_frame = frame(empty, preceding), frame(empty, following) + assert next_frame['y'] - previous['y'] - previous['height'] == 24 + assert pixels(empty) is None + geometry[empty] = {'remainingOrdinarySectionGapPt': 24, 'extraBannerGapPt': 0} + resized = pixels(context + '-resize-320') + image = Image.open(capture / (context + '-resize-320.png')).convert('RGB') + line = [image.getpixel((position, 600 if context == 'home' else 320)) for position in range(image.width)] + colored_positions = [position for position, (red, green, blue) in enumerate(line) if blue > 140 and green > 110 and red < 100] + assert min(colored_positions) == 0 and max(colored_positions) == 639 + previous = frame(context + '-resize-320', preceding) + next_frame = frame(context + '-resize-320', following) + height = next_frame['y'] - previous['y'] - previous['height'] - 48 + assert abs(height - 320 / 3) < 0.51 + geometry[context + '-resize-320'] = {'pixelWidthPt': 320, 'geometryHeightPt': height, 'roundingTolerancePt': 0.51} + assert image_info[context + '-one.png']['sha256'] == image_info[context + '-one-after-6s.png']['sha256'] + +assert frame('delayed-no-invented-height', 'carousel')['height'] == 0 +assert frame('delayed-next-valid', 'carousel')['height'] == 187.5 +assert frame('delayed-old-image-arrived', 'carousel')['height'] == 187.5 +assert image_info['inactive-start.png']['sha256'] == image_info['inactive-after-6s.png']['sha256'] +assert len(samples['many-wrap']['inputIDs']) == 21 +attempts = next(event['attempts'] for event in events if event['event'] == 'opener-driver-not-physical-tap') +assert attempts == ['sodaqa://banner/900', 'https://subbanner.qa.invalid/destination'] + +manifest = list(image_info) +for sheet_index in range(0, len(manifest), 12): + sheet = Image.new('RGB', (1000, 1422), 'white') + draw = ImageDraw.Draw(sheet) + for index, name in enumerate(manifest[sheet_index:sheet_index + 12]): + image = Image.open(capture / name).convert('RGB') + image.thumbnail((250, 445)) + column, row = index % 4, index // 4 + draw.text((column * 250 + 4, row * 474 + 4), name, fill='black') + sheet.paste(image, (column * 250, row * 474 + 25)) + sheet.save(evidence / ('contact-' + str(sheet_index // 12 + 1) + '.png')) + +result = {'nativeCaptureCount': len(image_info), 'additionalOSCapture': {'path': 'native-os-final.png', 'sha256': hashlib.sha256(os_capture.read_bytes()).hexdigest(), 'width': 750, 'height': 1334, 'fresh': True}, 'productHashesUnchanged': current == before, 'geometry': geometry, 'pngs': image_info, 'assertions': 'PASS', 'manualFilenameCorrection': {'many-twenty-first.png': 'Observed 20/21', 'many-wrap.png': 'Observed 21/21', 'native-os-final.png': 'Observed 01/21 after wrap'}, 'reduceMotionEnabledTest': 'NOT RUN; OS false; read-only environment; parent preferences unchanged', 'physicalTapAndSwipe': 'NOT RUN; no native coordinate input tool'} +(evidence / 'verification.json').write_text(json.dumps(result, indent=2)) +(evidence / 'source-hashes-final.json').write_text(json.dumps(current, indent=2)) +print(json.dumps({'nativeCaptures': len(image_info), 'geometry': geometry, 'assertions': 'PASS'}, indent=2)) diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/source/cleanup.py b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/source/cleanup.py new file mode 100644 index 00000000..09eab727 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/source/cleanup.py @@ -0,0 +1,42 @@ +import hashlib +import json +import os +from pathlib import Path +import shutil +import subprocess + +evidence = Path('docs/20261029_추천_서브_배너/reviews/evidence/native-final') +ownership = json.loads((evidence / 'ownership.json').read_text()) +root = Path(ownership['root']) +for key in ['TMPDIR', 'NODE_COMPILE_CACHE', 'BUN_RUNTIME_TRANSPILER_CACHE_PATH']: + os.environ[key] = str(root) +commands = [] +def run(args): + result = subprocess.run(args, text=True, capture_output=True) + receipt = {'args': args, 'exit': result.returncode, 'stdout': result.stdout, 'stderr': result.stderr} + commands.append(receipt) + return receipt + +device, bundle = ownership['device'], ownership['bundle'] +assert root.name == 'sub-banner-native-final.97cQKj' +assert bundle == 'qa.sodalive.subbanner.native.final.97cQKj' +assert run(['xcrun', 'simctl', 'terminate', device, bundle])['exit'] == 0 +assert run(['xcrun', 'simctl', 'uninstall', device, bundle])['exit'] == 0 +missing = run(['xcrun', 'simctl', 'get_app_container', device, bundle, 'data']) +assert missing['exit'] != 0 +process = run(['ps', '-p', str(ownership['pid']), '-o', 'pid=,comm=']) +assert process['exit'] != 0 +shutil.rmtree(root) +devices = json.loads(run(['xcrun', 'simctl', 'list', 'devices', 'booted', '-j'])['stdout']) +parent = next(item for items in devices['devices'].values() for item in items if item['udid'] == device) +assert parent['state'] == 'Booted' +before = json.loads((evidence / 'source-hashes-before.json').read_text()) +current = {path: hashlib.sha256(Path(path).read_bytes()).hexdigest() for path in before} +assert current == before +products = Path('/Users/klaus/Library/Developer/Xcode/DerivedData/SodaLive-clzdtvqrsfonoxcijatzkrqdzrsu/Build/Products/Debug-iphonesimulator') +dependencies = ['Kingfisher.o', 'Kingfisher.swiftmodule/x86_64-apple-ios-simulator.swiftmodule'] +dependency_hashes = {str(products / path): hashlib.sha256((products / path).read_bytes()).hexdigest() for path in dependencies} +(evidence / 'dependency-hashes.json').write_text(json.dumps(dependency_hashes, indent=2)) +receipt = {'commands': commands, 'appAbsent': True, 'pidAbsent': True, 'rootAbsent': not root.exists(), 'parentDeviceStillBooted': True, 'productHashesUnchanged': current == before, 'sharedCacheDeleted': False} +(evidence / 'cleanup.json').write_text(json.dumps(receipt, indent=2)) +print(json.dumps({key: value for key, value in receipt.items() if key != 'commands'}, indent=2)) diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/source/provenance.py b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/source/provenance.py new file mode 100644 index 00000000..3f5ab83d --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/source/provenance.py @@ -0,0 +1,57 @@ +import ast +from datetime import datetime, timezone +import hashlib +import json +import os +from pathlib import Path +import shutil +import subprocess +import sys + +evidence = Path('docs/20261029_추천_서브_배너/reviews/evidence/native-final') +original = evidence.with_name('native') +root = Path('/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-native-final.97cQKj') +bundle = 'qa.sodalive.subbanner.native.final.97cQKj' +expected = '7e25db3076d545ffc61ea3a1c5585e7b8a617964ef406d7570286d75a75e010a' +carousel = 'SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift' +for key in ['TMPDIR', 'NODE_COMPILE_CACHE', 'BUN_RUNTIME_TRANSPILER_CACHE_PATH']: + assert os.environ[key] == str(root) +sources = json.loads((original / 'source-hashes-before.json').read_text()) +current = {path: hashlib.sha256(Path(path).read_bytes()).hexdigest() for path in sources} +assert current[carousel] == expected +protected = sorted(path for path in original.rglob('*') if path.is_file()) +protected += [evidence.parents[1] / 'phase1-sub-banner-integration.md', evidence.parents[2] / 'plan-task.md', evidence.parents[2] / 'prd.md'] +protected_hashes = {str(path): hashlib.sha256(path.read_bytes()).hexdigest() for path in protected} +clone_checks = {} +for name in ['run.py', 'assess.py', 'cleanup.py', 'Doubles.swift', 'Harness.swift']: + baseline = (original / 'source' / name).read_text() + normalized = (evidence / 'source' / name).read_text().replace('evidence/native-final', 'evidence/native').replace('sub-banner-native-final.97cQKj', 'sub-banner-native.tstn99').replace(bundle, 'qa.sodalive.subbanner.native.tstn99') + assert normalized == baseline, name + clone_checks[name] = 'IDENTICAL after evidence root/temp root/bundle normalization' +for script in (evidence / 'source').glob('*.py'): + ast.parse(script.read_text(), filename=str(script)) +products = Path('/Users/klaus/Library/Developer/Xcode/DerivedData/SodaLive-clzdtvqrsfonoxcijatzkrqdzrsu/Build/Products/Debug-iphonesimulator') +dependencies = {str(products / path): hashlib.sha256((products / path).read_bytes()).hexdigest() for path in ['Kingfisher.o', 'Kingfisher.swiftmodule/x86_64-apple-ios-simulator.swiftmodule']} +after = '--after' in sys.argv +if after: + before = json.loads((evidence / 'provenance-before.json').read_text()) + assert current == before['sourceHashes'] + assert protected_hashes == before['protectedHashes'] + assert dependencies == before['dependencyHashes'] + assert not root.exists() +else: + assert root.is_dir() + assert not (evidence / 'provenance-before.json').exists() + for path in sources: + destination = evidence / 'production-source-snapshot' / path + destination.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(path, destination) +snapshots = {path: hashlib.sha256((evidence / 'production-source-snapshot' / path).read_bytes()).hexdigest() for path in sources} +assert snapshots == current +device_result = subprocess.run(['xcrun', 'simctl', 'list', 'devices', 'booted', '-j'], capture_output=True, text=True, check=True) +device_data = json.loads(device_result.stdout) +device = next(item for items in device_data['devices'].values() for item in items if item['udid'] == '482F7827-2E3A-4559-BAF4-48B68CC5725B') +assert device['state'] == 'Booted' +result = {'recordedAtUTC': datetime.now(timezone.utc).isoformat(), 'expectedCarouselSHA256': expected, 'sourceHashes': current, 'snapshotHashes': snapshots, 'sourceMtimes': {path: Path(path).stat().st_mtime for path in sources}, 'protectedHashes': protected_hashes, 'dependencyHashes': dependencies, 'harnessCloneChecks': clone_checks, 'ownedRoot': str(root), 'ownedBundle': bundle, 'ownedCache': str(root / 'module-cache'), 'cacheEnvironment': {key: os.environ[key] for key in ['TMPDIR', 'NODE_COMPILE_CACHE', 'BUN_RUNTIME_TRANSPILER_CACHE_PATH']}, 'parentDevice': device, 'afterCleanup': after, 'assertions': 'PASS'} +(evidence / ('provenance-after.json' if after else 'provenance-before.json')).write_text(json.dumps(result, indent=2)) +print(json.dumps({'afterCleanup': after, 'carouselSHA256': expected, 'sourceSnapshots': len(snapshots), 'protectedFiles': len(protected_hashes), 'harnessCloneChecks': clone_checks, 'assertions': 'PASS'}, indent=2)) diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/source/run.py b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/source/run.py new file mode 100644 index 00000000..b9240bcb --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/source/run.py @@ -0,0 +1,68 @@ +import hashlib +import json +import os +from pathlib import Path +import plistlib +import shutil +import subprocess +import time + +repo = Path.cwd() +evidence = repo / 'docs/20261029_추천_서브_배너/reviews/evidence/native-final' +root = Path('/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-native-final.97cQKj') +for key in ['TMPDIR', 'NODE_COMPILE_CACHE', 'BUN_RUNTIME_TRANSPILER_CACHE_PATH']: + os.environ[key] = str(root) +device = '482F7827-2E3A-4559-BAF4-48B68CC5725B' +bundle = 'qa.sodalive.subbanner.native.final.97cQKj' +products = Path('/Users/klaus/Library/Developer/Xcode/DerivedData/SodaLive-clzdtvqrsfonoxcijatzkrqdzrsu/Build/Products/Debug-iphonesimulator') +sources = [ + 'SodaLive/Sources/V2/Component/Banner/SubBanner.swift', + 'SodaLive/Sources/V2/Component/Banner/SubBannerLinkOpener.swift', + 'SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift', + 'SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift', + 'SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationView.swift', + 'SodaLive/Sources/UI/Theme/Spacing.swift', + 'SodaLive/Sources/UI/Theme/Color.swift', + 'SodaLive/Sources/Extensions/ColorExtension.swift', + 'SodaLive/Sources/Extensions/FontModifier.swift', +] +previous = evidence / 'commands.json' +commands = json.loads(previous.read_text()) if previous.exists() else [] +def run(args, timeout=120): + result = subprocess.run([str(arg) for arg in args], text=True, capture_output=True, timeout=timeout) + commands.append({'args': [str(arg) for arg in args], 'exit': result.returncode, 'stdout': result.stdout, 'stderr': result.stderr}) + (evidence / 'commands.json').write_text(json.dumps(commands, indent=2)) + print(result.stdout, result.stderr, flush=True) + result.check_returncode() + return result.stdout.strip() + +hashes = {path: hashlib.sha256((repo / path).read_bytes()).hexdigest() for path in sources} +(evidence / 'source-hashes-before.json').write_text(json.dumps(hashes, indent=2)) +app = root / 'SubBannerQA.app' +app.mkdir(exist_ok=True) +with (app / 'Info.plist').open('wb') as stream: + plistlib.dump({'CFBundleIdentifier': bundle, 'CFBundleName': 'SubBannerQA', 'CFBundleExecutable': 'SubBannerQA', 'CFBundlePackageType': 'APPL', 'CFBundleVersion': '1', 'CFBundleShortVersionString': '1', 'MinimumOSVersion': '16.6', 'LSRequiresIPhoneOS': True, 'UIDeviceFamily': [1], 'UILaunchScreen': {}}, stream) +sdk = run(['xcrun', '--sdk', 'iphonesimulator', '--show-sdk-path']) +run(['xcrun', '--sdk', 'iphonesimulator', 'swiftc', '-swift-version', '5', '-target', 'x86_64-apple-ios16.6-simulator', '-sdk', sdk, '-I', products, '-module-cache-path', root / 'module-cache', *sources, evidence / 'source/Doubles.swift', evidence / 'source/Harness.swift', products / 'Kingfisher.o', '-o', app / 'SubBannerQA'], timeout=180) +run(['xcrun', 'simctl', 'install', device, app]) +launch = run(['xcrun', 'simctl', 'launch', device, bundle]) +(evidence / 'ownership.json').write_text(json.dumps({'root': str(root), 'bundle': bundle, 'pid': int(launch.split(':')[-1]), 'device': device, 'deviceOwner': 'parent'}, indent=2)) +container = Path(run(['xcrun', 'simctl', 'get_app_container', device, bundle, 'data'])) +native = container / 'Documents/native' +deadline = time.monotonic() + 330 +while time.monotonic() < deadline: + time.sleep(5) + action_file = native / 'actions.json' + if action_file.exists(): + events = json.loads(action_file.read_text()) + print(events[-1]['event'], events[-1]['time'], flush=True) + if events[-1]['event'] == 'finished': + break +else: + print('TIMEOUT; preserving partial output', flush=True) +shutil.copytree(native, evidence / 'captures', dirs_exist_ok=True) +after = {path: hashlib.sha256((repo / path).read_bytes()).hexdigest() for path in sources} +(evidence / 'source-hashes-after.json').write_text(json.dumps(after, indent=2)) +(evidence / 'source-stability.json').write_text(json.dumps({'unchanged': hashes == after, 'changed': [path for path in sources if hashes[path] != after[path]]}, indent=2)) +run(['xcrun', 'simctl', 'io', device, 'screenshot', evidence / 'native-os-final.png']) +print('CAPTURES COPIED; OWN APP STILL INSTALLED UNTIL ASSESSMENT', flush=True) diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native-final/verification.json b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/verification.json new file mode 100644 index 00000000..0d37eac1 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native-final/verification.json @@ -0,0 +1,331 @@ +{ + "nativeCaptureCount": 41, + "additionalOSCapture": { + "path": "native-os-final.png", + "sha256": "a2228f05c1ebf83cde90f2cbd4c830e4a495b001dc5acfbf58c2faee950e737a", + "width": 750, + "height": 1334, + "fresh": true + }, + "productHashesUnchanged": true, + "geometry": { + "home-first": { + "pixelFramePt": { + "x": 0, + "width": 375.0, + "y": 280.0, + "height": 125.0 + }, + "precedingGapPt": 24, + "followingGapPt": 24, + "derivedHeightPt": 125 + }, + "home-second": { + "pixelFramePt": { + "x": 0, + "width": 375.0, + "y": 280.0, + "height": 187.5 + }, + "precedingGapPt": 24, + "followingGapPt": 24, + "derivedHeightPt": 187.5 + }, + "home-empty": { + "remainingOrdinarySectionGapPt": 24, + "extraBannerGapPt": 0 + }, + "home-resize-320": { + "pixelWidthPt": 320, + "geometryHeightPt": 106.66666666666669, + "roundingTolerancePt": 0.51 + }, + "content-first": { + "pixelFramePt": { + "x": 0, + "width": 375.0, + "y": 136.0, + "height": 125.0 + }, + "precedingGapPt": 24, + "followingGapPt": 24, + "derivedHeightPt": 125 + }, + "content-second": { + "pixelFramePt": { + "x": 0, + "width": 375.0, + "y": 136.0, + "height": 187.5 + }, + "precedingGapPt": 24, + "followingGapPt": 24, + "derivedHeightPt": 187.5 + }, + "content-empty": { + "remainingOrdinarySectionGapPt": 24, + "extraBannerGapPt": 0 + }, + "content-resize-320": { + "pixelWidthPt": 320, + "geometryHeightPt": 106.66666666666669, + "roundingTolerancePt": 0.51 + } + }, + "pngs": { + "active-before-5s.png": { + "sha256": "f7bf598d63d31ebffac09bcb5d3cd57183fe3f9da9f4120e14ccd22983c27ca5", + "width": 750, + "height": 1334, + "fresh": true + }, + "active-next.png": { + "sha256": "4880e28aeae451cddbda8f2ba58ec147b60440f26cfa86d75dbf0e6fa381b01f", + "width": 750, + "height": 1334, + "fresh": true + }, + "content-before-5s.png": { + "sha256": "e03bdd3d19802929920672f7f39d26df4bef465d9b13981250cc5fb02f2111a9", + "width": 750, + "height": 1334, + "fresh": true + }, + "content-empty.png": { + "sha256": "ee561f7ce93a7cd7c719dfb32eda4b40e6d0d7998bbb71cfdfa356cdc60e7bcb", + "width": 750, + "height": 1334, + "fresh": true + }, + "content-first.png": { + "sha256": "e03bdd3d19802929920672f7f39d26df4bef465d9b13981250cc5fb02f2111a9", + "width": 750, + "height": 1334, + "fresh": true + }, + "content-light.png": { + "sha256": "e03bdd3d19802929920672f7f39d26df4bef465d9b13981250cc5fb02f2111a9", + "width": 750, + "height": 1334, + "fresh": true + }, + "content-one-after-6s.png": { + "sha256": "c8c2eeee836d90b3422fe1cf7e8646f094e0db78e7bde73880e499317ca1c84d", + "width": 750, + "height": 1334, + "fresh": true + }, + "content-one.png": { + "sha256": "c8c2eeee836d90b3422fe1cf7e8646f094e0db78e7bde73880e499317ca1c84d", + "width": 750, + "height": 1334, + "fresh": true + }, + "content-resize-320.png": { + "sha256": "a6313fb5db500af6e6880ed4f62d42ed2f8e3d2c75f2adaa7d85d8186fffa0cc", + "width": 750, + "height": 1334, + "fresh": true + }, + "content-second.png": { + "sha256": "3558fe0a407f41360c69b241dc5c7bb63d5e2a5b73450db5918b2c87dcfaff13", + "width": 750, + "height": 1334, + "fresh": true + }, + "content-transition.png": { + "sha256": "cc146354db59430f125af45ede79a915e5fbc23aa7c2632cca070389c81432e8", + "width": 750, + "height": 1334, + "fresh": true + }, + "content-wrap.png": { + "sha256": "e03bdd3d19802929920672f7f39d26df4bef465d9b13981250cc5fb02f2111a9", + "width": 750, + "height": 1334, + "fresh": true + }, + "delayed-next-valid.png": { + "sha256": "6c20a5f54dc012c9a7d9259c9a7d0628f250262291bced052ab92652e98342c3", + "width": 750, + "height": 1334, + "fresh": true + }, + "delayed-no-invented-height.png": { + "sha256": "60643fc2ef02e26153272782cc4a97422d6ab0b08a0ca5422a94906c1fa4ff5a", + "width": 750, + "height": 1334, + "fresh": true + }, + "delayed-old-image-arrived.png": { + "sha256": "6c20a5f54dc012c9a7d9259c9a7d0628f250262291bced052ab92652e98342c3", + "width": 750, + "height": 1334, + "fresh": true + }, + "failure-placeholder-other-content.png": { + "sha256": "217c258f4a1b83f9c9bf3564cf615a5ef2d0600f1a62819cc7fc2a7becedaa9e", + "width": 750, + "height": 1334, + "fresh": true + }, + "failure-valid-first.png": { + "sha256": "96b2051aedb2733a5620b2de5305cac9532cf9f266b3292d91f550c63550f60b", + "width": 750, + "height": 1334, + "fresh": true + }, + "failure-wrap-valid.png": { + "sha256": "96b2051aedb2733a5620b2de5305cac9532cf9f266b3292d91f550c63550f60b", + "width": 750, + "height": 1334, + "fresh": true + }, + "home-before-5s.png": { + "sha256": "8fc8502f06140d7f04bfb6defea3c235639e874172014611ead622d1ac5306ca", + "width": 750, + "height": 1334, + "fresh": true + }, + "home-empty.png": { + "sha256": "34af8cfecd132f08666cdabd7972a44a536977e5ebee4244a61d1c697b7184a0", + "width": 750, + "height": 1334, + "fresh": true + }, + "home-first.png": { + "sha256": "8fc8502f06140d7f04bfb6defea3c235639e874172014611ead622d1ac5306ca", + "width": 750, + "height": 1334, + "fresh": true + }, + "home-light.png": { + "sha256": "8fc8502f06140d7f04bfb6defea3c235639e874172014611ead622d1ac5306ca", + "width": 750, + "height": 1334, + "fresh": true + }, + "home-one-after-6s.png": { + "sha256": "6dd493128e266ec896e9f5bc2fec1756fad5f5673e88235a7dc342b989515448", + "width": 750, + "height": 1334, + "fresh": true + }, + "home-one.png": { + "sha256": "6dd493128e266ec896e9f5bc2fec1756fad5f5673e88235a7dc342b989515448", + "width": 750, + "height": 1334, + "fresh": true + }, + "home-resize-320.png": { + "sha256": "78760d7c64bcaa360e7147f3f9fefba038ed177e223cbbd4c0c4377ab6e91399", + "width": 750, + "height": 1334, + "fresh": true + }, + "home-second.png": { + "sha256": "8ce24e8f55ea474bd74822282cf180571e6142bfc0942614acc2c6f4d4ac8ac0", + "width": 750, + "height": 1334, + "fresh": true + }, + "home-transition.png": { + "sha256": "39341418313ee206027f04a40bc7f72b526c92821b4ddfdbfa37782a317378e4", + "width": 750, + "height": 1334, + "fresh": true + }, + "home-wrap.png": { + "sha256": "8fc8502f06140d7f04bfb6defea3c235639e874172014611ead622d1ac5306ca", + "width": 750, + "height": 1334, + "fresh": true + }, + "inactive-after-6s.png": { + "sha256": "f7bf598d63d31ebffac09bcb5d3cd57183fe3f9da9f4120e14ccd22983c27ca5", + "width": 750, + "height": 1334, + "fresh": true + }, + "inactive-before.png": { + "sha256": "f7bf598d63d31ebffac09bcb5d3cd57183fe3f9da9f4120e14ccd22983c27ca5", + "width": 750, + "height": 1334, + "fresh": true + }, + "inactive-start.png": { + "sha256": "f7bf598d63d31ebffac09bcb5d3cd57183fe3f9da9f4120e14ccd22983c27ca5", + "width": 750, + "height": 1334, + "fresh": true + }, + "many-first.png": { + "sha256": "44a42bfedbf994063463e774e4c6d397ea33a71e1443489e3e5a2a0f7f48a1a9", + "width": 750, + "height": 1334, + "fresh": true + }, + "many-twenty-first.png": { + "sha256": "c4173138625345685ed9a945fbd359c2cae59b9265cba158f9c563722680133d", + "width": 750, + "height": 1334, + "fresh": true + }, + "many-wrap.png": { + "sha256": "3844369f5cf4ea0fa2a2a148c06dac9ada3b3838664f5d4a97fbdaa9c3bdbfdb", + "width": 750, + "height": 1334, + "fresh": true + }, + "reduce-first.png": { + "sha256": "c88339572c7bee2dc6c952958ce148331782f28d4b455d2ba9c4a746c64a4ba9", + "width": 750, + "height": 1334, + "fresh": true + }, + "reduce-second.png": { + "sha256": "77c8bcbcb1c693bc5d713fc3e97b7c81c9ed83999eecb3f6858966acf1cbe4dd", + "width": 750, + "height": 1334, + "fresh": true + }, + "reduce-transition.png": { + "sha256": "c88339572c7bee2dc6c952958ce148331782f28d4b455d2ba9c4a746c64a4ba9", + "width": 750, + "height": 1334, + "fresh": true + }, + "remount-first.png": { + "sha256": "4880e28aeae451cddbda8f2ba58ec147b60440f26cfa86d75dbf0e6fa381b01f", + "width": 750, + "height": 1334, + "fresh": true + }, + "remount-next.png": { + "sha256": "f7bf598d63d31ebffac09bcb5d3cd57183fe3f9da9f4120e14ccd22983c27ca5", + "width": 750, + "height": 1334, + "fresh": true + }, + "shrink-one.png": { + "sha256": "23ba00bf778494937ecd3f4f15dd36007127cdf412cb33d5c1f54f2f3a7b95e0", + "width": 750, + "height": 1334, + "fresh": true + }, + "unmounted-after-6s.png": { + "sha256": "8fc8aa79c74ce769ce96965823337521634feb015d742ecf1b5a21cabd275e8b", + "width": 750, + "height": 1334, + "fresh": true + } + }, + "assertions": "PASS", + "manualFilenameCorrection": { + "many-twenty-first.png": "Observed 20/21", + "many-wrap.png": "Observed 21/21", + "native-os-final.png": "Observed 02/21 after wrap; many-2-wide.png; manually checked current OS capture" + }, + "reduceMotionEnabledTest": "NOT RUN; OS false; read-only environment; parent preferences unchanged", + "physicalTapAndSwipe": "NOT RUN; no native coordinate input tool" +} diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/README.md b/docs/20261029_추천_서브_배너/reviews/evidence/native/README.md new file mode 100644 index 00000000..12c9c677 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native/README.md @@ -0,0 +1,72 @@ +# 추천 서브 배너 Native QA + +**결과: 실행한 native 배치·비율·empty·자동 순환·이미지 실패 격리 시나리오 PASS. 물리 탭/swipe, Reduce Motion ON, 실제 앱 전체 통합은 NOT RUN이다.** 제품 파일, 부모 PRD/계획은 수정하지 않았다. + +## 실행 Surface와 출처 + +- 부모 소유 iPhone SE3/iOS 18.3, UDID `482F7827-2E3A-4559-BAF4-48B68CC5725B`, 375×667pt, 2x. Xcode 26 SDK로 `x86_64-apple-ios16.6-simulator` 대상 독립 UIKit `UIHostingController` 앱을 컴파일했다. iOS 16.6 런타임 실행은 아니다. +- **production `SubBanner.swift`, `SubBannerLinkOpener.swift`, `SubBannerCarousel.swift`, `MainHomeRecommendationView.swift`, `MainContentRecommendationView.swift`를 수정·추출·복제하지 않고 원본 경로 그대로 컴파일했다.** 실제 spacing/color/hex/font 소스도 동일하게 포함했다. +- Kingfisher는 prebuilt `Kingfisher.o`와 x86_64 module을 링크했다. workspace/project `Package.resolved`는 `7.12.0`이다. `KFImage`를 대역으로 바꾸지 않았다. [dependency-hashes.json](dependency-hashes.json)에 링크 입력 해시가 있다. +- Kingfisher의 실제 downloader에 `URLSessionConfiguration.ephemeral.protocolClasses = [FixtureProtocol.self]`를 설정했다. QA host만 생성 PNG로 응답하고 그 외 요청은 로컬에서 실패시킨다. 인터넷·서버·계정은 사용하지 않았다. 1200×400 teal A, 1200×600 orange B는 explicit QA 이미지이며 이미지 안 글자는 fixture 내용이다. +- [source-hashes-before.json](source-hashes-before.json), [source-hashes-after.json](source-hashes-after.json), [source-hashes-final.json](source-hashes-final.json)은 production 입력 9개 모두 동일하다. [source-stability.json](source-stability.json)의 `unchanged=true`. 따라서 이번 실행 중 재캡처가 필요한 제품 변경은 없었다. + +## 명시적인 대역과 한계 + +[source/Doubles.swift](source/Doubles.swift)에 대체 목록 전체가 있다. 두 recommendation **View composition 자체는 실제 production**이며, 하위 섹션은 `QA ...`라고 적힌 48pt 회색 bar 또는 `EmptyView`로 대체했다. VM은 네트워크를 호출하지 않는 fixture VM, DTO는 최소 `FixtureResponse`, refresh wrapper는 native `ScrollView`, toast/purchase/media player는 무효 대역이다. 실제 기존 메인 배너 carousel은 이 하네스에서 실행하지 않았다. + +따라서 원문 View의 조건부 삽입 위치·`VStack(s24)`·부모 수평 padding 없음은 검증했지만, 실제 추천 카드 높이/로딩/refresh/toast/구매·API·인증·추천 response 필터 및 콘텐츠 `isEmpty`는 이 QA로 검증하지 않았다. `FixtureResponse.eligibleSubBanners`는 `isEligible`만 사용한다. placement 분리·실서버 목록은 부모 focused checks 범위다. 오류 격리에서 주변 **대역 bar 유지**를 관찰했으며 실제 콘텐츠의 탭 동작을 증명하지 않는다. + +## 증거와 판정 + +[captures/actions.json](captures/actions.json)에 monotonic timestamp, 입력 id 전체, 이미지 요청, scene phase, OS Reduce Motion 값, 주변 섹션/direct carousel 실제 geometry와 callback driver 결과가 있다. 모든 41개 native PNG는 앱 window `drawHierarchy` 캡처이며 별도 [native-os-final.png](native-os-final.png)는 `simctl io screenshot`이다. 42개 원본은 750×1334 PNG다. [contact-1.png](contact-1.png)~[contact-4.png](contact-4.png)는 원본을 축소 배치한 검토용 contact sheet이지 별도 UI 구현이 아니다. + +| 시나리오 | 판정 | 실제 관측/증거 | +|---|---|---| +| 홈 지정 위치 | PASS | [home-first](captures/home-first.png): QA active creators 다음, debut 이전. 기존 main banner 앞뒤 순서 유지 | +| 콘텐츠 지정 위치 | PASS | [content-first](captures/content-first.png): QA main banner 다음, exclusive 이전 | +| 전체 폭·원본 비율 | PASS | 양 화면 A는 x=0,width=375,height=125pt, B는 x=0,width=375,height=187.5pt. [home-second](captures/home-second.png), [content-second](captures/content-second.png) | +| s24·empty 추가 gap 없음 | PASS | 이미지 위/아래 각각 24pt. empty는 인접 bar 사이 일반 24pt만 남고 배너 추가 간격 0pt. [home-empty](captures/home-empty.png), [content-empty](captures/content-empty.png) | +| 실제 5초 sleep 자동 전환 | PASS, 정밀도 한정 | production sleep 값 변경 없음. Home은 scenario 시작 후 4.928s 기록까지 A, 5.807s 기록의 transition에서 B 이동 중, 6.553s 기록에 B 정착. Content는 4.632s 기록 A, 5.526s 기록 transition, 6.306s 기록 B. 기록은 screenshot 완료 뒤라 transition 시각은 정확한 시작점이 아니다 | +| 2항목 wrap | PASS | [home-wrap](captures/home-wrap.png), [content-wrap](captures/content-wrap.png): 다시 A 01/02 | +| 단일 항목 고정 | PASS, timer 생성 여부 직접 계측 아님 | 양 화면 one/one-after-6s PNG SHA-256 동일, 01/01 유지. production count guard도 보존 | +| 21개·20개 제한 없음 | PASS | many-first 01/21 → [many-twenty-first](captures/many-twenty-first.png)는 실제 **20/21** → [many-wrap](captures/many-wrap.png)는 실제 **21/21** → OS final 01/21. 실제 약 106초 기다렸고 timer/선택 state를 직접 바꾸지 않음 | +| 컨테이너 resize | PASS | 양 화면 375→320pt에서 x=0,width=320,height=106.6667pt. [home-resize-320](captures/home-resize-320.png), [content-resize-320](captures/content-resize-320.png). 기기 회전/실제 화면 resize가 아니라 부모 SwiftUI frame 변경 | +| 지연 이미지·현재 비율 격리 | PASS | 첫 A 요청 8초 지연, 초기 carousel height=0. [delayed-next-valid](captures/delayed-next-valid.png)에서 B height187.5. 늦은 A 성공 후 [delayed-old-image-arrived](captures/delayed-old-image-arrived.png)도 B/187.5 유지 | +| 실패 이미지 국소 처리 | PASS | A→실패 B에서 height125 회색 placeholder와 02/02, 주변 QA bar 유지. [failure-placeholder-other-content](captures/failure-placeholder-other-content.png). 이후 [failure-wrap-valid](captures/failure-wrap-valid.png)에서 A 정상 | +| inactive 중지/active 재개 | PASS, 주입 환경 | SwiftUI scenePhase `.inactive` 실제 전달 probe 후 6.094초 두 PNG SHA 동일 B 유지. active 전달 후 4.790초 기록 B, 6.276초 기록 A. [inactive-after-6s](captures/inactive-after-6s.png), [active-next](captures/active-next.png). OS background 실험은 NOT RUN | +| unmount/remount·축소 | PASS | 6초 unmount 후 [remount-first](captures/remount-first.png) A, 이후 [remount-next](captures/remount-next.png) B. 2→1은 [shrink-one](captures/shrink-one.png) A/01/01 | +| light/dark | PASS, 제품 black 유지 | 각 화면 light capture와 dark first 동일한 검은 화면/legible indicator. production black 배경과 하네스 명시 override 사용. System theme 전환 전체 앱은 미검증 | +| 링크 fallback driver | PASS, 실제 탭 아님 | production opener에 false completion 주입: `sodaqa://banner/900` 후 `https://subbanner.qa.invalid/destination` 각 1회. `opener-driver-not-physical-tap` 로그. `UIApplication.open` 실제 실행 없음 | +| physical tap/swipe·hit testing | NOT RUN | native 좌표 입력 도구 없음. 캐러셀의 `onTap` callback은 physical input으로 실행하지 않음. gesture 정의 확인을 동작 PASS로 취급하지 않음 | +| Reduce Motion ON | NOT RUN | `accessibilityReduceMotion`은 read-only environment라 setter 주입 compile 실패. 해당 modifier를 QA 소스에서 제거 후 성공. OS 값 false trace만 수집. 부모 simulator 접근성 설정은 바꾸지 않음. `reduce-*` 이름은 ON 증거가 아님 | + +`many-twenty-first`/`many-wrap`/`reduce-transition` 등 이름은 예정 촬영 시점명일 뿐 실제 상태 판정이 아니다. 실제 페이지/상태는 위 관측과 PNG를 따른다. scheduler 및 이미지 준비 시간으로 21번째 예정 시점에는 20번째였고 다음 캡처에 21번째가 확인됐다. 엄밀한 5.000초 정확도, 프레임률, 애니메이션 매끄러움·인터럽트·장시간 중복 timer 부재는 보증하지 않는다. + +## Geometry와 검증 방법 + +[verification.json](verification.json)은 주변 QA bar의 SwiftUI global frame 차이와 PNG의 양끝 fixture 색상 scan을 교차 검증한다. Home A/B는 y280pt, Content A/B는 y136pt. Home 뒤 bar y429→491.5, Content 뒤 bar y285→347.5. Empty 뒤 bar는 각각 y280/y136으로 올라와 원래 섹션 간격만 남았다. Direct context는 `.background(GeometryReader)`로 실제 carousel geometry를 기록한다. production carousel에 modifier를 삽입하지 않았다. + +[source/assess.py](source/assess.py) 실행 exit0: product 해시 동일, 모든 native PNG signature/750×1334/소스보다 신선함, 두 화면 전체 폭/비율/s24/empty/320pt, single 고정, inactive 고정, 지연 image 높이 격리, 21개 입력, opener 순서 assert 모두 PASS. 원본 주요 PNG와 전체 contact sheet를 직접 열어 겹침·잘림·페이지 번호·fixture 순서를 확인했다. 독립 리뷰는 요청에 따라 호출하지 않았다. + +## 실제 명령과 진단 + +정확한 확장 인자·stdout/stderr/exit는 [commands.json](commands.json)에 있다. 전체 앱 `xcodebuild`는 실행하지 않았다. + +```bash +export TMPDIR=/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-native.tstn99 +export NODE_COMPILE_CACHE="$TMPDIR" +export BUN_RUNTIME_TRANSPILER_CACHE_PATH="$TMPDIR" +python3 docs/20261029_추천_서브_배너/reviews/evidence/native/source/run.py +python3 docs/20261029_추천_서브_배너/reviews/evidence/native/source/assess.py +python3 docs/20261029_추천_서브_배너/reviews/evidence/native/source/cleanup.py +``` + +핵심 compile: `xcrun --sdk iphonesimulator swiftc -swift-version 5 -target x86_64-apple-ios16.6-simulator -sdk -I -module-cache-path /module-cache -o /SubBannerQA.app/SubBannerQA`. Install/launch는 지정 부모 UDID와 독립 bundle `qa.sodalive.subbanner.native.tstn99`만 사용했다. + +초기 컴파일은 QA의 read-only Reduce Motion setter 사용으로 exit1, modifier 제거 후 동일 native 전략의 두 번째 컴파일 exit0. 실패 명령도 receipt에 보존했다. LSP 진단 도구는 제공되지 않아 실행하지 못했으며 실제 swiftc compile 진단으로 대체했다. QA는 현재 소스를 그대로 소비했고 production 수정은 하지 않았다. + +## 소유권과 정리 + +[ownership.json](ownership.json)에 앱 bundle, PID `93117`, own temporary root를 launch 직후 기록했다. [cleanup.json](cleanup.json)은 **own app 종료·uninstall, PID 부재, own root 삭제, 부모 device Booted 유지, product 해시 동일**을 확인한다. 공유 cache 삭제, simulator shutdown/delete/reset, worktree, commit/push, 외부 서비스 호출은 하지 않았다. + +남은 확인: physical tap/swipe, Reduce Motion ON, OS background/foreground, 실기기/iOS16.6/Dynamic Type/VoiceOver, 전체 SodaLive 앱 통합·실서버는 미검증이다. 이 결과는 native 부분 수용 증거이며 전체 제품 최종 승인이나 독립 리뷰 PASS를 대신하지 않는다. diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/actions.json b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/actions.json new file mode 100644 index 00000000..9af7a777 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/actions.json @@ -0,0 +1,3273 @@ +[ + { + "event" : "environment", + "phase" : "active", + "reduceMotion" : false, + "scenario" : "preparing", + "time" : 0.53669893404003233 + }, + { + "context" : "home", + "count" : 2, + "event" : "scenario-start", + "scenario" : "home-pair", + "time" : 1.4757472290657461 + }, + { + "event" : "environment", + "phase" : "active", + "reduceMotion" : false, + "scenario" : "home-pair", + "time" : 1.5451539750210941 + }, + { + "event" : "home-first", + "frames" : { + "QA active creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 208 + }, + "QA debut creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 429 + }, + "QA home live" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA home main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 136 + }, + "QA other home content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 501 + } + }, + "inputIDs" : [ + 1, + 2 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "home-first.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + } + ], + "scenario" : "home-pair", + "time" : 2.4030421610223129, + "width" : 375 + }, + { + "event" : "home-before-5s", + "frames" : { + "QA active creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 208 + }, + "QA debut creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 429 + }, + "QA home live" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA home main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 136 + }, + "QA other home content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 501 + } + }, + "inputIDs" : [ + 1, + 2 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "home-before-5s.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + } + ], + "scenario" : "home-pair", + "time" : 6.4040292220888659, + "width" : 375 + }, + { + "event" : "home-transition", + "frames" : { + "QA active creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 208 + }, + "QA debut creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 491.5 + }, + "QA home live" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA home main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 136 + }, + "QA other home content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 563.5 + } + }, + "inputIDs" : [ + 1, + 2 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "home-transition.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + } + ], + "scenario" : "home-pair", + "time" : 7.2826485420810059, + "width" : 375 + }, + { + "event" : "home-second", + "frames" : { + "QA active creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 208 + }, + "QA debut creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 491.5 + }, + "QA home live" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA home main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 136 + }, + "QA other home content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 563.5 + } + }, + "inputIDs" : [ + 1, + 2 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "home-second.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + } + ], + "scenario" : "home-pair", + "time" : 8.0287099099950865, + "width" : 375 + }, + { + "event" : "home-wrap", + "frames" : { + "QA active creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 208 + }, + "QA debut creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 429 + }, + "QA home live" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA home main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 136 + }, + "QA other home content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 501 + } + }, + "inputIDs" : [ + 1, + 2 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "home-wrap.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + } + ], + "scenario" : "home-pair", + "time" : 12.998640250065364, + "width" : 375 + }, + { + "event" : "home-light", + "frames" : { + "QA active creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 208 + }, + "QA debut creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 429 + }, + "QA home live" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA home main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 136 + }, + "QA other home content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 501 + } + }, + "inputIDs" : [ + 1, + 2 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "home-light.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + } + ], + "scenario" : "home-pair", + "time" : 13.406766513013281, + "width" : 375 + }, + { + "event" : "home-empty", + "frames" : { + "QA active creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 208 + }, + "QA debut creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 280 + }, + "QA home live" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA home main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 136 + }, + "QA other home content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 352 + } + }, + "inputIDs" : [ + + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "home-empty.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + } + ], + "scenario" : "home-pair", + "time" : 13.910796258016489, + "width" : 375 + }, + { + "event" : "home-one", + "frames" : { + "QA active creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 208 + }, + "QA debut creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 429 + }, + "QA home live" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA home main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 136 + }, + "QA other home content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 501 + } + }, + "inputIDs" : [ + 3 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "home-one.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + } + ], + "scenario" : "home-pair", + "time" : 14.684069303097203, + "width" : 375 + }, + { + "event" : "home-one-after-6s", + "frames" : { + "QA active creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 208 + }, + "QA debut creators" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 429 + }, + "QA home live" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA home main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 136 + }, + "QA other home content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 501 + } + }, + "inputIDs" : [ + 3 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "home-one-after-6s.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + } + ], + "scenario" : "home-pair", + "time" : 20.766429161070846, + "width" : 375 + }, + { + "event" : "home-resize-320", + "frames" : { + "QA active creators" : { + "height" : 48, + "width" : 320, + "x" : 0, + "y" : 208 + }, + "QA debut creators" : { + "height" : 48, + "width" : 320, + "x" : 0, + "y" : 410.66666666666669 + }, + "QA home live" : { + "height" : 48, + "width" : 320, + "x" : 0, + "y" : 64 + }, + "QA home main banner" : { + "height" : 48, + "width" : 320, + "x" : 0, + "y" : 136 + }, + "QA other home content" : { + "height" : 48.000000000000057, + "width" : 320, + "x" : 0, + "y" : 482.66666666666669 + } + }, + "inputIDs" : [ + 3 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "home-resize-320.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + } + ], + "scenario" : "home-pair", + "time" : 21.383744181017391, + "width" : 320 + }, + { + "context" : "content", + "count" : 2, + "event" : "scenario-start", + "scenario" : "content-pair", + "time" : 21.863158976077102 + }, + { + "event" : "environment", + "phase" : "active", + "reduceMotion" : false, + "scenario" : "content-pair", + "time" : 21.900616487022489 + }, + { + "event" : "content-first", + "frames" : { + "QA content main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA exclusive series" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 285 + }, + "QA other content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 357 + } + }, + "inputIDs" : [ + 1, + 2 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "content-first.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 906226.14831963403, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 906226.17059628211, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + } + ], + "scenario" : "content-pair", + "time" : 22.798997105099261, + "width" : 375 + }, + { + "event" : "content-before-5s", + "frames" : { + "QA content main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA exclusive series" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 285 + }, + "QA other content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 357 + } + }, + "inputIDs" : [ + 1, + 2 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "content-before-5s.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 906226.14831963403, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 906226.17059628211, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + } + ], + "scenario" : "content-pair", + "time" : 26.495558171998709, + "width" : 375 + }, + { + "event" : "content-transition", + "frames" : { + "QA content main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA exclusive series" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 347.5 + }, + "QA other content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 419.5 + } + }, + "inputIDs" : [ + 1, + 2 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "content-transition.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 906226.14831963403, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 906226.17059628211, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + } + ], + "scenario" : "content-pair", + "time" : 27.388914745068178, + "width" : 375 + }, + { + "event" : "content-second", + "frames" : { + "QA content main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA exclusive series" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 347.5 + }, + "QA other content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 419.5 + } + }, + "inputIDs" : [ + 1, + 2 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "content-second.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 906226.14831963403, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 906226.17059628211, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + } + ], + "scenario" : "content-pair", + "time" : 28.169360205065459, + "width" : 375 + }, + { + "event" : "content-wrap", + "frames" : { + "QA content main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA exclusive series" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 285 + }, + "QA other content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 357 + } + }, + "inputIDs" : [ + 1, + 2 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "content-wrap.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 906226.14831963403, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 906226.17059628211, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + } + ], + "scenario" : "content-pair", + "time" : 33.38149883504957, + "width" : 375 + }, + { + "event" : "content-light", + "frames" : { + "QA content main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA exclusive series" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 285 + }, + "QA other content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 357 + } + }, + "inputIDs" : [ + 1, + 2 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "content-light.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 906226.14831963403, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 906226.17059628211, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + } + ], + "scenario" : "content-pair", + "time" : 33.802260532043874, + "width" : 375 + }, + { + "event" : "content-empty", + "frames" : { + "QA content main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA exclusive series" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 136 + }, + "QA other content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 208 + } + }, + "inputIDs" : [ + + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "content-empty.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 906226.14831963403, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 906226.17059628211, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + } + ], + "scenario" : "content-pair", + "time" : 34.319609354017302, + "width" : 375 + }, + { + "event" : "content-one", + "frames" : { + "QA content main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA exclusive series" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 285 + }, + "QA other content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 357 + } + }, + "inputIDs" : [ + 3 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "content-one.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 906226.14831963403, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 906226.17059628211, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 906238.57465889107, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + } + ], + "scenario" : "content-pair", + "time" : 35.053385364008136, + "width" : 375 + }, + { + "event" : "content-one-after-6s", + "frames" : { + "QA content main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA exclusive series" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 285 + }, + "QA other content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 357 + } + }, + "inputIDs" : [ + 3 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "content-one-after-6s.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 906226.14831963403, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 906226.17059628211, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 906238.57465889107, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + } + ], + "scenario" : "content-pair", + "time" : 41.164184253080748, + "width" : 375 + }, + { + "event" : "content-resize-320", + "frames" : { + "QA content main banner" : { + "height" : 48, + "width" : 320, + "x" : 0, + "y" : 64 + }, + "QA exclusive series" : { + "height" : 48, + "width" : 320, + "x" : 0, + "y" : 266.66666666666669 + }, + "QA other content" : { + "height" : 48, + "width" : 320, + "x" : 0, + "y" : 338.66666666666669 + } + }, + "inputIDs" : [ + 3 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "content-resize-320.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 906226.14831963403, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 906226.17059628211, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 906238.57465889107, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + } + ], + "scenario" : "content-pair", + "time" : 41.811548391007818, + "width" : 320 + }, + { + "context" : "direct", + "count" : 2, + "event" : "scenario-start", + "scenario" : "delayed-current", + "time" : 42.29815262905322 + }, + { + "event" : "environment", + "phase" : "active", + "reduceMotion" : false, + "scenario" : "delayed-current", + "time" : 42.335316151031293 + }, + { + "event" : "delayed-no-invented-height", + "frames" : { + "carousel" : { + "height" : 0, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 140 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 10, + 11 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "delayed-no-invented-height.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 906226.14831963403, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 906226.17059628211, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 906238.57465889107, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 906246.58286218403, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 906246.5881454621, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + } + ], + "scenario" : "delayed-current", + "time" : 43.029872058075853, + "width" : 375 + }, + { + "event" : "delayed-next-valid", + "frames" : { + "carousel" : { + "height" : 187.5, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 327.5 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 10, + 11 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "delayed-next-valid.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 906226.14831963403, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 906226.17059628211, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 906238.57465889107, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 906246.58286218403, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 906246.5881454621, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + } + ], + "scenario" : "delayed-current", + "time" : 48.561556611093692, + "width" : 375 + }, + { + "event" : "delayed-old-image-arrived", + "frames" : { + "carousel" : { + "height" : 187.5, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 327.5 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 10, + 11 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "delayed-old-image-arrived.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 906226.14831963403, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 906226.17059628211, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 906238.57465889107, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 906246.58286218403, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 906246.5881454621, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + } + ], + "scenario" : "delayed-current", + "time" : 51.547296654083766, + "width" : 375 + }, + { + "context" : "content", + "count" : 2, + "event" : "scenario-start", + "scenario" : "failure-isolation", + "time" : 51.714743894059211 + }, + { + "event" : "environment", + "phase" : "active", + "reduceMotion" : false, + "scenario" : "failure-isolation", + "time" : 51.743613868020475 + }, + { + "event" : "failure-valid-first", + "frames" : { + "QA content main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA exclusive series" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 285 + }, + "QA other content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 357 + } + }, + "inputIDs" : [ + 20, + 21 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "failure-valid-first.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 906226.14831963403, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 906226.17059628211, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 906238.57465889107, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 906246.58286218403, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 906246.5881454621, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 906255.99671375111, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 906256.02351316204, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + } + ], + "scenario" : "failure-isolation", + "time" : 52.671063362038694, + "width" : 375 + }, + { + "event" : "failure-placeholder-other-content", + "frames" : { + "QA content main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA exclusive series" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 285 + }, + "QA other content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 357 + } + }, + "inputIDs" : [ + 20, + 21 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "failure-placeholder-other-content.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 906226.14831963403, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 906226.17059628211, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 906238.57465889107, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 906246.58286218403, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 906246.5881454621, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 906255.99671375111, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 906256.02351316204, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + } + ], + "scenario" : "failure-isolation", + "time" : 57.798333963030018, + "width" : 375 + }, + { + "event" : "failure-wrap-valid", + "frames" : { + "QA content main banner" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 64 + }, + "QA exclusive series" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 285 + }, + "QA other content" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 357 + } + }, + "inputIDs" : [ + 20, + 21 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "failure-wrap-valid.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 906226.14831963403, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 906226.17059628211, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 906238.57465889107, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 906246.58286218403, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 906246.5881454621, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 906255.99671375111, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 906256.02351316204, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + } + ], + "scenario" : "failure-isolation", + "time" : 63.02467517205514, + "width" : 375 + }, + { + "context" : "direct", + "count" : 2, + "event" : "scenario-start", + "scenario" : "inactive-remount", + "time" : 63.192597879096866 + }, + { + "event" : "environment", + "phase" : "active", + "reduceMotion" : false, + "scenario" : "inactive-remount", + "time" : 63.21831036999356 + }, + { + "event" : "inactive-before", + "frames" : { + "carousel" : { + "height" : 187.5, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 327.5 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 30, + 31 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "inactive-before.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 906226.14831963403, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 906226.17059628211, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 906238.57465889107, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 906246.58286218403, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 906246.5881454621, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 906255.99671375111, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 906256.02351316204, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + }, + { + "uptime" : 906267.46704513102, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-wide.png" + }, + { + "uptime" : 906267.49546460807, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-tall.png" + } + ], + "scenario" : "inactive-remount", + "time" : 69.086252320092171, + "width" : 375 + }, + { + "event" : "inject-inactive", + "scenario" : "inactive-remount", + "time" : 69.094432333018631 + }, + { + "event" : "phase", + "scenario" : "inactive-remount", + "time" : 69.110479110036977, + "value" : "inactive" + }, + { + "event" : "inactive-start", + "frames" : { + "carousel" : { + "height" : 187.5, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 327.5 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 30, + 31 + ], + "osReduceMotion" : false, + "phase" : "inactive", + "png" : "inactive-start.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 906226.14831963403, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 906226.17059628211, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 906238.57465889107, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 906246.58286218403, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 906246.5881454621, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 906255.99671375111, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 906256.02351316204, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + }, + { + "uptime" : 906267.46704513102, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-wide.png" + }, + { + "uptime" : 906267.49546460807, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-tall.png" + } + ], + "scenario" : "inactive-remount", + "time" : 69.577743933070451, + "width" : 375 + }, + { + "event" : "inactive-after-6s", + "frames" : { + "carousel" : { + "height" : 187.5, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 327.5 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 30, + 31 + ], + "osReduceMotion" : false, + "phase" : "inactive", + "png" : "inactive-after-6s.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 906226.14831963403, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 906226.17059628211, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 906238.57465889107, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 906246.58286218403, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 906246.5881454621, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 906255.99671375111, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 906256.02351316204, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + }, + { + "uptime" : 906267.46704513102, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-wide.png" + }, + { + "uptime" : 906267.49546460807, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-tall.png" + } + ], + "scenario" : "inactive-remount", + "time" : 75.672131193103269, + "width" : 375 + }, + { + "event" : "inject-active", + "scenario" : "inactive-remount", + "time" : 75.681613474036567 + }, + { + "event" : "phase", + "scenario" : "inactive-remount", + "time" : 75.69928526703734, + "value" : "active" + }, + { + "event" : "active-before-5s", + "frames" : { + "carousel" : { + "height" : 187.5, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 327.5 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 30, + 31 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "active-before-5s.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 906226.14831963403, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 906226.17059628211, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 906238.57465889107, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 906246.58286218403, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 906246.5881454621, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 906255.99671375111, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 906256.02351316204, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + }, + { + "uptime" : 906267.46704513102, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-wide.png" + }, + { + "uptime" : 906267.49546460807, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-tall.png" + } + ], + "scenario" : "inactive-remount", + "time" : 80.471144664101303, + "width" : 375 + }, + { + "event" : "active-next", + "frames" : { + "carousel" : { + "height" : 125, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 265 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 30, + 31 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "active-next.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 906226.14831963403, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 906226.17059628211, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 906238.57465889107, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 906246.58286218403, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 906246.5881454621, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 906255.99671375111, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 906256.02351316204, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + }, + { + "uptime" : 906267.46704513102, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-wide.png" + }, + { + "uptime" : 906267.49546460807, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-tall.png" + } + ], + "scenario" : "inactive-remount", + "time" : 81.958039054065011, + "width" : 375 + }, + { + "event" : "unmount", + "scenario" : "inactive-remount", + "time" : 81.967841719044372 + }, + { + "event" : "unmounted-after-6s", + "frames" : { + + }, + "inputIDs" : [ + 30, + 31 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "unmounted-after-6s.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 906226.14831963403, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 906226.17059628211, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 906238.57465889107, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 906246.58286218403, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 906246.5881454621, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 906255.99671375111, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 906256.02351316204, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + }, + { + "uptime" : 906267.46704513102, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-wide.png" + }, + { + "uptime" : 906267.49546460807, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-tall.png" + } + ], + "scenario" : "inactive-remount", + "time" : 88.460920925019309, + "width" : 375 + }, + { + "event" : "remount", + "scenario" : "inactive-remount", + "time" : 88.471321807010099 + }, + { + "event" : "environment", + "phase" : "active", + "reduceMotion" : false, + "scenario" : "inactive-remount", + "time" : 88.501183653017506 + }, + { + "event" : "remount-first", + "frames" : { + "carousel" : { + "height" : 125, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 265 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 30, + 31 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "remount-first.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 906226.14831963403, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 906226.17059628211, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 906238.57465889107, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 906246.58286218403, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 906246.5881454621, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 906255.99671375111, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 906256.02351316204, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + }, + { + "uptime" : 906267.46704513102, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-wide.png" + }, + { + "uptime" : 906267.49546460807, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-tall.png" + } + ], + "scenario" : "inactive-remount", + "time" : 89.214847571100108, + "width" : 375 + }, + { + "event" : "remount-next", + "frames" : { + "carousel" : { + "height" : 187.5, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 327.5 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 30, + 31 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "remount-next.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 906226.14831963403, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 906226.17059628211, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 906238.57465889107, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 906246.58286218403, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 906246.5881454621, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 906255.99671375111, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 906256.02351316204, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + }, + { + "uptime" : 906267.46704513102, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-wide.png" + }, + { + "uptime" : 906267.49546460807, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-tall.png" + } + ], + "scenario" : "inactive-remount", + "time" : 94.418050298001617, + "width" : 375 + }, + { + "event" : "shrink-one", + "frames" : { + "carousel" : { + "height" : 125, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 265 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 32 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "shrink-one.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 906226.14831963403, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 906226.17059628211, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 906238.57465889107, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 906246.58286218403, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 906246.5881454621, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 906255.99671375111, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 906256.02351316204, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + }, + { + "uptime" : 906267.46704513102, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-wide.png" + }, + { + "uptime" : 906267.49546460807, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-tall.png" + }, + { + "uptime" : 906298.68452673208, + "url" : "https:\/\/subbanner.qa.invalid\/shrink-wide.png" + } + ], + "scenario" : "inactive-remount", + "time" : 95.153885852079839, + "width" : 375 + }, + { + "context" : "direct", + "count" : 2, + "event" : "scenario-start", + "scenario" : "reduced-motion", + "time" : 95.322306538000703 + }, + { + "enabledTest" : "NOT RUN: read-only SwiftUI environment; parent device preferences unchanged", + "event" : "reduce-motion-observation-only", + "osValue" : false, + "scenario" : "reduced-motion", + "time" : 95.333420080016367 + }, + { + "event" : "environment", + "phase" : "active", + "reduceMotion" : false, + "scenario" : "reduced-motion", + "time" : 95.360725267091766 + }, + { + "event" : "reduce-first", + "frames" : { + "carousel" : { + "height" : 125, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 265 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 40, + 41 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "reduce-first.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 906226.14831963403, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 906226.17059628211, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 906238.57465889107, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 906246.58286218403, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 906246.5881454621, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 906255.99671375111, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 906256.02351316204, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + }, + { + "uptime" : 906267.46704513102, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-wide.png" + }, + { + "uptime" : 906267.49546460807, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-tall.png" + }, + { + "uptime" : 906298.68452673208, + "url" : "https:\/\/subbanner.qa.invalid\/shrink-wide.png" + }, + { + "uptime" : 906299.61043819704, + "url" : "https:\/\/subbanner.qa.invalid\/reduce-wide.png" + }, + { + "uptime" : 906299.63532692008, + "url" : "https:\/\/subbanner.qa.invalid\/reduce-tall.png" + } + ], + "scenario" : "reduced-motion", + "time" : 96.175312772043981, + "width" : 375 + }, + { + "event" : "reduce-transition", + "frames" : { + "carousel" : { + "height" : 125, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 265 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 40, + 41 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "reduce-transition.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 906226.14831963403, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 906226.17059628211, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 906238.57465889107, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 906246.58286218403, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 906246.5881454621, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 906255.99671375111, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 906256.02351316204, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + }, + { + "uptime" : 906267.46704513102, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-wide.png" + }, + { + "uptime" : 906267.49546460807, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-tall.png" + }, + { + "uptime" : 906298.68452673208, + "url" : "https:\/\/subbanner.qa.invalid\/shrink-wide.png" + }, + { + "uptime" : 906299.61043819704, + "url" : "https:\/\/subbanner.qa.invalid\/reduce-wide.png" + }, + { + "uptime" : 906299.63532692008, + "url" : "https:\/\/subbanner.qa.invalid\/reduce-tall.png" + } + ], + "scenario" : "reduced-motion", + "time" : 100.79765504901297, + "width" : 375 + }, + { + "event" : "reduce-second", + "frames" : { + "carousel" : { + "height" : 187.5, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 327.5 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 40, + 41 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "reduce-second.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 906226.14831963403, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 906226.17059628211, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 906238.57465889107, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 906246.58286218403, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 906246.5881454621, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 906255.99671375111, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 906256.02351316204, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + }, + { + "uptime" : 906267.46704513102, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-wide.png" + }, + { + "uptime" : 906267.49546460807, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-tall.png" + }, + { + "uptime" : 906298.68452673208, + "url" : "https:\/\/subbanner.qa.invalid\/shrink-wide.png" + }, + { + "uptime" : 906299.61043819704, + "url" : "https:\/\/subbanner.qa.invalid\/reduce-wide.png" + }, + { + "uptime" : 906299.63532692008, + "url" : "https:\/\/subbanner.qa.invalid\/reduce-tall.png" + } + ], + "scenario" : "reduced-motion", + "time" : 101.63738802203443, + "width" : 375 + }, + { + "context" : "direct", + "count" : 21, + "event" : "scenario-start", + "scenario" : "twenty-one", + "time" : 101.8081480790861 + }, + { + "event" : "environment", + "phase" : "active", + "reduceMotion" : false, + "scenario" : "twenty-one", + "time" : 101.92569241707679 + }, + { + "event" : "many-first", + "frames" : { + "carousel" : { + "height" : 125, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 265 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "many-first.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 906226.14831963403, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 906226.17059628211, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 906238.57465889107, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 906246.58286218403, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 906246.5881454621, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 906255.99671375111, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 906256.02351316204, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + }, + { + "uptime" : 906267.46704513102, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-wide.png" + }, + { + "uptime" : 906267.49546460807, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-tall.png" + }, + { + "uptime" : 906298.68452673208, + "url" : "https:\/\/subbanner.qa.invalid\/shrink-wide.png" + }, + { + "uptime" : 906299.61043819704, + "url" : "https:\/\/subbanner.qa.invalid\/reduce-wide.png" + }, + { + "uptime" : 906299.63532692008, + "url" : "https:\/\/subbanner.qa.invalid\/reduce-tall.png" + }, + { + "uptime" : 906306.23014644301, + "url" : "https:\/\/subbanner.qa.invalid\/many-1-wide.png" + }, + { + "uptime" : 906306.23017047008, + "url" : "https:\/\/subbanner.qa.invalid\/many-2-wide.png" + }, + { + "uptime" : 906306.25993568311, + "url" : "https:\/\/subbanner.qa.invalid\/many-3-wide.png" + }, + { + "uptime" : 906306.36898299202, + "url" : "https:\/\/subbanner.qa.invalid\/many-4-wide.png" + }, + { + "uptime" : 906306.4197958851, + "url" : "https:\/\/subbanner.qa.invalid\/many-5-wide.png" + }, + { + "uptime" : 906306.53865028406, + "url" : "https:\/\/subbanner.qa.invalid\/many-6-wide.png" + }, + { + "uptime" : 906306.59977684706, + "url" : "https:\/\/subbanner.qa.invalid\/many-7-wide.png" + }, + { + "uptime" : 906306.72728180804, + "url" : "https:\/\/subbanner.qa.invalid\/many-8-wide.png" + }, + { + "uptime" : 906306.80431990302, + "url" : "https:\/\/subbanner.qa.invalid\/many-9-wide.png" + } + ], + "scenario" : "twenty-one", + "time" : 102.76253761607222, + "width" : 375 + }, + { + "event" : "many-twenty-first", + "frames" : { + "carousel" : { + "height" : 125, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 265 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "many-twenty-first.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 906226.14831963403, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 906226.17059628211, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 906238.57465889107, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 906246.58286218403, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 906246.5881454621, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 906255.99671375111, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 906256.02351316204, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + }, + { + "uptime" : 906267.46704513102, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-wide.png" + }, + { + "uptime" : 906267.49546460807, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-tall.png" + }, + { + "uptime" : 906298.68452673208, + "url" : "https:\/\/subbanner.qa.invalid\/shrink-wide.png" + }, + { + "uptime" : 906299.61043819704, + "url" : "https:\/\/subbanner.qa.invalid\/reduce-wide.png" + }, + { + "uptime" : 906299.63532692008, + "url" : "https:\/\/subbanner.qa.invalid\/reduce-tall.png" + }, + { + "uptime" : 906306.23014644301, + "url" : "https:\/\/subbanner.qa.invalid\/many-1-wide.png" + }, + { + "uptime" : 906306.23017047008, + "url" : "https:\/\/subbanner.qa.invalid\/many-2-wide.png" + }, + { + "uptime" : 906306.25993568311, + "url" : "https:\/\/subbanner.qa.invalid\/many-3-wide.png" + }, + { + "uptime" : 906306.36898299202, + "url" : "https:\/\/subbanner.qa.invalid\/many-4-wide.png" + }, + { + "uptime" : 906306.4197958851, + "url" : "https:\/\/subbanner.qa.invalid\/many-5-wide.png" + }, + { + "uptime" : 906306.53865028406, + "url" : "https:\/\/subbanner.qa.invalid\/many-6-wide.png" + }, + { + "uptime" : 906306.59977684706, + "url" : "https:\/\/subbanner.qa.invalid\/many-7-wide.png" + }, + { + "uptime" : 906306.72728180804, + "url" : "https:\/\/subbanner.qa.invalid\/many-8-wide.png" + }, + { + "uptime" : 906306.80431990302, + "url" : "https:\/\/subbanner.qa.invalid\/many-9-wide.png" + }, + { + "uptime" : 906307.01509298, + "url" : "https:\/\/subbanner.qa.invalid\/many-10-wide.png" + }, + { + "uptime" : 906307.015589074, + "url" : "https:\/\/subbanner.qa.invalid\/many-11-wide.png" + }, + { + "uptime" : 906307.09204433602, + "url" : "https:\/\/subbanner.qa.invalid\/many-12-wide.png" + }, + { + "uptime" : 906307.194996053, + "url" : "https:\/\/subbanner.qa.invalid\/many-13-wide.png" + }, + { + "uptime" : 906307.26231897902, + "url" : "https:\/\/subbanner.qa.invalid\/many-14-wide.png" + }, + { + "uptime" : 906307.33891814109, + "url" : "https:\/\/subbanner.qa.invalid\/many-15-wide.png" + }, + { + "uptime" : 906307.44595937501, + "url" : "https:\/\/subbanner.qa.invalid\/many-16-wide.png" + }, + { + "uptime" : 906307.61257411505, + "url" : "https:\/\/subbanner.qa.invalid\/many-17-wide.png" + }, + { + "uptime" : 906307.71213262202, + "url" : "https:\/\/subbanner.qa.invalid\/many-18-wide.png" + }, + { + "uptime" : 906307.84525284602, + "url" : "https:\/\/subbanner.qa.invalid\/many-19-wide.png" + }, + { + "uptime" : 906307.97756142903, + "url" : "https:\/\/subbanner.qa.invalid\/many-20-wide.png" + }, + { + "uptime" : 906308.09739075904, + "url" : "https:\/\/subbanner.qa.invalid\/many-21-wide.png" + } + ], + "scenario" : "twenty-one", + "time" : 202.68109613808338, + "width" : 375 + }, + { + "event" : "many-wrap", + "frames" : { + "carousel" : { + "height" : 125, + "width" : 375, + "x" : 0, + "y" : 116 + }, + "QA direct after" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 265 + }, + "QA direct before" : { + "height" : 48, + "width" : 375, + "x" : 0, + "y" : 44 + } + }, + "inputIDs" : [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121 + ], + "osReduceMotion" : false, + "phase" : "active", + "png" : "many-wrap.png", + "requests" : [ + { + "uptime" : 906205.80151793209, + "url" : "https:\/\/subbanner.qa.invalid\/home-wide.png" + }, + { + "uptime" : 906205.83572366706, + "url" : "https:\/\/subbanner.qa.invalid\/home-tall.png" + }, + { + "uptime" : 906218.162067945, + "url" : "https:\/\/subbanner.qa.invalid\/home-single-wide.png" + }, + { + "uptime" : 906226.14831963403, + "url" : "https:\/\/subbanner.qa.invalid\/content-wide.png" + }, + { + "uptime" : 906226.17059628211, + "url" : "https:\/\/subbanner.qa.invalid\/content-tall.png" + }, + { + "uptime" : 906238.57465889107, + "url" : "https:\/\/subbanner.qa.invalid\/content-single-wide.png" + }, + { + "uptime" : 906246.58286218403, + "url" : "https:\/\/subbanner.qa.invalid\/delay-wide.png" + }, + { + "uptime" : 906246.5881454621, + "url" : "https:\/\/subbanner.qa.invalid\/valid-tall.png" + }, + { + "uptime" : 906255.99671375111, + "url" : "https:\/\/subbanner.qa.invalid\/valid-wide.png" + }, + { + "uptime" : 906256.02351316204, + "url" : "https:\/\/subbanner.qa.invalid\/fail.png" + }, + { + "uptime" : 906267.46704513102, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-wide.png" + }, + { + "uptime" : 906267.49546460807, + "url" : "https:\/\/subbanner.qa.invalid\/lifecycle-tall.png" + }, + { + "uptime" : 906298.68452673208, + "url" : "https:\/\/subbanner.qa.invalid\/shrink-wide.png" + }, + { + "uptime" : 906299.61043819704, + "url" : "https:\/\/subbanner.qa.invalid\/reduce-wide.png" + }, + { + "uptime" : 906299.63532692008, + "url" : "https:\/\/subbanner.qa.invalid\/reduce-tall.png" + }, + { + "uptime" : 906306.23014644301, + "url" : "https:\/\/subbanner.qa.invalid\/many-1-wide.png" + }, + { + "uptime" : 906306.23017047008, + "url" : "https:\/\/subbanner.qa.invalid\/many-2-wide.png" + }, + { + "uptime" : 906306.25993568311, + "url" : "https:\/\/subbanner.qa.invalid\/many-3-wide.png" + }, + { + "uptime" : 906306.36898299202, + "url" : "https:\/\/subbanner.qa.invalid\/many-4-wide.png" + }, + { + "uptime" : 906306.4197958851, + "url" : "https:\/\/subbanner.qa.invalid\/many-5-wide.png" + }, + { + "uptime" : 906306.53865028406, + "url" : "https:\/\/subbanner.qa.invalid\/many-6-wide.png" + }, + { + "uptime" : 906306.59977684706, + "url" : "https:\/\/subbanner.qa.invalid\/many-7-wide.png" + }, + { + "uptime" : 906306.72728180804, + "url" : "https:\/\/subbanner.qa.invalid\/many-8-wide.png" + }, + { + "uptime" : 906306.80431990302, + "url" : "https:\/\/subbanner.qa.invalid\/many-9-wide.png" + }, + { + "uptime" : 906307.01509298, + "url" : "https:\/\/subbanner.qa.invalid\/many-10-wide.png" + }, + { + "uptime" : 906307.015589074, + "url" : "https:\/\/subbanner.qa.invalid\/many-11-wide.png" + }, + { + "uptime" : 906307.09204433602, + "url" : "https:\/\/subbanner.qa.invalid\/many-12-wide.png" + }, + { + "uptime" : 906307.194996053, + "url" : "https:\/\/subbanner.qa.invalid\/many-13-wide.png" + }, + { + "uptime" : 906307.26231897902, + "url" : "https:\/\/subbanner.qa.invalid\/many-14-wide.png" + }, + { + "uptime" : 906307.33891814109, + "url" : "https:\/\/subbanner.qa.invalid\/many-15-wide.png" + }, + { + "uptime" : 906307.44595937501, + "url" : "https:\/\/subbanner.qa.invalid\/many-16-wide.png" + }, + { + "uptime" : 906307.61257411505, + "url" : "https:\/\/subbanner.qa.invalid\/many-17-wide.png" + }, + { + "uptime" : 906307.71213262202, + "url" : "https:\/\/subbanner.qa.invalid\/many-18-wide.png" + }, + { + "uptime" : 906307.84525284602, + "url" : "https:\/\/subbanner.qa.invalid\/many-19-wide.png" + }, + { + "uptime" : 906307.97756142903, + "url" : "https:\/\/subbanner.qa.invalid\/many-20-wide.png" + }, + { + "uptime" : 906308.09739075904, + "url" : "https:\/\/subbanner.qa.invalid\/many-21-wide.png" + } + ], + "scenario" : "twenty-one", + "time" : 208.12360101100057, + "width" : 375 + }, + { + "attempts" : [ + "sodaqa:\/\/banner\/900", + "https:\/\/subbanner.qa.invalid\/destination" + ], + "event" : "opener-driver-not-physical-tap", + "scenario" : "twenty-one", + "time" : 208.14119667198975 + }, + { + "event" : "finished", + "scenario" : "twenty-one", + "time" : 208.16181460709777 + } +] \ No newline at end of file diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/active-before-5s.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/active-before-5s.png new file mode 100644 index 00000000..cf168108 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/active-before-5s.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/active-next.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/active-next.png new file mode 100644 index 00000000..96c0c986 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/active-next.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/content-before-5s.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/content-before-5s.png new file mode 100644 index 00000000..dd8b8726 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/content-before-5s.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/content-empty.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/content-empty.png new file mode 100644 index 00000000..d5059328 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/content-empty.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/content-first.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/content-first.png new file mode 100644 index 00000000..dd8b8726 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/content-first.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/content-light.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/content-light.png new file mode 100644 index 00000000..dd8b8726 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/content-light.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/content-one-after-6s.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/content-one-after-6s.png new file mode 100644 index 00000000..c33788ac Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/content-one-after-6s.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/content-one.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/content-one.png new file mode 100644 index 00000000..c33788ac Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/content-one.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/content-resize-320.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/content-resize-320.png new file mode 100644 index 00000000..678c3402 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/content-resize-320.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/content-second.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/content-second.png new file mode 100644 index 00000000..688c60f3 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/content-second.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/content-transition.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/content-transition.png new file mode 100644 index 00000000..5821805c Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/content-transition.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/content-wrap.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/content-wrap.png new file mode 100644 index 00000000..dd8b8726 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/content-wrap.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/delayed-next-valid.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/delayed-next-valid.png new file mode 100644 index 00000000..62de0ec7 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/delayed-next-valid.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/delayed-no-invented-height.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/delayed-no-invented-height.png new file mode 100644 index 00000000..a710199b Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/delayed-no-invented-height.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/delayed-old-image-arrived.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/delayed-old-image-arrived.png new file mode 100644 index 00000000..62de0ec7 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/delayed-old-image-arrived.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/failure-placeholder-other-content.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/failure-placeholder-other-content.png new file mode 100644 index 00000000..34a17e61 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/failure-placeholder-other-content.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/failure-valid-first.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/failure-valid-first.png new file mode 100644 index 00000000..2c6fd408 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/failure-valid-first.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/failure-wrap-valid.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/failure-wrap-valid.png new file mode 100644 index 00000000..2c6fd408 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/failure-wrap-valid.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/home-before-5s.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/home-before-5s.png new file mode 100644 index 00000000..6f972ff3 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/home-before-5s.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/home-empty.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/home-empty.png new file mode 100644 index 00000000..b10e190d Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/home-empty.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/home-first.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/home-first.png new file mode 100644 index 00000000..6f972ff3 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/home-first.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/home-light.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/home-light.png new file mode 100644 index 00000000..6f972ff3 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/home-light.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/home-one-after-6s.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/home-one-after-6s.png new file mode 100644 index 00000000..885209f1 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/home-one-after-6s.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/home-one.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/home-one.png new file mode 100644 index 00000000..885209f1 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/home-one.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/home-resize-320.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/home-resize-320.png new file mode 100644 index 00000000..23868198 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/home-resize-320.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/home-second.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/home-second.png new file mode 100644 index 00000000..f96f82b8 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/home-second.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/home-transition.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/home-transition.png new file mode 100644 index 00000000..f3c8545d Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/home-transition.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/home-wrap.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/home-wrap.png new file mode 100644 index 00000000..6f972ff3 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/home-wrap.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/inactive-after-6s.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/inactive-after-6s.png new file mode 100644 index 00000000..cf168108 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/inactive-after-6s.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/inactive-before.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/inactive-before.png new file mode 100644 index 00000000..cf168108 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/inactive-before.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/inactive-start.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/inactive-start.png new file mode 100644 index 00000000..cf168108 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/inactive-start.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/many-first.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/many-first.png new file mode 100644 index 00000000..2be3d4d5 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/many-first.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/many-twenty-first.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/many-twenty-first.png new file mode 100644 index 00000000..fc8745ee Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/many-twenty-first.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/many-wrap.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/many-wrap.png new file mode 100644 index 00000000..8fb62b1e Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/many-wrap.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/reduce-first.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/reduce-first.png new file mode 100644 index 00000000..635a7d03 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/reduce-first.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/reduce-second.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/reduce-second.png new file mode 100644 index 00000000..bbe0cee2 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/reduce-second.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/reduce-transition.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/reduce-transition.png new file mode 100644 index 00000000..635a7d03 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/reduce-transition.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/remount-first.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/remount-first.png new file mode 100644 index 00000000..96c0c986 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/remount-first.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/remount-next.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/remount-next.png new file mode 100644 index 00000000..cf168108 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/remount-next.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/shrink-one.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/shrink-one.png new file mode 100644 index 00000000..325bbad4 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/shrink-one.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/unmounted-after-6s.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/unmounted-after-6s.png new file mode 100644 index 00000000..576d5ee5 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/captures/unmounted-after-6s.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/cleanup.json b/docs/20261029_추천_서브_배너/reviews/evidence/native/cleanup.json new file mode 100644 index 00000000..9451e62e --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native/cleanup.json @@ -0,0 +1,72 @@ +{ + "commands": [ + { + "args": [ + "xcrun", + "simctl", + "terminate", + "482F7827-2E3A-4559-BAF4-48B68CC5725B", + "qa.sodalive.subbanner.native.tstn99" + ], + "exit": 0, + "stdout": "", + "stderr": "" + }, + { + "args": [ + "xcrun", + "simctl", + "uninstall", + "482F7827-2E3A-4559-BAF4-48B68CC5725B", + "qa.sodalive.subbanner.native.tstn99" + ], + "exit": 0, + "stdout": "", + "stderr": "" + }, + { + "args": [ + "xcrun", + "simctl", + "get_app_container", + "482F7827-2E3A-4559-BAF4-48B68CC5725B", + "qa.sodalive.subbanner.native.tstn99", + "data" + ], + "exit": 2, + "stdout": "", + "stderr": "An error was encountered processing the command (domain=NSPOSIXErrorDomain, code=2):\nThe operation couldn\u2019t be completed. No such file or directory\nNo such file or directory\n" + }, + { + "args": [ + "ps", + "-p", + "93117", + "-o", + "pid=,comm=" + ], + "exit": 1, + "stdout": "", + "stderr": "" + }, + { + "args": [ + "xcrun", + "simctl", + "list", + "devices", + "booted", + "-j" + ], + "exit": 0, + "stdout": "{\n \"devices\" : {\n \"com.apple.CoreSimulator.SimRuntime.iOS-17-2\" : [\n\n ],\n \"com.apple.CoreSimulator.SimRuntime.iOS-26-0\" : [\n\n ],\n \"com.apple.CoreSimulator.SimRuntime.iOS-18-3\" : [\n {\n \"lastBootedAt\" : \"2026-10-29T14:27:43Z\",\n \"dataPath\" : \"\\/Users\\/klaus\\/Library\\/Developer\\/CoreSimulator\\/Devices\\/482F7827-2E3A-4559-BAF4-48B68CC5725B\\/data\",\n \"dataPathSize\" : 1427083264,\n \"logPath\" : \"\\/Users\\/klaus\\/Library\\/Logs\\/CoreSimulator\\/482F7827-2E3A-4559-BAF4-48B68CC5725B\",\n \"udid\" : \"482F7827-2E3A-4559-BAF4-48B68CC5725B\",\n \"isAvailable\" : true,\n \"logPathSize\" : 249856,\n \"deviceTypeIdentifier\" : \"com.apple.CoreSimulator.SimDeviceType.iPhone-SE-3rd-generation\",\n \"state\" : \"Booted\",\n \"name\" : \"SubBannerQA-20261029\"\n }\n ],\n \"com.apple.CoreSimulator.SimRuntime.xrOS-1-0\" : [\n\n ],\n \"com.apple.CoreSimulator.SimRuntime.iOS-14-4\" : [\n\n ],\n \"com.apple.CoreSimulator.SimRuntime.iOS-15-0\" : [\n\n ],\n \"com.apple.CoreSimulator.SimRuntime.iOS-15-5\" : [\n\n ],\n \"com.apple.CoreSimulator.SimRuntime.iOS-17-5\" : [\n\n ],\n \"com.apple.CoreSimulator.SimRuntime.iOS-13-7\" : [\n\n ]\n }\n}\n", + "stderr": "" + } + ], + "appAbsent": true, + "pidAbsent": true, + "rootAbsent": true, + "parentDeviceStillBooted": true, + "productHashesUnchanged": true, + "sharedCacheDeleted": false +} \ No newline at end of file diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/commands.json b/docs/20261029_추천_서브_배너/reviews/evidence/native/commands.json new file mode 100644 index 00000000..1b0920a1 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native/commands.json @@ -0,0 +1,144 @@ +[ + { + "args": [ + "xcrun", + "--sdk", + "iphonesimulator", + "--show-sdk-path" + ], + "exit": 0, + "stdout": "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.0.sdk\n", + "stderr": "" + }, + { + "args": [ + "xcrun", + "--sdk", + "iphonesimulator", + "swiftc", + "-swift-version", + "5", + "-target", + "x86_64-apple-ios16.6-simulator", + "-sdk", + "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.0.sdk", + "-I", + "/Users/klaus/Library/Developer/Xcode/DerivedData/SodaLive-clzdtvqrsfonoxcijatzkrqdzrsu/Build/Products/Debug-iphonesimulator", + "-module-cache-path", + "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-native.tstn99/module-cache", + "SodaLive/Sources/V2/Component/Banner/SubBanner.swift", + "SodaLive/Sources/V2/Component/Banner/SubBannerLinkOpener.swift", + "SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift", + "SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift", + "SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationView.swift", + "SodaLive/Sources/UI/Theme/Spacing.swift", + "SodaLive/Sources/UI/Theme/Color.swift", + "SodaLive/Sources/Extensions/ColorExtension.swift", + "SodaLive/Sources/Extensions/FontModifier.swift", + "/Users/klaus/Develop/sodalive/iOS/SodaLive/docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/source/Doubles.swift", + "/Users/klaus/Develop/sodalive/iOS/SodaLive/docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/source/Harness.swift", + "/Users/klaus/Library/Developer/Xcode/DerivedData/SodaLive-clzdtvqrsfonoxcijatzkrqdzrsu/Build/Products/Debug-iphonesimulator/Kingfisher.o", + "-o", + "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-native.tstn99/SubBannerQA.app/SubBannerQA" + ], + "exit": 1, + "stdout": "", + "stderr": "/Users/klaus/Develop/sodalive/iOS/SodaLive/docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/source/Harness.swift:93:30: error: cannot convert value of type 'KeyPath' to expected argument type 'WritableKeyPath'\n 91 | .background(PhaseProbe())\n 92 | .environment(\\.scenePhase, state.phase)\n 93 | .environment(\\.accessibilityReduceMotion, state.reduceMotion)\n | `- error: cannot convert value of type 'KeyPath' to expected argument type 'WritableKeyPath'\n 94 | }\n 95 | Spacer(minLength: 0)\n" + }, + { + "args": [ + "xcrun", + "--sdk", + "iphonesimulator", + "--show-sdk-path" + ], + "exit": 0, + "stdout": "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.0.sdk\n", + "stderr": "" + }, + { + "args": [ + "xcrun", + "--sdk", + "iphonesimulator", + "swiftc", + "-swift-version", + "5", + "-target", + "x86_64-apple-ios16.6-simulator", + "-sdk", + "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.0.sdk", + "-I", + "/Users/klaus/Library/Developer/Xcode/DerivedData/SodaLive-clzdtvqrsfonoxcijatzkrqdzrsu/Build/Products/Debug-iphonesimulator", + "-module-cache-path", + "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-native.tstn99/module-cache", + "SodaLive/Sources/V2/Component/Banner/SubBanner.swift", + "SodaLive/Sources/V2/Component/Banner/SubBannerLinkOpener.swift", + "SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift", + "SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift", + "SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationView.swift", + "SodaLive/Sources/UI/Theme/Spacing.swift", + "SodaLive/Sources/UI/Theme/Color.swift", + "SodaLive/Sources/Extensions/ColorExtension.swift", + "SodaLive/Sources/Extensions/FontModifier.swift", + "/Users/klaus/Develop/sodalive/iOS/SodaLive/docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/source/Doubles.swift", + "/Users/klaus/Develop/sodalive/iOS/SodaLive/docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/source/Harness.swift", + "/Users/klaus/Library/Developer/Xcode/DerivedData/SodaLive-clzdtvqrsfonoxcijatzkrqdzrsu/Build/Products/Debug-iphonesimulator/Kingfisher.o", + "-o", + "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-native.tstn99/SubBannerQA.app/SubBannerQA" + ], + "exit": 0, + "stdout": "", + "stderr": "" + }, + { + "args": [ + "xcrun", + "simctl", + "install", + "482F7827-2E3A-4559-BAF4-48B68CC5725B", + "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-native.tstn99/SubBannerQA.app" + ], + "exit": 0, + "stdout": "", + "stderr": "" + }, + { + "args": [ + "xcrun", + "simctl", + "launch", + "482F7827-2E3A-4559-BAF4-48B68CC5725B", + "qa.sodalive.subbanner.native.tstn99" + ], + "exit": 0, + "stdout": "qa.sodalive.subbanner.native.tstn99: 93117\n", + "stderr": "" + }, + { + "args": [ + "xcrun", + "simctl", + "get_app_container", + "482F7827-2E3A-4559-BAF4-48B68CC5725B", + "qa.sodalive.subbanner.native.tstn99", + "data" + ], + "exit": 0, + "stdout": "/Users/klaus/Library/Developer/CoreSimulator/Devices/482F7827-2E3A-4559-BAF4-48B68CC5725B/data/Containers/Data/Application/BB5A2FB5-0655-49EF-862F-7DC3EC07B3BE\n", + "stderr": "" + }, + { + "args": [ + "xcrun", + "simctl", + "io", + "482F7827-2E3A-4559-BAF4-48B68CC5725B", + "screenshot", + "/Users/klaus/Develop/sodalive/iOS/SodaLive/docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/native-os-final.png" + ], + "exit": 0, + "stdout": "", + "stderr": "Detected file type 'PNG' from extension\nWrote screenshot to: /Users/klaus/Develop/sodalive/iOS/SodaLive/docs/20261029_\ucd94\ucc9c_\uc11c\ube0c_\ubc30\ub108/reviews/evidence/native/native-os-final.png\n" + } +] \ No newline at end of file diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/contact-1.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/contact-1.png new file mode 100644 index 00000000..aaa84873 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/contact-1.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/contact-2.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/contact-2.png new file mode 100644 index 00000000..62ff1df1 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/contact-2.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/contact-3.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/contact-3.png new file mode 100644 index 00000000..c59d8407 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/contact-3.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/contact-4.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/contact-4.png new file mode 100644 index 00000000..0b143272 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/contact-4.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/dependency-hashes.json b/docs/20261029_추천_서브_배너/reviews/evidence/native/dependency-hashes.json new file mode 100644 index 00000000..45b4f5c7 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native/dependency-hashes.json @@ -0,0 +1,4 @@ +{ + "/Users/klaus/Library/Developer/Xcode/DerivedData/SodaLive-clzdtvqrsfonoxcijatzkrqdzrsu/Build/Products/Debug-iphonesimulator/Kingfisher.o": "ab9481b5935132edf5744692d88ff4c3e34f1a68f4014f88267a5ad5d95f31b8", + "/Users/klaus/Library/Developer/Xcode/DerivedData/SodaLive-clzdtvqrsfonoxcijatzkrqdzrsu/Build/Products/Debug-iphonesimulator/Kingfisher.swiftmodule/x86_64-apple-ios-simulator.swiftmodule": "b155d956bfaa735f7c72f4630b5be0e2dc82fcb768c2c0ce930ab17faff22fdc" +} \ No newline at end of file diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/native-os-final.png b/docs/20261029_추천_서브_배너/reviews/evidence/native/native-os-final.png new file mode 100644 index 00000000..2c64beaf Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/native/native-os-final.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/ownership.json b/docs/20261029_추천_서브_배너/reviews/evidence/native/ownership.json new file mode 100644 index 00000000..7a11f823 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native/ownership.json @@ -0,0 +1,7 @@ +{ + "root": "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-native.tstn99", + "bundle": "qa.sodalive.subbanner.native.tstn99", + "pid": 93117, + "device": "482F7827-2E3A-4559-BAF4-48B68CC5725B", + "deviceOwner": "parent" +} \ No newline at end of file diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/source-hashes-after.json b/docs/20261029_추천_서브_배너/reviews/evidence/native/source-hashes-after.json new file mode 100644 index 00000000..7a798cfd --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native/source-hashes-after.json @@ -0,0 +1,11 @@ +{ + "SodaLive/Sources/V2/Component/Banner/SubBanner.swift": "875bcdb953e47e9c3e157a7d5adc8b39c271691af2b02437fc738ac727d5b7e9", + "SodaLive/Sources/V2/Component/Banner/SubBannerLinkOpener.swift": "0aa860b206d0dff737cb5847741a2b531dbe633728b5fa5f2eeea5410834dab3", + "SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift": "1bf03b8b1e05310acce09f12b2f09b35af3587f4a7d486b22945ec502cfb506d", + "SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift": "c49150d7b504b711c249458a3e2fb96c1f6a75d2b83d97ff29f674d9b928b48f", + "SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationView.swift": "2d0240fb8a150e7e365cc0daff67fa027c31ce709e12f518c1b52f0cec56a0b8", + "SodaLive/Sources/UI/Theme/Spacing.swift": "372a73b821236c15449fa022cd628f61556f1381a16f234a4b77e833c2e588c1", + "SodaLive/Sources/UI/Theme/Color.swift": "0f50ec7d3e77e0813fd1a806440c01baf96f8fdfa2a41faccd6dc4df52cf647a", + "SodaLive/Sources/Extensions/ColorExtension.swift": "747347d86ade888fe9c1d30629285b4a71991bc0f21189a0d4b76ad314c32364", + "SodaLive/Sources/Extensions/FontModifier.swift": "086fb0a19d884124a4cca638f7c8483de724ee31a61603b2cbd489aabf26a6c6" +} \ No newline at end of file diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/source-hashes-before.json b/docs/20261029_추천_서브_배너/reviews/evidence/native/source-hashes-before.json new file mode 100644 index 00000000..7a798cfd --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native/source-hashes-before.json @@ -0,0 +1,11 @@ +{ + "SodaLive/Sources/V2/Component/Banner/SubBanner.swift": "875bcdb953e47e9c3e157a7d5adc8b39c271691af2b02437fc738ac727d5b7e9", + "SodaLive/Sources/V2/Component/Banner/SubBannerLinkOpener.swift": "0aa860b206d0dff737cb5847741a2b531dbe633728b5fa5f2eeea5410834dab3", + "SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift": "1bf03b8b1e05310acce09f12b2f09b35af3587f4a7d486b22945ec502cfb506d", + "SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift": "c49150d7b504b711c249458a3e2fb96c1f6a75d2b83d97ff29f674d9b928b48f", + "SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationView.swift": "2d0240fb8a150e7e365cc0daff67fa027c31ce709e12f518c1b52f0cec56a0b8", + "SodaLive/Sources/UI/Theme/Spacing.swift": "372a73b821236c15449fa022cd628f61556f1381a16f234a4b77e833c2e588c1", + "SodaLive/Sources/UI/Theme/Color.swift": "0f50ec7d3e77e0813fd1a806440c01baf96f8fdfa2a41faccd6dc4df52cf647a", + "SodaLive/Sources/Extensions/ColorExtension.swift": "747347d86ade888fe9c1d30629285b4a71991bc0f21189a0d4b76ad314c32364", + "SodaLive/Sources/Extensions/FontModifier.swift": "086fb0a19d884124a4cca638f7c8483de724ee31a61603b2cbd489aabf26a6c6" +} \ No newline at end of file diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/source-hashes-final.json b/docs/20261029_추천_서브_배너/reviews/evidence/native/source-hashes-final.json new file mode 100644 index 00000000..7a798cfd --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native/source-hashes-final.json @@ -0,0 +1,11 @@ +{ + "SodaLive/Sources/V2/Component/Banner/SubBanner.swift": "875bcdb953e47e9c3e157a7d5adc8b39c271691af2b02437fc738ac727d5b7e9", + "SodaLive/Sources/V2/Component/Banner/SubBannerLinkOpener.swift": "0aa860b206d0dff737cb5847741a2b531dbe633728b5fa5f2eeea5410834dab3", + "SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift": "1bf03b8b1e05310acce09f12b2f09b35af3587f4a7d486b22945ec502cfb506d", + "SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift": "c49150d7b504b711c249458a3e2fb96c1f6a75d2b83d97ff29f674d9b928b48f", + "SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationView.swift": "2d0240fb8a150e7e365cc0daff67fa027c31ce709e12f518c1b52f0cec56a0b8", + "SodaLive/Sources/UI/Theme/Spacing.swift": "372a73b821236c15449fa022cd628f61556f1381a16f234a4b77e833c2e588c1", + "SodaLive/Sources/UI/Theme/Color.swift": "0f50ec7d3e77e0813fd1a806440c01baf96f8fdfa2a41faccd6dc4df52cf647a", + "SodaLive/Sources/Extensions/ColorExtension.swift": "747347d86ade888fe9c1d30629285b4a71991bc0f21189a0d4b76ad314c32364", + "SodaLive/Sources/Extensions/FontModifier.swift": "086fb0a19d884124a4cca638f7c8483de724ee31a61603b2cbd489aabf26a6c6" +} \ No newline at end of file diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/source-stability.json b/docs/20261029_추천_서브_배너/reviews/evidence/native/source-stability.json new file mode 100644 index 00000000..cc72526d --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native/source-stability.json @@ -0,0 +1,4 @@ +{ + "unchanged": true, + "changed": [] +} \ No newline at end of file diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/source/Doubles.swift b/docs/20261029_추천_서브_배너/reviews/evidence/native/source/Doubles.swift new file mode 100644 index 00000000..6cbd8a9d --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native/source/Doubles.swift @@ -0,0 +1,160 @@ +import SwiftUI +import Combine + +enum LanguageOption: String { case system, ko, en, ja } +struct QAItem { let creatorId = 1; let postId = 1; let price = 1 } +struct RecommendationBannerResponse {} +struct AudioBannerResponse {} +struct FixtureResponse { + var subBanners: [SubBanner] + var eligibleSubBanners: [SubBanner] { subBanners.filter(\.isEligible) } + let lives: [QAItem] = [.init()] + let banners: [QAItem] = [.init()] + let recentlyActiveCreators: [QAItem] = [.init()] + let recentDebutCreators: [QAItem] = [.init()] + let aiCharacters: [QAItem] = [] + let cheerCreators: [QAItem] = [] + let popularCommunityPosts: [QAItem] = [] + let originalSeries: [QAItem] = [.init()] + let newAndHotAudios: [QAItem] = [] + let latestAudios: [QAItem] = [] + let freeAudios: [QAItem] = [] + let pointAudios: [QAItem] = [] + let mostCommentedAudios: [QAItem] = [] + let recommendedAudios: [QAItem] = [] +} +final class MainHomeRecommendationViewModel: ObservableObject { + static weak var current: MainHomeRecommendationViewModel? + @Published var recommendations: FixtureResponse? = .init(subBanners: []) + @Published var isLoading = false + @Published var errorMessage = "" + @Published var isShowPopup = false + @Published var isShowCommunityPostPurchaseDialog = false + var purchasingCommunityPostPrice = 0 + init() { Self.current = self; recommendations = .init(subBanners: HarnessState.shared.items) } + func refresh() async {} + func fetchRecommendations() {} + func isFollowAllCompleted(_ key: String) -> Bool { false } + func isFollowAllLoading(_ key: String) -> Bool { false } + func followAll(creatorIds: [Int], completionKey: String) {} + func likeCommunityPost(postId: Int) {} + func openCommunityPostPurchase(postId: Int, price: Int) {} + func purchaseCommunityPost() {} +} +final class MainContentRecommendationViewModel: ObservableObject { + static weak var current: MainContentRecommendationViewModel? + @Published var recommendations: FixtureResponse? = .init(subBanners: []) + @Published var isLoading = false + @Published var isShowPopup = false + var errorMessage = "" + let hasLoaded = true + let shouldShowEmptyState = false + let emptyStateMessage = "QA empty" + init() { Self.current = self; recommendations = .init(subBanners: HarnessState.shared.items) } + func refresh() async {} + func fetchRecommendations() {} +} +final class CreatorCommunityMediaPlayerManager: ObservableObject { + static let shared = CreatorCommunityMediaPlayerManager() + @Published var isShowPopup = false + let errorMessage = "" + let isLoading = false +} +enum I18n { + enum MainContentRecommendation { + static let sodaLiveOnlySectionTitle = "QA exclusive series" + static let newAndHotSectionTitle = "QA new and hot" + static let latestAudioSectionTitle = "QA latest" + static let freeAudioSectionTitle = "QA free" + static let pointAudioSectionTitle = "QA point" + static let mostCommentedAudioSectionTitle = "QA comments" + static let recommendedAudioSectionTitle = "QA recommended" + } +} +struct Rectangles: PreferenceKey { + static var defaultValue: [String: CGRect] = [:] + static func reduce(value: inout [String: CGRect], nextValue: () -> [String: CGRect]) { value.merge(nextValue(), uniquingKeysWith: { _, new in new }) } +} +extension View { + func probe(_ name: String) -> some View { + background(GeometryReader { proxy in Color.clear.preference(key: Rectangles.self, value: [name: proxy.frame(in: .global)]) }) + } + func sodaToast(isPresented: Binding, message: String, autohideIn: Double) -> some View { self } +} +struct QABar: View { + let name: String + var body: some View { + Text(name).appFont(.body3).foregroundColor(.white) + .frame(maxWidth: .infinity).frame(height: SodaSpacing.s48) + .background(Color.gray900).probe(name) + } +} +struct LottieRefreshableScrollView: View { + let action: () async -> Void + @ViewBuilder let content: () -> Content + var body: some View { ScrollView { content() } } +} +struct MainHomeLiveSection: View { + let items: [QAItem]; let onTapLive: (Int) -> Void; let onTapAll: () -> Void + var body: some View { QABar(name: "QA home live") } +} +struct MainHomeBannerSection: View { + let items: [QAItem]; let onTapBanner: (RecommendationBannerResponse) -> Void + var body: some View { QABar(name: "QA home main banner") } +} +struct MainHomeActiveCreatorSection: View { + let items: [QAItem]; let onTapLive: (Int) -> Void; let onTapCreator: (Int) -> Void + let onTapContent: (Int) -> Void; let onTapCommunityPost: (Int) -> Void + var body: some View { QABar(name: "QA active creators") } +} +struct MainHomeRecentDebutCreatorSection: View { + let items: [QAItem]; let onTapCreator: (Int) -> Void + var body: some View { QABar(name: "QA debut creators") } +} +struct MainHomeAiCharacterSection: View { + let items: [QAItem]; let onTapCharacter: (Int) -> Void + var body: some View { EmptyView() } +} +struct MainHomeCheerCreatorSection: View { + let items: [QAItem]; let isFollowAllCompleted: Bool; let isFollowAllLoading: Bool + let onTapCreator: (Int) -> Void; let onTapFollowAll: () -> Void + var body: some View { EmptyView() } +} +struct MainHomePopularCommunitySection: View { + let items: [QAItem]; let onTapCommunityPost: (Int) -> Void; let onTapLike: (Int) -> Void; let onTapPurchase: (QAItem) -> Void + var body: some View { EmptyView() } +} +struct MainHomeBusinessInfoSection: View { var body: some View { QABar(name: "QA other home content") } } +struct CommunityPostPurchaseDialog: View { + @Binding var isShowing: Bool; let can: Int; let confirmAction: () -> Void + var body: some View { EmptyView() } +} +struct LoadingView: View { var body: some View { ProgressView() } } +struct MainContentAudioBannerSection: View { + let items: [QAItem]; let onTapBanner: (AudioBannerResponse) -> Void + var body: some View { QABar(name: "QA content main banner") } +} +struct MainContentVoiceOnOnlySection: View { + let title: String; let items: [QAItem]; let action: () -> Void; let onTapSeries: (Int) -> Void + var body: some View { QABar(name: "QA exclusive series") } +} +struct MainContentAudioListCarouselSection: View { + let title: String; let items: [QAItem]; let action: () -> Void; let onTapContent: (Int) -> Void + var body: some View { QABar(name: "QA other content") } +} +struct MainContentAudioHorizontalCardSection: View { + let title: String; let items: [QAItem]; let action: () -> Void; let onTapContent: (Int) -> Void + var body: some View { EmptyView() } +} +struct MainContentMostCommentedAudioSection: View { + let title: String; let items: [QAItem]; let onTapContent: (Int) -> Void + var body: some View { EmptyView() } +} +struct MainContentRecommendedAudioGridSection: View { + let title: String; let items: [QAItem]; let onTapContent: (Int) -> Void + var body: some View { EmptyView() } +} +struct MainContentAudioEmptyStateView: View { + let message: String + var body: some View { Text(message) } +} diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/source/Harness.swift b/docs/20261029_추천_서브_배너/reviews/evidence/native/source/Harness.swift new file mode 100644 index 00000000..8a84d623 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native/source/Harness.swift @@ -0,0 +1,207 @@ +import SwiftUI +import UIKit +import Kingfisher + +final class HarnessState: ObservableObject { + static let shared = HarnessState() + @Published var items: [SubBanner] = [] + @Published var context = "home" + @Published var mounted = true + @Published var width: CGFloat = 375 + @Published var phase = ScenePhase.active + @Published var reduceMotion = false + var frames: [String: CGRect] = [:] + var scenario = "preparing" + func update(_ items: [SubBanner]) { + self.items = items + MainHomeRecommendationViewModel.current?.recommendations = .init(subBanners: items) + MainContentRecommendationViewModel.current?.recommendations = .init(subBanners: items) + } +} + +final class FixtureProtocol: URLProtocol { + static var requests: [[String: Any]] = [] + override class func canInit(with request: URLRequest) -> Bool { true } + override class func canonicalRequest(for request: URLRequest) -> URLRequest { request } + override func startLoading() { + let url = request.url! + DispatchQueue.main.async { + Self.requests.append(["url": url.absoluteString, "uptime": ProcessInfo.processInfo.systemUptime]) + } + guard url.host == "subbanner.qa.invalid" else { + client?.urlProtocol(self, didFailWithError: URLError(.notConnectedToInternet)); return + } + if url.path.contains("fail") { + client?.urlProtocol(self, didFailWithError: URLError(.cannotDecodeContentData)); return + } + let tall = url.path.contains("tall") + let size = CGSize(width: 1200, height: tall ? 600 : 400) + let format = UIGraphicsImageRendererFormat(); format.scale = 1 + let image = UIGraphicsImageRenderer(size: size, format: format).image { context in + (tall ? UIColor.systemOrange : UIColor.systemTeal).setFill() + context.fill(CGRect(origin: .zero, size: size)) + let title = tall ? "B 1200 x 600" : "A 1200 x 400" + title.draw(at: CGPoint(x: 64, y: 96), withAttributes: [.font: UIFont.boldSystemFont(ofSize: 70), .foregroundColor: UIColor.black]) + url.lastPathComponent.draw(at: CGPoint(x: 64, y: 210), withAttributes: [.font: UIFont.systemFont(ofSize: 42), .foregroundColor: UIColor.black]) + } + let deliver = { + self.client?.urlProtocol(self, didReceive: HTTPURLResponse(url: url, statusCode: 200, httpVersion: nil, headerFields: ["Content-Type": "image/png"])!, cacheStoragePolicy: .notAllowed) + self.client?.urlProtocol(self, didLoad: image.pngData()!) + self.client?.urlProtocolDidFinishLoading(self) + } + if url.path.contains("delay") { DispatchQueue.main.asyncAfter(deadline: .now() + 8, execute: deliver) } + else { deliver() } + } + override func stopLoading() {} +} + +struct PhaseProbe: View { + @Environment(\.scenePhase) private var phase + @Environment(\.accessibilityReduceMotion) private var reduce + var body: some View { + Color.clear.onAppear { HarnessDelegate.current?.record("environment", ["phase": String(describing: phase), "reduceMotion": reduce]) } + .onChange(of: phase) { HarnessDelegate.current?.record("phase", ["value": String(describing: $0)]) } + .onChange(of: reduce) { HarnessDelegate.current?.record("reduceMotion", ["value": $0]) } + } +} +struct HarnessRoot: View { + @ObservedObject var state = HarnessState.shared + var body: some View { + VStack(spacing: 0) { + Text("NATIVE QA / " + state.context).appFont(.caption2).foregroundColor(.white).frame(height: SodaSpacing.s24) + if state.mounted { + Group { + if state.context == "home" { + MainHomeRecommendationView(onTapLive: { _ in }, onTapCreator: { _ in }, onTapContent: { _ in }, onTapCharacter: { _ in }, onTapCommunityPost: { _ in }, onTapBanner: { _ in }, onTapLiveAll: {}, onTapFollowAll: { $0() }) + } else if state.context == "content" { + MainContentRecommendationView(onTapContent: { _ in }, onTapBanner: { _ in }, onTapSeries: { _ in }, onTapLatestAudioAll: {}, onTapNewAndHotAll: {}, onTapSodaLiveOnlyAll: {}, onTapFreeAudioAll: {}, onTapPointAudioAll: {}) + } else { + VStack(spacing: SodaSpacing.s24) { + QABar(name: "QA direct before") + if !state.items.isEmpty { + SubBannerCarousel(items: state.items) { item in HarnessDelegate.current?.record("carousel-onTap", ["id": item.id]) }.probe("carousel") + } + QABar(name: "QA direct after") + Spacer() + } + } + } + .frame(width: state.width) + .frame(maxWidth: .infinity, alignment: .leading) + .background(PhaseProbe()) + .environment(\.scenePhase, state.phase) + } + Spacer(minLength: 0) + } + .background(Color.black) + .onPreferenceChange(Rectangles.self) { state.frames = $0 } + } +} + +final class HarnessDelegate: NSObject, UIApplicationDelegate { + static weak var current: HarnessDelegate? + var window: UIWindow? + let state = HarnessState.shared + let started = ProcessInfo.processInfo.systemUptime + var events: [[String: Any]] = [] + let output = URL.documentsDirectory.appendingPathComponent("native", isDirectory: true) + func application(_ application: UIApplication, didFinishLaunchingWithOptions options: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { + Self.current = self + UserDefaults.standard.set("en", forKey: "app.language") + let config = URLSessionConfiguration.ephemeral + config.protocolClasses = [FixtureProtocol.self] + ImageDownloader.default.sessionConfiguration = config + try! FileManager.default.createDirectory(at: output, withIntermediateDirectories: true) + let window = UIWindow(frame: UIScreen.main.bounds) + window.rootViewController = UIHostingController(rootView: HarnessRoot()) + window.overrideUserInterfaceStyle = .dark + self.window = window + window.makeKeyAndVisible() + after(1) { Task { @MainActor in await self.run() } } + return true + } + func after(_ seconds: Double, _ action: @escaping () -> Void) { DispatchQueue.main.asyncAfter(deadline: .now() + seconds, execute: action) } + @MainActor func wait(_ seconds: Double) async { try? await Task.sleep(nanoseconds: UInt64(seconds * 1_000_000_000)) } + func record(_ event: String, _ details: [String: Any] = [:]) { + var entry = details + entry["event"] = event; entry["time"] = ProcessInfo.processInfo.systemUptime - started; entry["scenario"] = state.scenario + events.append(entry) + try! JSONSerialization.data(withJSONObject: events, options: [.prettyPrinted, .sortedKeys]).write(to: output.appendingPathComponent("actions.json")) + } + func sample(_ name: String) { + guard let window else { return } + let image = UIGraphicsImageRenderer(bounds: window.bounds).image { _ in window.drawHierarchy(in: window.bounds, afterScreenUpdates: true) } + try! image.pngData()!.write(to: output.appendingPathComponent(name + ".png")) + let frames = state.frames.mapValues { ["x": $0.minX, "y": $0.minY, "width": $0.width, "height": $0.height] } + record(name, ["frames": frames, "inputIDs": state.items.map(\.id), "width": state.width, "phase": String(describing: state.phase), "osReduceMotion": UIAccessibility.isReduceMotionEnabled, "requests": FixtureProtocol.requests, "png": name + ".png"]) + } + func banner(_ id: Int, _ path: String, _ context: String = "home") -> SubBanner { + SubBanner(id: id, placement: context == "content" ? .contentRecommendation : .homeRecommendation, imageUrl: "https://subbanner.qa.invalid/" + path, webUrl: "https://subbanner.qa.invalid/destination", androidDeepLink: nil, iosDeepLink: "sodaqa://banner/\(id)") + } + @MainActor func setup(_ name: String, context: String, items: [SubBanner]) async { + state.mounted = false + await wait(0.15) + state.scenario = name; state.context = context; state.width = 375; state.phase = .active; state.reduceMotion = false + state.update(items); state.mounted = true + record("scenario-start", ["context": context, "count": items.count]) + } + @MainActor func run() async { + for context in ["home", "content"] { + let pair = [banner(1, context + "-wide.png", context), banner(2, context + "-tall.png", context)] + await setup(context + "-pair", context: context, items: pair) + await wait(0.8); sample(context + "-first") + await wait(3.6); sample(context + "-before-5s") + await wait(0.75); sample(context + "-transition") + await wait(0.65); sample(context + "-second") + await wait(4.8); sample(context + "-wrap") + window?.overrideUserInterfaceStyle = .light + await wait(0.3); sample(context + "-light") + window?.overrideUserInterfaceStyle = .dark + state.update([]); await wait(0.4); sample(context + "-empty") + state.update([banner(3, context + "-single-wide.png", context)]) + await wait(0.6); sample(context + "-one") + await wait(6); sample(context + "-one-after-6s") + state.width = 320; await wait(0.5); sample(context + "-resize-320") + state.width = 375; await wait(0.3) + } + await setup("delayed-current", context: "direct", items: [banner(10, "delay-wide.png"), banner(11, "valid-tall.png")]) + await wait(0.6); sample("delayed-no-invented-height") + await wait(5.3); sample("delayed-next-valid") + await wait(2.7); sample("delayed-old-image-arrived") + await setup("failure-isolation", context: "content", items: [banner(20, "valid-wide.png"), banner(21, "fail.png")]) + await wait(0.8); sample("failure-valid-first") + await wait(5); sample("failure-placeholder-other-content") + await wait(5); sample("failure-wrap-valid") + await setup("inactive-remount", context: "direct", items: [banner(30, "lifecycle-wide.png"), banner(31, "lifecycle-tall.png")]) + await wait(5.8); sample("inactive-before") + state.phase = .inactive; record("inject-inactive") + await wait(0.3); sample("inactive-start") + await wait(6); sample("inactive-after-6s") + state.phase = .active; record("inject-active") + await wait(4.4); sample("active-before-5s") + await wait(1.3); sample("active-next") + state.mounted = false; record("unmount") + await wait(6); sample("unmounted-after-6s") + state.mounted = true; record("remount") + await wait(0.6); sample("remount-first") + await wait(5.1); sample("remount-next") + state.update([banner(32, "shrink-wide.png")]); await wait(0.6); sample("shrink-one") + await setup("reduced-motion", context: "direct", items: [banner(40, "reduce-wide.png"), banner(41, "reduce-tall.png")]) + record("reduce-motion-observation-only", ["osValue": UIAccessibility.isReduceMotionEnabled, "enabledTest": "NOT RUN: read-only SwiftUI environment; parent device preferences unchanged"]) + await wait(0.7); sample("reduce-first") + await wait(4.45); sample("reduce-transition") + await wait(0.7); sample("reduce-second") + await setup("twenty-one", context: "direct", items: (1...21).map { banner(100 + $0, "many-\($0)-wide.png") }) + await wait(0.8); sample("many-first") + await wait(99.8); sample("many-twenty-first") + await wait(5); sample("many-wrap") + let target = banner(900, "action-wide.png") + var attempts: [String] = [] + SubBannerLinkOpener.open(target) { url, completion in attempts.append(url.absoluteString); completion(false) } + record("opener-driver-not-physical-tap", ["attempts": attempts]) + record("finished") + } +} +@main struct Entry { + static func main() { UIApplicationMain(CommandLine.argc, CommandLine.unsafeArgv, nil, NSStringFromClass(HarnessDelegate.self)) } +} diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/source/assess.py b/docs/20261029_추천_서브_배너/reviews/evidence/native/source/assess.py new file mode 100644 index 00000000..97319de6 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native/source/assess.py @@ -0,0 +1,96 @@ +import hashlib +import json +from pathlib import Path +from PIL import Image, ImageDraw + +evidence = Path('docs/20261029_추천_서브_배너/reviews/evidence/native') +capture = evidence / 'captures' +events = json.loads((capture / 'actions.json').read_text()) +samples = {event['event']: event for event in events if 'png' in event} +before = json.loads((evidence / 'source-hashes-before.json').read_text()) +current = {path: hashlib.sha256(Path(path).read_bytes()).hexdigest() for path in before} +assert current == before, 'Product source changed; rerun native capture' +assert events[-1]['event'] == 'finished' +image_info = {} +for path in sorted(capture.glob('*.png')): + assert path.read_bytes().startswith(b'\x89PNG\r\n\x1a\n') + image = Image.open(path).convert('RGB') + assert image.size == (750, 1334) + assert path.stat().st_mtime > max(Path(source).stat().st_mtime for source in before) + image_info[path.name] = {'sha256': hashlib.sha256(path.read_bytes()).hexdigest(), 'width': image.width, 'height': image.height, 'fresh': True} +os_capture = evidence / 'native-os-final.png' +assert os_capture.read_bytes().startswith(b'\x89PNG\r\n\x1a\n') +assert Image.open(os_capture).size == (750, 1334) +assert os_capture.stat().st_mtime > max(Path(source).stat().st_mtime for source in before) + +def frame(name, key): + return samples[name]['frames'][key] + +def pixels(name): + image = Image.open(capture / (name + '.png')).convert('RGB') + rows = [] + for position in range(image.height): + left, right = image.getpixel((0, position)), image.getpixel((image.width - 1, position)) + def colored(value): + red, green, blue = value + return (blue > 140 and green > 110 and red < 100) or (red > 200 and 80 < green < 200 and blue < 60) + if colored(left) and colored(right): + rows.append(position) + if not rows: + return None + return {'x': 0, 'width': image.width / 2, 'y': min(rows) / 2, 'height': len(rows) / 2} + +geometry = {} +for context, preceding, following in [('home', 'QA active creators', 'QA debut creators'), ('content', 'QA content main banner', 'QA exclusive series')]: + for suffix, expected in [('first', 125), ('second', 187.5)]: + name = context + '-' + suffix + previous = frame(name, preceding) + next_frame = frame(name, following) + image = pixels(name) + derived_height = next_frame['y'] - previous['y'] - previous['height'] - 48 + assert derived_height == expected + assert image['height'] == expected and image['width'] == 375 and image['x'] == 0 + assert image['y'] - previous['y'] - previous['height'] == 24 + assert next_frame['y'] - image['y'] - image['height'] == 24 + geometry[name] = {'pixelFramePt': image, 'precedingGapPt': 24, 'followingGapPt': 24, 'derivedHeightPt': derived_height} + empty = context + '-empty' + previous, next_frame = frame(empty, preceding), frame(empty, following) + assert next_frame['y'] - previous['y'] - previous['height'] == 24 + assert pixels(empty) is None + geometry[empty] = {'remainingOrdinarySectionGapPt': 24, 'extraBannerGapPt': 0} + resized = pixels(context + '-resize-320') + image = Image.open(capture / (context + '-resize-320.png')).convert('RGB') + line = [image.getpixel((position, 600 if context == 'home' else 320)) for position in range(image.width)] + colored_positions = [position for position, (red, green, blue) in enumerate(line) if blue > 140 and green > 110 and red < 100] + assert min(colored_positions) == 0 and max(colored_positions) == 639 + previous = frame(context + '-resize-320', preceding) + next_frame = frame(context + '-resize-320', following) + height = next_frame['y'] - previous['y'] - previous['height'] - 48 + assert abs(height - 320 / 3) < 0.51 + geometry[context + '-resize-320'] = {'pixelWidthPt': 320, 'geometryHeightPt': height, 'roundingTolerancePt': 0.51} + assert image_info[context + '-one.png']['sha256'] == image_info[context + '-one-after-6s.png']['sha256'] + +assert frame('delayed-no-invented-height', 'carousel')['height'] == 0 +assert frame('delayed-next-valid', 'carousel')['height'] == 187.5 +assert frame('delayed-old-image-arrived', 'carousel')['height'] == 187.5 +assert image_info['inactive-start.png']['sha256'] == image_info['inactive-after-6s.png']['sha256'] +assert len(samples['many-wrap']['inputIDs']) == 21 +attempts = next(event['attempts'] for event in events if event['event'] == 'opener-driver-not-physical-tap') +assert attempts == ['sodaqa://banner/900', 'https://subbanner.qa.invalid/destination'] + +manifest = list(image_info) +for sheet_index in range(0, len(manifest), 12): + sheet = Image.new('RGB', (1000, 1422), 'white') + draw = ImageDraw.Draw(sheet) + for index, name in enumerate(manifest[sheet_index:sheet_index + 12]): + image = Image.open(capture / name).convert('RGB') + image.thumbnail((250, 445)) + column, row = index % 4, index // 4 + draw.text((column * 250 + 4, row * 474 + 4), name, fill='black') + sheet.paste(image, (column * 250, row * 474 + 25)) + sheet.save(evidence / ('contact-' + str(sheet_index // 12 + 1) + '.png')) + +result = {'nativeCaptureCount': len(image_info), 'additionalOSCapture': {'path': 'native-os-final.png', 'sha256': hashlib.sha256(os_capture.read_bytes()).hexdigest(), 'width': 750, 'height': 1334, 'fresh': True}, 'productHashesUnchanged': current == before, 'geometry': geometry, 'pngs': image_info, 'assertions': 'PASS', 'manualFilenameCorrection': {'many-twenty-first.png': 'Observed 20/21', 'many-wrap.png': 'Observed 21/21', 'native-os-final.png': 'Observed 01/21 after wrap'}, 'reduceMotionEnabledTest': 'NOT RUN; OS false; read-only environment; parent preferences unchanged', 'physicalTapAndSwipe': 'NOT RUN; no native coordinate input tool'} +(evidence / 'verification.json').write_text(json.dumps(result, indent=2)) +(evidence / 'source-hashes-final.json').write_text(json.dumps(current, indent=2)) +print(json.dumps({'nativeCaptures': len(image_info), 'geometry': geometry, 'assertions': 'PASS'}, indent=2)) diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/source/cleanup.py b/docs/20261029_추천_서브_배너/reviews/evidence/native/source/cleanup.py new file mode 100644 index 00000000..eb44d6df --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native/source/cleanup.py @@ -0,0 +1,42 @@ +import hashlib +import json +import os +from pathlib import Path +import shutil +import subprocess + +evidence = Path('docs/20261029_추천_서브_배너/reviews/evidence/native') +ownership = json.loads((evidence / 'ownership.json').read_text()) +root = Path(ownership['root']) +for key in ['TMPDIR', 'NODE_COMPILE_CACHE', 'BUN_RUNTIME_TRANSPILER_CACHE_PATH']: + os.environ[key] = str(root) +commands = [] +def run(args): + result = subprocess.run(args, text=True, capture_output=True) + receipt = {'args': args, 'exit': result.returncode, 'stdout': result.stdout, 'stderr': result.stderr} + commands.append(receipt) + return receipt + +device, bundle = ownership['device'], ownership['bundle'] +assert root.name == 'sub-banner-native.tstn99' +assert bundle == 'qa.sodalive.subbanner.native.tstn99' +assert run(['xcrun', 'simctl', 'terminate', device, bundle])['exit'] == 0 +assert run(['xcrun', 'simctl', 'uninstall', device, bundle])['exit'] == 0 +missing = run(['xcrun', 'simctl', 'get_app_container', device, bundle, 'data']) +assert missing['exit'] != 0 +process = run(['ps', '-p', str(ownership['pid']), '-o', 'pid=,comm=']) +assert process['exit'] != 0 +shutil.rmtree(root) +devices = json.loads(run(['xcrun', 'simctl', 'list', 'devices', 'booted', '-j'])['stdout']) +parent = next(item for items in devices['devices'].values() for item in items if item['udid'] == device) +assert parent['state'] == 'Booted' +before = json.loads((evidence / 'source-hashes-before.json').read_text()) +current = {path: hashlib.sha256(Path(path).read_bytes()).hexdigest() for path in before} +assert current == before +products = Path('/Users/klaus/Library/Developer/Xcode/DerivedData/SodaLive-clzdtvqrsfonoxcijatzkrqdzrsu/Build/Products/Debug-iphonesimulator') +dependencies = ['Kingfisher.o', 'Kingfisher.swiftmodule/x86_64-apple-ios-simulator.swiftmodule'] +dependency_hashes = {str(products / path): hashlib.sha256((products / path).read_bytes()).hexdigest() for path in dependencies} +(evidence / 'dependency-hashes.json').write_text(json.dumps(dependency_hashes, indent=2)) +receipt = {'commands': commands, 'appAbsent': True, 'pidAbsent': True, 'rootAbsent': not root.exists(), 'parentDeviceStillBooted': True, 'productHashesUnchanged': current == before, 'sharedCacheDeleted': False} +(evidence / 'cleanup.json').write_text(json.dumps(receipt, indent=2)) +print(json.dumps({key: value for key, value in receipt.items() if key != 'commands'}, indent=2)) diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/source/run.py b/docs/20261029_추천_서브_배너/reviews/evidence/native/source/run.py new file mode 100644 index 00000000..2d92bee3 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native/source/run.py @@ -0,0 +1,68 @@ +import hashlib +import json +import os +from pathlib import Path +import plistlib +import shutil +import subprocess +import time + +repo = Path.cwd() +evidence = repo / 'docs/20261029_추천_서브_배너/reviews/evidence/native' +root = Path('/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/sub-banner-native.tstn99') +for key in ['TMPDIR', 'NODE_COMPILE_CACHE', 'BUN_RUNTIME_TRANSPILER_CACHE_PATH']: + os.environ[key] = str(root) +device = '482F7827-2E3A-4559-BAF4-48B68CC5725B' +bundle = 'qa.sodalive.subbanner.native.tstn99' +products = Path('/Users/klaus/Library/Developer/Xcode/DerivedData/SodaLive-clzdtvqrsfonoxcijatzkrqdzrsu/Build/Products/Debug-iphonesimulator') +sources = [ + 'SodaLive/Sources/V2/Component/Banner/SubBanner.swift', + 'SodaLive/Sources/V2/Component/Banner/SubBannerLinkOpener.swift', + 'SodaLive/Sources/V2/Component/Banner/SubBannerCarousel.swift', + 'SodaLive/Sources/V2/Main/Home/Recommendation/MainHomeRecommendationView.swift', + 'SodaLive/Sources/V2/Main/Content/Recommendation/MainContentRecommendationView.swift', + 'SodaLive/Sources/UI/Theme/Spacing.swift', + 'SodaLive/Sources/UI/Theme/Color.swift', + 'SodaLive/Sources/Extensions/ColorExtension.swift', + 'SodaLive/Sources/Extensions/FontModifier.swift', +] +previous = evidence / 'commands.json' +commands = json.loads(previous.read_text()) if previous.exists() else [] +def run(args, timeout=120): + result = subprocess.run([str(arg) for arg in args], text=True, capture_output=True, timeout=timeout) + commands.append({'args': [str(arg) for arg in args], 'exit': result.returncode, 'stdout': result.stdout, 'stderr': result.stderr}) + (evidence / 'commands.json').write_text(json.dumps(commands, indent=2)) + print(result.stdout, result.stderr, flush=True) + result.check_returncode() + return result.stdout.strip() + +hashes = {path: hashlib.sha256((repo / path).read_bytes()).hexdigest() for path in sources} +(evidence / 'source-hashes-before.json').write_text(json.dumps(hashes, indent=2)) +app = root / 'SubBannerQA.app' +app.mkdir(exist_ok=True) +with (app / 'Info.plist').open('wb') as stream: + plistlib.dump({'CFBundleIdentifier': bundle, 'CFBundleName': 'SubBannerQA', 'CFBundleExecutable': 'SubBannerQA', 'CFBundlePackageType': 'APPL', 'CFBundleVersion': '1', 'CFBundleShortVersionString': '1', 'MinimumOSVersion': '16.6', 'LSRequiresIPhoneOS': True, 'UIDeviceFamily': [1], 'UILaunchScreen': {}}, stream) +sdk = run(['xcrun', '--sdk', 'iphonesimulator', '--show-sdk-path']) +run(['xcrun', '--sdk', 'iphonesimulator', 'swiftc', '-swift-version', '5', '-target', 'x86_64-apple-ios16.6-simulator', '-sdk', sdk, '-I', products, '-module-cache-path', root / 'module-cache', *sources, evidence / 'source/Doubles.swift', evidence / 'source/Harness.swift', products / 'Kingfisher.o', '-o', app / 'SubBannerQA'], timeout=180) +run(['xcrun', 'simctl', 'install', device, app]) +launch = run(['xcrun', 'simctl', 'launch', device, bundle]) +(evidence / 'ownership.json').write_text(json.dumps({'root': str(root), 'bundle': bundle, 'pid': int(launch.split(':')[-1]), 'device': device, 'deviceOwner': 'parent'}, indent=2)) +container = Path(run(['xcrun', 'simctl', 'get_app_container', device, bundle, 'data'])) +native = container / 'Documents/native' +deadline = time.monotonic() + 330 +while time.monotonic() < deadline: + time.sleep(5) + action_file = native / 'actions.json' + if action_file.exists(): + events = json.loads(action_file.read_text()) + print(events[-1]['event'], events[-1]['time'], flush=True) + if events[-1]['event'] == 'finished': + break +else: + print('TIMEOUT; preserving partial output', flush=True) +shutil.copytree(native, evidence / 'captures', dirs_exist_ok=True) +after = {path: hashlib.sha256((repo / path).read_bytes()).hexdigest() for path in sources} +(evidence / 'source-hashes-after.json').write_text(json.dumps(after, indent=2)) +(evidence / 'source-stability.json').write_text(json.dumps({'unchanged': hashes == after, 'changed': [path for path in sources if hashes[path] != after[path]]}, indent=2)) +run(['xcrun', 'simctl', 'io', device, 'screenshot', evidence / 'native-os-final.png']) +print('CAPTURES COPIED; OWN APP STILL INSTALLED UNTIL ASSESSMENT', flush=True) diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/native/verification.json b/docs/20261029_추천_서브_배너/reviews/evidence/native/verification.json new file mode 100644 index 00000000..574af0cb --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/native/verification.json @@ -0,0 +1,331 @@ +{ + "nativeCaptureCount": 41, + "additionalOSCapture": { + "path": "native-os-final.png", + "sha256": "a79e5e44bbee7a123266c9391c8bf2138d52028a9674c0aa8633cc8ce2d84dd4", + "width": 750, + "height": 1334, + "fresh": true + }, + "productHashesUnchanged": true, + "geometry": { + "home-first": { + "pixelFramePt": { + "x": 0, + "width": 375.0, + "y": 280.0, + "height": 125.0 + }, + "precedingGapPt": 24, + "followingGapPt": 24, + "derivedHeightPt": 125 + }, + "home-second": { + "pixelFramePt": { + "x": 0, + "width": 375.0, + "y": 280.0, + "height": 187.5 + }, + "precedingGapPt": 24, + "followingGapPt": 24, + "derivedHeightPt": 187.5 + }, + "home-empty": { + "remainingOrdinarySectionGapPt": 24, + "extraBannerGapPt": 0 + }, + "home-resize-320": { + "pixelWidthPt": 320, + "geometryHeightPt": 106.66666666666669, + "roundingTolerancePt": 0.51 + }, + "content-first": { + "pixelFramePt": { + "x": 0, + "width": 375.0, + "y": 136.0, + "height": 125.0 + }, + "precedingGapPt": 24, + "followingGapPt": 24, + "derivedHeightPt": 125 + }, + "content-second": { + "pixelFramePt": { + "x": 0, + "width": 375.0, + "y": 136.0, + "height": 187.5 + }, + "precedingGapPt": 24, + "followingGapPt": 24, + "derivedHeightPt": 187.5 + }, + "content-empty": { + "remainingOrdinarySectionGapPt": 24, + "extraBannerGapPt": 0 + }, + "content-resize-320": { + "pixelWidthPt": 320, + "geometryHeightPt": 106.66666666666669, + "roundingTolerancePt": 0.51 + } + }, + "pngs": { + "active-before-5s.png": { + "sha256": "f7bf598d63d31ebffac09bcb5d3cd57183fe3f9da9f4120e14ccd22983c27ca5", + "width": 750, + "height": 1334, + "fresh": true + }, + "active-next.png": { + "sha256": "4880e28aeae451cddbda8f2ba58ec147b60440f26cfa86d75dbf0e6fa381b01f", + "width": 750, + "height": 1334, + "fresh": true + }, + "content-before-5s.png": { + "sha256": "e03bdd3d19802929920672f7f39d26df4bef465d9b13981250cc5fb02f2111a9", + "width": 750, + "height": 1334, + "fresh": true + }, + "content-empty.png": { + "sha256": "ee561f7ce93a7cd7c719dfb32eda4b40e6d0d7998bbb71cfdfa356cdc60e7bcb", + "width": 750, + "height": 1334, + "fresh": true + }, + "content-first.png": { + "sha256": "e03bdd3d19802929920672f7f39d26df4bef465d9b13981250cc5fb02f2111a9", + "width": 750, + "height": 1334, + "fresh": true + }, + "content-light.png": { + "sha256": "e03bdd3d19802929920672f7f39d26df4bef465d9b13981250cc5fb02f2111a9", + "width": 750, + "height": 1334, + "fresh": true + }, + "content-one-after-6s.png": { + "sha256": "c8c2eeee836d90b3422fe1cf7e8646f094e0db78e7bde73880e499317ca1c84d", + "width": 750, + "height": 1334, + "fresh": true + }, + "content-one.png": { + "sha256": "c8c2eeee836d90b3422fe1cf7e8646f094e0db78e7bde73880e499317ca1c84d", + "width": 750, + "height": 1334, + "fresh": true + }, + "content-resize-320.png": { + "sha256": "a6313fb5db500af6e6880ed4f62d42ed2f8e3d2c75f2adaa7d85d8186fffa0cc", + "width": 750, + "height": 1334, + "fresh": true + }, + "content-second.png": { + "sha256": "3558fe0a407f41360c69b241dc5c7bb63d5e2a5b73450db5918b2c87dcfaff13", + "width": 750, + "height": 1334, + "fresh": true + }, + "content-transition.png": { + "sha256": "22e35cf56b149fa74b0cc5c602a64fbb738ffde4ef933acc83ba01be4e04ab6d", + "width": 750, + "height": 1334, + "fresh": true + }, + "content-wrap.png": { + "sha256": "e03bdd3d19802929920672f7f39d26df4bef465d9b13981250cc5fb02f2111a9", + "width": 750, + "height": 1334, + "fresh": true + }, + "delayed-next-valid.png": { + "sha256": "6c20a5f54dc012c9a7d9259c9a7d0628f250262291bced052ab92652e98342c3", + "width": 750, + "height": 1334, + "fresh": true + }, + "delayed-no-invented-height.png": { + "sha256": "60643fc2ef02e26153272782cc4a97422d6ab0b08a0ca5422a94906c1fa4ff5a", + "width": 750, + "height": 1334, + "fresh": true + }, + "delayed-old-image-arrived.png": { + "sha256": "6c20a5f54dc012c9a7d9259c9a7d0628f250262291bced052ab92652e98342c3", + "width": 750, + "height": 1334, + "fresh": true + }, + "failure-placeholder-other-content.png": { + "sha256": "217c258f4a1b83f9c9bf3564cf615a5ef2d0600f1a62819cc7fc2a7becedaa9e", + "width": 750, + "height": 1334, + "fresh": true + }, + "failure-valid-first.png": { + "sha256": "96b2051aedb2733a5620b2de5305cac9532cf9f266b3292d91f550c63550f60b", + "width": 750, + "height": 1334, + "fresh": true + }, + "failure-wrap-valid.png": { + "sha256": "96b2051aedb2733a5620b2de5305cac9532cf9f266b3292d91f550c63550f60b", + "width": 750, + "height": 1334, + "fresh": true + }, + "home-before-5s.png": { + "sha256": "8fc8502f06140d7f04bfb6defea3c235639e874172014611ead622d1ac5306ca", + "width": 750, + "height": 1334, + "fresh": true + }, + "home-empty.png": { + "sha256": "34af8cfecd132f08666cdabd7972a44a536977e5ebee4244a61d1c697b7184a0", + "width": 750, + "height": 1334, + "fresh": true + }, + "home-first.png": { + "sha256": "8fc8502f06140d7f04bfb6defea3c235639e874172014611ead622d1ac5306ca", + "width": 750, + "height": 1334, + "fresh": true + }, + "home-light.png": { + "sha256": "8fc8502f06140d7f04bfb6defea3c235639e874172014611ead622d1ac5306ca", + "width": 750, + "height": 1334, + "fresh": true + }, + "home-one-after-6s.png": { + "sha256": "6dd493128e266ec896e9f5bc2fec1756fad5f5673e88235a7dc342b989515448", + "width": 750, + "height": 1334, + "fresh": true + }, + "home-one.png": { + "sha256": "6dd493128e266ec896e9f5bc2fec1756fad5f5673e88235a7dc342b989515448", + "width": 750, + "height": 1334, + "fresh": true + }, + "home-resize-320.png": { + "sha256": "78760d7c64bcaa360e7147f3f9fefba038ed177e223cbbd4c0c4377ab6e91399", + "width": 750, + "height": 1334, + "fresh": true + }, + "home-second.png": { + "sha256": "8ce24e8f55ea474bd74822282cf180571e6142bfc0942614acc2c6f4d4ac8ac0", + "width": 750, + "height": 1334, + "fresh": true + }, + "home-transition.png": { + "sha256": "765df410019493e09f234a9b92cc2e6d358280938c0cdd28624a8f3ac11d2981", + "width": 750, + "height": 1334, + "fresh": true + }, + "home-wrap.png": { + "sha256": "8fc8502f06140d7f04bfb6defea3c235639e874172014611ead622d1ac5306ca", + "width": 750, + "height": 1334, + "fresh": true + }, + "inactive-after-6s.png": { + "sha256": "f7bf598d63d31ebffac09bcb5d3cd57183fe3f9da9f4120e14ccd22983c27ca5", + "width": 750, + "height": 1334, + "fresh": true + }, + "inactive-before.png": { + "sha256": "f7bf598d63d31ebffac09bcb5d3cd57183fe3f9da9f4120e14ccd22983c27ca5", + "width": 750, + "height": 1334, + "fresh": true + }, + "inactive-start.png": { + "sha256": "f7bf598d63d31ebffac09bcb5d3cd57183fe3f9da9f4120e14ccd22983c27ca5", + "width": 750, + "height": 1334, + "fresh": true + }, + "many-first.png": { + "sha256": "44a42bfedbf994063463e774e4c6d397ea33a71e1443489e3e5a2a0f7f48a1a9", + "width": 750, + "height": 1334, + "fresh": true + }, + "many-twenty-first.png": { + "sha256": "c4173138625345685ed9a945fbd359c2cae59b9265cba158f9c563722680133d", + "width": 750, + "height": 1334, + "fresh": true + }, + "many-wrap.png": { + "sha256": "3844369f5cf4ea0fa2a2a148c06dac9ada3b3838664f5d4a97fbdaa9c3bdbfdb", + "width": 750, + "height": 1334, + "fresh": true + }, + "reduce-first.png": { + "sha256": "c88339572c7bee2dc6c952958ce148331782f28d4b455d2ba9c4a746c64a4ba9", + "width": 750, + "height": 1334, + "fresh": true + }, + "reduce-second.png": { + "sha256": "77c8bcbcb1c693bc5d713fc3e97b7c81c9ed83999eecb3f6858966acf1cbe4dd", + "width": 750, + "height": 1334, + "fresh": true + }, + "reduce-transition.png": { + "sha256": "c88339572c7bee2dc6c952958ce148331782f28d4b455d2ba9c4a746c64a4ba9", + "width": 750, + "height": 1334, + "fresh": true + }, + "remount-first.png": { + "sha256": "4880e28aeae451cddbda8f2ba58ec147b60440f26cfa86d75dbf0e6fa381b01f", + "width": 750, + "height": 1334, + "fresh": true + }, + "remount-next.png": { + "sha256": "f7bf598d63d31ebffac09bcb5d3cd57183fe3f9da9f4120e14ccd22983c27ca5", + "width": 750, + "height": 1334, + "fresh": true + }, + "shrink-one.png": { + "sha256": "23ba00bf778494937ecd3f4f15dd36007127cdf412cb33d5c1f54f2f3a7b95e0", + "width": 750, + "height": 1334, + "fresh": true + }, + "unmounted-after-6s.png": { + "sha256": "8fc8aa79c74ce769ce96965823337521634feb015d742ecf1b5a21cabd275e8b", + "width": 750, + "height": 1334, + "fresh": true + } + }, + "assertions": "PASS", + "manualFilenameCorrection": { + "many-twenty-first.png": "Observed 20/21", + "many-wrap.png": "Observed 21/21", + "native-os-final.png": "Observed 01/21 after wrap" + }, + "reduceMotionEnabledTest": "NOT RUN; OS false; read-only environment; parent preferences unchanged", + "physicalTapAndSwipe": "NOT RUN; no native coordinate input tool" +} \ No newline at end of file diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/no-count/README.md b/docs/20261029_추천_서브_배너/reviews/evidence/no-count/README.md new file mode 100644 index 00000000..82bd236e --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/no-count/README.md @@ -0,0 +1,41 @@ +# Phase 4: 서브 배너 카운트 제거 증거 + +2026-10-30, PRD DEC-009. 제품 변경은 `SubBannerCarousel.swift`의 overlay 6줄 및 `pageIndicator` helper/구분 공백 17줄 삭제뿐이다(181→158줄). 원본 줄 75~80, 118~134를 제외한 문자열과 최종 파일의 완전 일치를 확인했다. 접근성 페이지/전체 라벨, 제스처·순환·타이머·레이아웃은 동일하다. + +## 소스 연결 +- 변경 전 SHA256: `b3e4c5333cf8249ccd7773b5cabd372efb5a5565323bd7778034e110386151fd`. +- 변경 후/runner 실행 전/컴파일 대상/실행 후 SHA256: `15ade1c2e0cd26fca4b0e1833bbb08aa42627335a8c79099528caedc337015d8`. +- 기존 `BannerCarousel.swift` SHA256: `3dc39a2bcb081ed0013a8940f22537b79765353881e0f9468aae98f547bcde19`. 메인 배너 및 기존 harness/UI tests/runner/project generator 해시 불변 확인. + +## 실행 명령과 결과 +첫 도구 호출에서 `RUN_TMP=$(mktemp -d "/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/no-count.XXXXXX")`로 소유 root `no-count.zj3m6P` 및 `OWNED-RESOURCES.txt` 정리 TODO를 생성했다. 모든 shell에서 이 root의 `TMPDIR`, `NODE_COMPILE_CACHE`, `BUN_INSTALL_CACHE_DIR`를 사용했고 기존 runner도 그 아래 module/cache/tmp를 사용했다. + +```bash +export RUN_TMP=/var/folders/yh/8xsbvpsj5wg2qnxzxdp11_gm0000gn/T/opencode/no-count.zj3m6P +export TMPDIR="$RUN_TMP/" NODE_COMPILE_CACHE="$RUN_TMP/node-cache" BUN_INSTALL_CACHE_DIR="$RUN_TMP/bun-cache" +export KINGFISHER_MODULE_PATH=/Users/klaus/Library/Developer/Xcode/DerivedData/SodaLive-clzdtvqrsfonoxcijatzkrqdzrsu/Build/Products/Debug-iphonesimulator +bash work/tests/check-sub-banners.sh --ui +xcrun simctl create NoCount-zj3m6P com.apple.CoreSimulator.SimDeviceType.iPhone-SE-3rd-generation com.apple.CoreSimulator.SimRuntime.iOS-18-3 +export SB_SIMULATOR_ID=755670D7-8F09-458C-9B2A-8707973E1B38 SB_RUN_ROOT="$RUN_TMP" SB_RUN_NAME=no-count +xcrun simctl boot "$SB_SIMULATOR_ID" +xcrun simctl bootstatus "$SB_SIMULATOR_ID" -b +bash work/tests/run-sub-banner-gesture-tests.sh \ + -only-testing:GestureUITests/SubBannerGestureUITests/testManualBoundaryFrames \ + -only-testing:GestureUITests/SubBannerGestureUITests/testSwipeDoesNotTap +xcrun xcresulttool get test-results summary --path "$RUN_TMP/no-count.xcresult" +xcrun xcresulttool export attachments --test-id 'SubBannerGestureUITests/testManualBoundaryFrames()' \ + --path "$RUN_TMP/no-count.xcresult" --output-path "$RUN_TMP/attachments" +``` + +- `--ui`: exit 0, production carousel/model/UIKit opener iOS16.6 typecheck PASS(설치된 Kingfisher 및 실제 theme/font). +- 전용 iPhone SE3, iOS18.3 runtime(실제 OS18.3.1/22D8075), x86_64. `TEST SUCCEEDED`, xcodebuild exit 0, **2 tests / 0 failures / 0 skipped**. +- `testManualBoundaryFrames`: PASS, 15.416초. 실제 좌표 입력으로 1→3→1 양방향 경계 이동, 페이지 접근성 라벨 유지, callback0 확인. +- `testSwipeDoesNotTap`: PASS, 9.295초. 실제 swipe 후 callback0, 현재 배너 tap 후 callback1/ID2 확인. 합계 24.711초. +- native attachment 1장을 [no-count-native.png](no-count-native.png)로 보존하고 직접 열어 확인했다. `BANNER 3`은 fixture 이미지 내부 글자이며 카운터가 아니다. 이미지 우측 상단은 파란 이미지 그대로이고 카운트 capsule/텍스트가 없다. 가로 전체 폭과 아래 콘텐츠가 정상 표시된다. +- PNG SHA256: `038f3787462ec58907ed31ca22bdb14a14166cde8dcd68188c42d9f77d610c14`(750×1334). 원본 attachment `3DC829EB-E276-482A-ABF6-939AC29FEAFE.png`를 그대로 복사했다. + +## 정리 및 검증 한계 +- 소유 앱 `invalid.sodalive.gesturefix.Gesture`, runner `invalid.sodalive.gesturefix.GestureUITests.xctrunner`에 각각 `xcrun simctl uninstall "$SB_SIMULATOR_ID" ` exit 0. `simctl listapps`에서 두 bundle 부재 확인 PASS. +- `xcrun simctl shutdown "$SB_SIMULATOR_ID"` 및 `xcrun simctl delete "$SB_SIMULATOR_ID"` exit 0. 소유 device 디렉터리 부재 확인 PASS. `ps -p 35102,35139,35180,34167 -o pid=,comm=` 출력 없음: runner35102, 앱35139/35180, 해당 simulator launchd34167 모두 종료. +- 증거 보존 및 최종 소스 해시 재확인 후 `rm -rf "$RUN_TMP"`, `test ! -e "$RUN_TMP"` PASS. root 하위 build/xcresult/임시 프레임/cache와 `OWNED-RESOURCES.txt` 메모까지 제거했다. 최종 출력: `PASS: owned root/cache/note and simulator directory absent`. 공유 Kingfisher/DerivedData cache와 기존 device는 삭제하지 않았다. +- 새 의존성·테스트·scaffolding·worktree·commit·push 없음. 기존 테스트 2개와 UI typecheck만 실행했으며 전체11개 suite/전체 앱 빌드/실서비스/실기기/VoiceOver 자체 조작은 재검증하지 않았다. LSP 도구는 제공되지 않아 native Swift typecheck 및 harness build/test로 검증했다. diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/no-count/no-count-native.png b/docs/20261029_추천_서브_배너/reviews/evidence/no-count/no-count-native.png new file mode 100644 index 00000000..fe744fe3 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/no-count/no-count-native.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/parent-native-final.png b/docs/20261029_추천_서브_배너/reviews/evidence/parent-native-final.png new file mode 100644 index 00000000..89b99e8c Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/parent-native-final.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/parent-native-smoke.png b/docs/20261029_추천_서브_배너/reviews/evidence/parent-native-smoke.png new file mode 100644 index 00000000..c4748153 Binary files /dev/null and b/docs/20261029_추천_서브_배너/reviews/evidence/parent-native-smoke.png differ diff --git a/docs/20261029_추천_서브_배너/reviews/evidence/parent-verification.md b/docs/20261029_추천_서브_배너/reviews/evidence/parent-verification.md new file mode 100644 index 00000000..e12c0825 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/evidence/parent-verification.md @@ -0,0 +1,35 @@ +# 상위 실행자 검증 기록 + +## 직접 실행 결과 +- `bash work/tests/check-sub-banners.sh`: exit 0. 실제 API/repository/VM/DTO/opener 검사 5개 PASS 출력 확인. +- `bash work/tests/check-sub-banners.sh --ui`: exit 0. 실제 Kingfisher/UIKit/SwiftUI 및 iOS16.6 대상 타입 검사 통과. +- `for script in work/tests/check-*.sh; do bash "$script" || exit "$?"; done`: 600초 제한 내 exit 0. 신규 검사 포함 16개 스크립트가 한 루프에서 모두 통과했다. 마지막 검사 `PASS: 113 assertions` 확인. +- 양 앱 Debug 시뮬레이터 빌드 exit 0. `SodaLive` 로그의 `** BUILD SUCCEEDED **`도 확인했다. 최초 격리 DerivedData의 전체 SDK 재빌드는 600초 timeout으로 중단되어 성공으로 계산하지 않았다. 이후 기존 Xcode 캐시를 사용했으며 공유 캐시는 삭제하지 않는다. + +```bash +xcodebuild -workspace SodaLive.xcworkspace -scheme SodaLive -configuration Debug \ + -sdk iphonesimulator -destination 'platform=iOS Simulator,id=482F7827-2E3A-4559-BAF4-48B68CC5725B' \ + -disableAutomaticPackageResolution CODE_SIGNING_ALLOWED=NO ONLY_ACTIVE_ARCH=YES build +# 동일 명령의 scheme만 SodaLive-dev로 바꾼 빌드도 exit 0 +``` + +## 직접 Native 재실행 +- QA worker의 원본 `native/source/Doubles.swift`, `Harness.swift`와 현재 production View/캐러셀/DTO/opener/테마를 직접 `xcrun --sdk iphonesimulator swiftc`로 컴파일했다. 별도 product 수정이나 웹 모사는 없다. +- 부모 소유 앱 `qa.sodalive.subbanner.parent.x6gleo`, PID 4387을 소유 simulator에서 설치·실행했다. +- `xcrun simctl io 482F7827-2E3A-4559-BAF4-48B68CC5725B screenshot ...`으로 [parent-native-smoke.png](parent-native-smoke.png), [parent-native-final.png](parent-native-final.png)를 직접 캡처했다. smoke에서 home-single-wide, 01/01, active creators 아래 전체 폭 이미지를 직접 읽어 확인했다. +- 실행 중 생성된 actions.json에서 home-first/home-second/content-first/content-second 이벤트를 확인했다. 전체 worker matrix를 재검증했다고 주장하지 않으며 상세 42장/geometry 판정은 [native/README.md](native/README.md)를 따른다. +- 부모 smoke 앱은 terminate/uninstall 후 `ps -p 4387` 부재를 확인했다: `Parent native app teardown: PASS`. + +## 범위와 한계 +- 실제 배너/두 View composition은 production 소스이며 주변 섹션과 VM은 명시 QA 대역이다. live 추천 API, 전체 앱의 손가락 탭·swipe/외부 앱 이동, 실기기 검증은 수행하지 않았다. +- `lsp_diagnostics` 도구가 제공되지 않아 LSP 실행 대신 실제 Swift 타입 검사와 두 전체 앱 빌드를 수행했다. 이를 LSP PASS로 바꾸어 보고하지 않는다. +- 저장소 diff, 신규 production 3파일, CLI tests, harness Swift와 실행 스크립트 및 native 보고서를 읽었다. 기존 main BannerCarousel와 공유 AuthPlugin은 diff에 없다. +- 독립 리뷰 및 최종 root/device 정리 결과는 후속 기록으로 추가한다. + +## 후속 확인 +- 두 추천 진입 경로를 추가 확인했다. `MainView.swift:204`의 home/content 분기는 직접 화면을 구성하고, `MainHomeView.swift:67`과 `MainContentView.swift:45`의 추천 탭에도 로그인 차단이 없다. 기존 following/detail 전용 로그인 guard는 변경하지 않았다. +- 부모의 final smoke PNG는 21개 목록 중 19/21이 자동으로 표시된 시점이다. worker의 21번째/순환 증거와 구분한다. +- 부모 소유 실패 build DerivedData, smoke app bundle, native module cache는 제거 후 부재 확인했다. 리뷰가 읽는 diff/log는 최종 리뷰가 끝날 때까지 보존한다. +- REV-P1-001 대응: 마지막 제스처 변경의 SHA256 `7e25db3076d545ffc61ea3a1c5585e7b8a617964ef406d7570286d75a75e010a`로 SodaLive scheme을 위 명령 그대로 재빌드하여 exit0을 확인했다. 집중 production checks도 5개 PASS/exit0 재확인했다. 기존 42장의 캡처는 이전 버전 증거로 보존하고 최신 native matrix는 별도 `native-final/`에 기록한다. +- 최종 동일 reviewer가 현재 source/snapshot 해시 및 native-final 전후/최종 manifest, 원본 경로 컴파일·42PNG·cleanup을 검토하여 REV-P1-001 ADDRESSED / APPROVE 판정했다. 추가 제품 수정은 없었다. +- 최종 소유 자원 정리: parent simulator shutdown/delete 및 device 디렉터리 부재, own root `sub-banner-ios-run.x6GLEo` 삭제 및 부재, 임시 note 삭제를 확인했다. `Owned run root/cache and simulator absent: PASS`. 임시 build 로그는 reviewer 확인 뒤 삭제했으며 명령·exit·검토 결과는 이 문서에 보존한다. shared Xcode cache와 다른 simulator는 삭제하지 않았다. diff --git a/docs/20261029_추천_서브_배너/reviews/phase1-sub-banner-integration.md b/docs/20261029_추천_서브_배너/reviews/phase1-sub-banner-integration.md new file mode 100644 index 00000000..583d8e09 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/phase1-sub-banner-integration.md @@ -0,0 +1,55 @@ +# 추천 서브 배너 통합 리뷰 + +## 1. 리뷰 정보 +| 항목 | 내용 | +|---|---| +| 대상 | P1-T1 구현 및 P2-GATE 증거 | +| 기준 | 미커밋 working tree, 신규 production 3파일 포함 | +| 일자 | 2026-10-30 | +| 리뷰어 | Oracle, 단일 독립 읽기 전용 리뷰 | +| 기준 문서 | ../prd.md, ../plan-task.md | +| 상태 | 수정 검증 완료: APPROVE, 남은 차단 항목 없음 | + +## 2. 목적과 범위 +두 추천 GET/auth/status, DTO/precision/filter, opener, 실제 View composition/캐러셀, CLI checks 및 native 캡처/geometry를 검토했다. 기존 인증 plugin·main banner와 인접 경로도 읽었다. reviewer는 제품이나 보고서 파일을 수정하지 않았으며 이 문서는 상위 실행자가 반환 판정을 기록한 것이다. + +## 3. 판정 기준 +확정 요구사항 위반 또는 현재 checkout을 검증했다는 주장을 성립시키지 못하는 증거 문제를 차단한다. 공개된 physical tap/swipe·실서비스 미검증 범위 자체를 새로운 제품 요구사항으로 추가하지 않는다. + +## 4. 검토 근거와 결과 +- HTTP200와 success 확인, 익명 헤더 생략, follow/auth 보존, 기존 데이터 유지가 코드와 tests에 부합한다. +- Int 직접 디코딩, enum placement, 순서 보존 및 콘텐츠 isEmpty, 날짜/20개 cap 없음 확인. +- 원문 URI/scheme/host 검증 및 iOS 우선/웹 1회 대체, Android 실행 없음 확인. +- 두 화면의 지정 위치/조건부 삽입, 캐러셀 안전 index/현재 URL 비율/task 취소/swipe 흐름 확인. +- native contact sheet 4개와 양 화면 first/second/empty/single/OS 및 부모 PNG를 읽었다. 기존 이미지의 폭375, 높이125→187.5, s24, empty 추가 gap0 관측 자체는 유효하다. +- 두 build log의 BUILD SUCCEEDED 확인. 전체16개 check 단일 루프 PASS는 리뷰 dispatch 이후 상위 실행자가 추가 확인했고 evidence/parent-verification.md에 기록했다. + +## 5. 발견 사항 +| ID | 심각도 | 상태 | 제목 | 후속 Task | +|---|---|---|---|---| +| REV-P1-001 | High, 검증 Gate | 수정 완료 | 현재 캐러셀과 native 캡처 소스 해시 불일치 | P2-R1 | + +확정된 제품 기능·보안 결함은 없음. 검증 증거 불일치 1건. + +## 6. REV-P1-001 상세 +- 기존 native/source-hashes-final.json의 캐러셀 SHA256: `1bf03b8b1e05310acce09f12b2f09b35af3587f4a7d486b22945ec502cfb506d`. +- 현재 production 캐러셀 SHA256: `7e25db3076d545ffc61ea3a1c5585e7b8a617964ef406d7570286d75a75e010a`. +- 기존 native 최종 해시 기록 이후 캐러셀에 제스처 처리가 변경됐다. `unchanged=true`는 당시 실행 전후만 증명하며 현재 checkout까지 증명하지 않는다. +- 부모 smoke는 현재 코드 이후 실행됐지만 전체 matrix와 소스 해시 연결이 부족하다. 과거 해시/증거를 덮어쓰지 않고 최신 소스 재검증 증거를 별도로 추가한다. +- 관련 기준: PRD SB-013/014/019, plan P2-GATE 현재 코드 native 검증. + +## 7. 후속 작업 +P2-R1에 현재 소스 snapshot/hash와 변경 범위를 기록하고 native matrix 재실행 및 SodaLive build 재확인을 수행한다. 제품 수정은 필요하지 않다. + +## 8. 종료 판정 +초기 판정 REQUEST_CHANGES. P2-R1 증거를 같은 reviewer에게 제한된 재검토로 제출한다. +잔여 한계: physical tap/swipe/세로 scroll 공존, 실제 UIApplication.open/OS background, Reduce Motion ON, 실서비스·전체 앱 UI는 미검증이다. + +## 9. 후속 검증 기록 +- 상위 실행자도 현재 해시와 수정 시각(2026-10-29 23:53:09)을 확인했다. parent smoke/final PNG는 2026-10-30 00:03:26/00:06:28, SodaLive-dev build 완료는 2026-10-29 23:58:03으로 이후 실행이다. +- 다음 재검증은 별도 `evidence/native-final/`에 보존하고 기존 `native/`는 변경하지 않는다. +- 변경 대조: 이전에 읽은 캐러셀은 `highPriorityGesture`, 드래그 방향 구분 없는 offset/onEnded, 드래그 중 자동 진행 허용이었다. 현재는 `@GestureState isSwiping`·tap guard를 추가하고 `simultaneousGesture` 및 수평 우세 조건을 사용하며 드래그 중 자동 진행을 건너뛴다. 이는 동작 변경이므로 단순 공백 변경으로 증거를 승계하지 않는다. 배치·이미지 비율·DTO/opener 변경은 없다. +- 2026-10-30 상위 재검증: 현재 SHA256 `7e25db3076d545ffc61ea3a1c5585e7b8a617964ef406d7570286d75a75e010a`를 출력하며 SodaLive Debug simulator build exit0, 집중 production checks 5개 PASS/exit0. 기록 `build-sodalive-final.log`; 최신 native matrix는 별도 실행 중. +- 2026-10-30 최종 Oracle delta 재검토: **REV-P1-001 ADDRESSED, APPROVE, 남은 차단 항목 없음**. 현재 source/snapshot/전후최종 manifest의 동일 해시, 원본 production 경로 컴파일, 최신42PNG·시나리오·cleanup, 최종 build의 BUILD SUCCEEDED를 reviewer가 직접 확인했다. 기존 기능 리뷰를 반복하거나 범위를 확대하지 않았다. +- 최신 증거: [native-final/README.md](evidence/native-final/README.md), [source-hashes-final.json](evidence/native-final/source-hashes-final.json), [provenance-after.json](evidence/native-final/provenance-after.json). 이전 native 증거는 보존한다. +- 상위 실행자도 현재 source와 snapshot cmp PASS 및 최종 build log를 확인했고, 소유 simulator/root/cache를 정리했다. 실기기·physical tap/swipe·실서비스 등 기존 공개 한계는 그대로다. diff --git a/docs/20261029_추천_서브_배너/reviews/phase3-sub-banner-gesture.md b/docs/20261029_추천_서브_배너/reviews/phase3-sub-banner-gesture.md new file mode 100644 index 00000000..6c969248 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/phase3-sub-banner-gesture.md @@ -0,0 +1,53 @@ +# Phase 3: 스와이프 오탭·경계 애니메이션 리뷰 + +## 1. 리뷰 정보 +| 항목 | 내용 | +|---|---| +| 대상 | P3-T1, SB-021/022 | +| 일자 | 2026-10-30 | +| 리뷰어 | Oracle, 단일 독립 읽기 전용 리뷰; 상위 실행자가 판정 기록 | +| 기준 | working tree와 이전 native-final snapshot의 component diff | +| 상태 | APPROVE, 차단 항목 없음 | + +## 2. 목적과 범위 +swipe 후 이전 배너 링크 실행과 마지막→첫번째 순간 교체를 확인했다. product 수정은 `SubBannerCarousel.swift` 하나이며 기존 main carousel, 두 화면 조립, 모델/API/opener는 바꾸지 않았다. native XCTest 하네스·UI tests·실행기와 RED/GREEN·실제 렌더 프레임을 함께 검토했다. + +## 3. 판정 기준 +실제 swipe/인식된 짧은 drag/세로 scroll에서 tap callback이 없어야 하고, 정상 탭은 표시 항목에 한 번만 전달되어야 한다. 순환은 같은 방향의 중간 프레임을 가지며 논리 페이지·이미지 비율·탭 ID·목록 교체 안전성을 유지해야 한다. + +## 4. 검토 근거 +- [구현·RED/GREEN 보고서](evidence/gesture-fix/implementation.md), native 로그, test-summary와 frame-analysis.json. +- 현재 component와 GREEN snapshot SHA256: `b3e4c5333cf8249ccd7773b5cabd372efb5a5565323bd7778034e110386151fd`, source/snapshot cmp 동일. +- baseline actual swipe: 페이지 이동 후 `taps=1;last=1` assertion 실패. 수정본 actual coordinate tests 11/11 PASS. +- baseline 경계 0049→0050 전체 교체와 수정본 automatic0063/manual0029/forward0032 중간 PNG를 reviewer가 직접 읽었다. + +## 5. 발견 사항 +확정 차단 결함 없음. **APPROVE**, 신뢰도 높음(두 결함 범위 한정). + +## 6. 주요 판단 +- `PrimitiveButtonStyle`의 `drag.exclusively(before: TapGesture())`로 Button release와 GestureState 초기화 순서에 의존하지 않고 인식을 분리한다. `.global` 좌표로 이동하는 라벨의 drag translation 오류를 제거하고 부모 scroll과 simultaneous 참여를 유지한다. +- 복제 endpoint identity와 display/logical index를 분리한다. 인접 복제로 애니메이션한 뒤 같은 이미지로만 정렬한다. revision/index guard로 교체·이탈 시 이전 정렬 callback을 무효화한다. +- accessibility Button/activation/adjustable action 및 현재 페이지만 노출하는 처리가 보존됐다. VoiceOver 자체 조작 완료를 의미하지는 않는다. + +## 7. 상위 실행자 재검증 +| 실행 | 결과 | +|---|---| +| 명시 simulator ID로 native suite 재실행 | exit0, 11 tests/0 failures/0 skipped, 134.827초; [parent-test-summary.json](evidence/gesture-fix/parent-test-summary.json) | +| 전체 `work/tests/check-*.sh` 순차 루프 | 16개 단일 루프 exit0, 마지막 113 assertions PASS | +| SodaLive / SodaLive-dev Debug simulator build | 각각 exit0, 최종 product hash로 컴파일 | +| 현재 product와 native GREEN snapshot | cmp exit0, SHA 동일 | +| runner bash syntax·명시 환경 실행 | PASS; 임시 UDID 고정 제거, `SB_SIMULATOR_ID`로 재실행 | + +```bash +SB_SIMULATOR_ID= \ +KINGFISHER_MODULE_PATH= \ +bash work/tests/run-sub-banner-gesture-tests.sh +``` + +runner가 출력하는 root에는 xcresult/log가 남는다. 증거 확인 뒤 호출자가 해당 root와 테스트 앱만 정리한다. x86_64 simulator와 설치된 Kingfisher 빌드 산출물이 필요한 로컬 검사다. + +## 8. 종료 판정 +Oracle APPROVE 후 상위 native suite/전체 checks/양 앱 build 결과를 연결했다. 소유 앱2개 uninstall 및 simulator shutdown/delete, device 경로 부재를 확인했다. 상위 root/cache·임시 note 정리 영수증은 plan에 기록한다. + +## 9. 잔여 검증 범위 +실제 input은 iPhone SE3/iOS18.3.1 simulator다. 실기기·외부 서비스/링크 앱 launch·VoiceOver 직접 조작·지연0ms 초고속 재입력은 미검증이다. LSP 도구는 제공되지 않아 실제 컴파일/typecheck/build/test를 사용했다. 기존 API·화면 배치·5초 간격은 유지했다. diff --git a/docs/20261029_추천_서브_배너/reviews/phase4-sub-banner-count.md b/docs/20261029_추천_서브_배너/reviews/phase4-sub-banner-count.md new file mode 100644 index 00000000..68669792 --- /dev/null +++ b/docs/20261029_추천_서브_배너/reviews/phase4-sub-banner-count.md @@ -0,0 +1,17 @@ +# Phase 4: 서브 배너 카운트 제거 리뷰 + +## 리뷰 정보·범위 +2026-10-30, P4-T1/DEC-009. 단일 독립 읽기 전용 리뷰로 이전 Phase3 snapshot과 현재 component, native PNG 및 검증 기록을 대조했다. 판정은 **APPROVE**다. + +## 판정 기준 +시각적 우측 상단 배지만 없어야 한다. 접근성 페이지 라벨·스와이프/순환·타이머·원본 비율·메인 배너는 그대로여야 한다. + +## 검토 근거 +- diff는 overlay 6줄과 고립 helper/구분 공백 17줄 삭제뿐이며 나머지 소스 동일. +- [native PNG](evidence/no-count/no-count-native.png)에서 우측 상단은 이미지 그대로이고 카운트가 없다. 이미지 내부 BANNER 3 글자는 fixture 내용이다. +- [실행 기록](evidence/no-count/README.md): UI typecheck exit0, 실제 좌표 입력 테스트 2/2 PASS, 소스/캡처 해시 일치, 소유 QA 자원 부재 확인. +- 상위 실행자도 현재 소스와 PNG를 읽고 UI typecheck를 재실행해 exit0을 확인했다. `git diff --check`와 소유 root/device 부재 검사 PASS. + +## 발견 사항·종료 판정 +확정 발견 사항 없음. APPROVE, 수정 필요 항목 없음. +순수 렌더링 삭제에 맞춰 전체 앱 빌드·전체 회귀·실기기·VoiceOver 자체 조작은 재실행하지 않았다.