diff --git a/.eslintrc.js b/.eslintrc.js
index 5a6b0ec2d..22bb7f714 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -184,7 +184,7 @@ module.exports = {
},
//disable some rules for certain files
overrides: [{
- //these files are getting deleted soon, so ingore the eslint warnings for now
+ //these files are getting deleted soon, so ignore the eslint warnings for now
files: ['src/brsTypes/**/*.ts'],
rules: {
'@typescript-eslint/no-invalid-this': 'off',
diff --git a/.vscode/settings.json b/.vscode/settings.json
index b2f602f22..d96421ddb 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -46,5 +46,6 @@
"a11y-structure": "ignore",
"a11y-mouse-events-have-key-events": "ignore",
"a11y-missing-content": "ignore",
+ "a11y-click-events-have-key-events": "ignore"
}
-}
\ No newline at end of file
+}
diff --git a/images/icons/inspect-active.svg b/images/icons/inspect-active.svg
new file mode 100644
index 000000000..0de8c1f5e
--- /dev/null
+++ b/images/icons/inspect-active.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 99df35ba5..1a7bd8f3d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -23,14 +23,16 @@
"glob": "^7.1.3",
"hex-rgb": "^5.0.0",
"iconv-lite": "0.4.24",
+ "jpeg-js": "^0.4.4",
"just-throttle": "^4.0.1",
"net": "^1.0.2",
"node-cache": "^4.2.0",
"node-ssdp": "^4.0.0",
+ "pngjs": "^7.0.0",
"pretty-bytes": "^5.6.0",
"roku-debug": "^0.18.3",
"roku-deploy": "^3.9.3",
- "roku-test-automation": "^2.0.0-beta.11",
+ "roku-test-automation": "^2.0.0-beta.14",
"semver": "^7.1.3",
"source-map": "^0.7.3",
"thenby": "^1.3.4",
@@ -86,7 +88,7 @@
},
"engines": {
"node": "^12.12.0",
- "vscode": "^1.53.0"
+ "vscode": "^1.57.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
@@ -1172,20 +1174,20 @@
}
},
"node_modules/@types/express": {
- "version": "4.17.14",
- "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz",
- "integrity": "sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==",
+ "version": "4.17.17",
+ "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz",
+ "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==",
"dependencies": {
"@types/body-parser": "*",
- "@types/express-serve-static-core": "^4.17.18",
+ "@types/express-serve-static-core": "^4.17.33",
"@types/qs": "*",
"@types/serve-static": "*"
}
},
"node_modules/@types/express-serve-static-core": {
- "version": "4.17.31",
- "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz",
- "integrity": "sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==",
+ "version": "4.17.33",
+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz",
+ "integrity": "sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==",
"dependencies": {
"@types/node": "*",
"@types/qs": "*",
@@ -1212,9 +1214,9 @@
}
},
"node_modules/@types/http-proxy": {
- "version": "1.17.9",
- "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz",
- "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==",
+ "version": "1.17.10",
+ "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.10.tgz",
+ "integrity": "sha512-Qs5aULi+zV1bwKAg5z1PWnDXWmsn+LxIvUGv6E2+OOMYhclZMO+OXd9pYVf2gLykf2I7IV2u7oTHwChPNsvJ7g==",
"dependencies": {
"@types/node": "*"
}
@@ -1295,9 +1297,9 @@
"dev": true
},
"node_modules/@types/serve-static": {
- "version": "1.15.0",
- "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz",
- "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==",
+ "version": "1.15.1",
+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.1.tgz",
+ "integrity": "sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==",
"dependencies": {
"@types/mime": "*",
"@types/node": "*"
@@ -2969,9 +2971,9 @@
}
},
"node_modules/content-type": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
- "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
"engines": {
"node": ">= 0.6"
}
@@ -4864,29 +4866,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/get-stack-trace": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/get-stack-trace/-/get-stack-trace-2.1.1.tgz",
- "integrity": "sha512-dhqSDD9lHU/6FvIZ9KbXGmVK6IKr9ZskZtNOUvhlCiONlnqatu4FmAeRbxCfJJVuQ0NWfz6dAbibKQg19B7AmQ==",
- "dependencies": {
- "bluebird": "^3.7.1",
- "source-map": "^0.8.0-beta.0"
- },
- "engines": {
- "node": ">=8.0"
- }
- },
- "node_modules/get-stack-trace/node_modules/source-map": {
- "version": "0.8.0-beta.0",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz",
- "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==",
- "dependencies": {
- "whatwg-url": "^7.0.0"
- },
- "engines": {
- "node": ">= 8"
- }
- },
"node_modules/get-stream": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
@@ -6153,6 +6132,11 @@
"integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==",
"dev": true
},
+ "node_modules/jpeg-js": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz",
+ "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg=="
+ },
"node_modules/js-sdsl": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz",
@@ -6486,11 +6470,6 @@
"integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==",
"dev": true
},
- "node_modules/lodash.sortby": {
- "version": "4.7.0",
- "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
- "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA=="
- },
"node_modules/lodash.upperfirst": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz",
@@ -7952,6 +7931,14 @@
"node": ">=8"
}
},
+ "node_modules/pngjs": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz",
+ "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==",
+ "engines": {
+ "node": ">=14.19.0"
+ }
+ },
"node_modules/portfinder": {
"version": "1.0.32",
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz",
@@ -8778,14 +8765,13 @@
}
},
"node_modules/roku-test-automation": {
- "version": "2.0.0-beta.11",
- "resolved": "https://registry.npmjs.org/roku-test-automation/-/roku-test-automation-2.0.0-beta.11.tgz",
- "integrity": "sha512-YQ+mmqjKr9VTl8viotciGBFt80FT5KzoiT/MZ+pG8fb7OaBuRDlqy91+lmu6qkjblMKjbgJ/2SaURIuh+LgckA==",
+ "version": "2.0.0-beta.14",
+ "resolved": "https://registry.npmjs.org/roku-test-automation/-/roku-test-automation-2.0.0-beta.14.tgz",
+ "integrity": "sha512-q/1nSyArDopEyp3phq3s500n6hNFVvBhPFrAAcMWfCvvIx2pqYM1rcBZ4piNcqZfUX4RENgVHDgi9pPzCrlWjg==",
"dependencies": {
"@suitest/types": "^4.6.0",
"ajv": "^6.12.6",
"fs-extra": "^7.0.1",
- "get-stack-trace": "^2.1.1",
"http-network-proxy": "^1.0.11",
"needle": "^2.9.1",
"path": "^0.12.7",
@@ -9872,14 +9858,6 @@
"node": ">=0.8"
}
},
- "node_modules/tr46": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
- "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==",
- "dependencies": {
- "punycode": "^2.1.0"
- }
- },
"node_modules/traverse-chain": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/traverse-chain/-/traverse-chain-0.1.0.tgz",
@@ -10689,11 +10667,6 @@
"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.8.tgz",
"integrity": "sha512-obtSWTlbJ+a+TFRYGaUumtVwb+InIUVI0Lu0VBUAPmj2cU5JutEXg3xUE0c2J5Tcy7h2DEKVJBFi+Y9ZSFzzPQ=="
},
- "node_modules/webidl-conversions": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
- "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="
- },
"node_modules/websocket-driver": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
@@ -10717,16 +10690,6 @@
"node": ">=0.8.0"
}
},
- "node_modules/whatwg-url": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
- "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==",
- "dependencies": {
- "lodash.sortby": "^4.7.0",
- "tr46": "^1.0.1",
- "webidl-conversions": "^4.0.2"
- }
- },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -11899,20 +11862,20 @@
}
},
"@types/express": {
- "version": "4.17.14",
- "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz",
- "integrity": "sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==",
+ "version": "4.17.17",
+ "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz",
+ "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==",
"requires": {
"@types/body-parser": "*",
- "@types/express-serve-static-core": "^4.17.18",
+ "@types/express-serve-static-core": "^4.17.33",
"@types/qs": "*",
"@types/serve-static": "*"
}
},
"@types/express-serve-static-core": {
- "version": "4.17.31",
- "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz",
- "integrity": "sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==",
+ "version": "4.17.33",
+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz",
+ "integrity": "sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==",
"requires": {
"@types/node": "*",
"@types/qs": "*",
@@ -11939,9 +11902,9 @@
}
},
"@types/http-proxy": {
- "version": "1.17.9",
- "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz",
- "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==",
+ "version": "1.17.10",
+ "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.10.tgz",
+ "integrity": "sha512-Qs5aULi+zV1bwKAg5z1PWnDXWmsn+LxIvUGv6E2+OOMYhclZMO+OXd9pYVf2gLykf2I7IV2u7oTHwChPNsvJ7g==",
"requires": {
"@types/node": "*"
}
@@ -12022,9 +11985,9 @@
"dev": true
},
"@types/serve-static": {
- "version": "1.15.0",
- "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz",
- "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==",
+ "version": "1.15.1",
+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.1.tgz",
+ "integrity": "sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==",
"requires": {
"@types/mime": "*",
"@types/node": "*"
@@ -13274,9 +13237,9 @@
}
},
"content-type": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
- "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="
},
"convert-source-map": {
"version": "1.9.0",
@@ -14723,25 +14686,6 @@
"resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz",
"integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ=="
},
- "get-stack-trace": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/get-stack-trace/-/get-stack-trace-2.1.1.tgz",
- "integrity": "sha512-dhqSDD9lHU/6FvIZ9KbXGmVK6IKr9ZskZtNOUvhlCiONlnqatu4FmAeRbxCfJJVuQ0NWfz6dAbibKQg19B7AmQ==",
- "requires": {
- "bluebird": "^3.7.1",
- "source-map": "^0.8.0-beta.0"
- },
- "dependencies": {
- "source-map": {
- "version": "0.8.0-beta.0",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz",
- "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==",
- "requires": {
- "whatwg-url": "^7.0.0"
- }
- }
- }
- },
"get-stream": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
@@ -15656,6 +15600,11 @@
}
}
},
+ "jpeg-js": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz",
+ "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg=="
+ },
"js-sdsl": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz",
@@ -15938,11 +15887,6 @@
"integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==",
"dev": true
},
- "lodash.sortby": {
- "version": "4.7.0",
- "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
- "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA=="
- },
"lodash.upperfirst": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz",
@@ -17068,6 +17012,11 @@
}
}
},
+ "pngjs": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz",
+ "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow=="
+ },
"portfinder": {
"version": "1.0.32",
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz",
@@ -17696,14 +17645,13 @@
}
},
"roku-test-automation": {
- "version": "2.0.0-beta.11",
- "resolved": "https://registry.npmjs.org/roku-test-automation/-/roku-test-automation-2.0.0-beta.11.tgz",
- "integrity": "sha512-YQ+mmqjKr9VTl8viotciGBFt80FT5KzoiT/MZ+pG8fb7OaBuRDlqy91+lmu6qkjblMKjbgJ/2SaURIuh+LgckA==",
+ "version": "2.0.0-beta.14",
+ "resolved": "https://registry.npmjs.org/roku-test-automation/-/roku-test-automation-2.0.0-beta.14.tgz",
+ "integrity": "sha512-q/1nSyArDopEyp3phq3s500n6hNFVvBhPFrAAcMWfCvvIx2pqYM1rcBZ4piNcqZfUX4RENgVHDgi9pPzCrlWjg==",
"requires": {
"@suitest/types": "^4.6.0",
"ajv": "^6.12.6",
"fs-extra": "^7.0.1",
- "get-stack-trace": "^2.1.1",
"http-network-proxy": "^1.0.11",
"needle": "^2.9.1",
"path": "^0.12.7",
@@ -18547,14 +18495,6 @@
"punycode": "^2.1.1"
}
},
- "tr46": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
- "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==",
- "requires": {
- "punycode": "^2.1.0"
- }
- },
"traverse-chain": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/traverse-chain/-/traverse-chain-0.1.0.tgz",
@@ -19181,11 +19121,6 @@
"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.8.tgz",
"integrity": "sha512-obtSWTlbJ+a+TFRYGaUumtVwb+InIUVI0Lu0VBUAPmj2cU5JutEXg3xUE0c2J5Tcy7h2DEKVJBFi+Y9ZSFzzPQ=="
},
- "webidl-conversions": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
- "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="
- },
"websocket-driver": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
@@ -19203,16 +19138,6 @@
"integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==",
"dev": true
},
- "whatwg-url": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
- "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==",
- "requires": {
- "lodash.sortby": "^4.7.0",
- "tr46": "^1.0.1",
- "webidl-conversions": "^4.0.2"
- }
- },
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
diff --git a/package.json b/package.json
index e02f812b3..5292743d0 100644
--- a/package.json
+++ b/package.json
@@ -14,7 +14,7 @@
"multi-root-ready"
],
"engines": {
- "vscode": "^1.53.0",
+ "vscode": "^1.57.0",
"node": "^12.12.0"
},
"repository": {
@@ -64,14 +64,16 @@
"glob": "^7.1.3",
"hex-rgb": "^5.0.0",
"iconv-lite": "0.4.24",
+ "jpeg-js": "^0.4.4",
"just-throttle": "^4.0.1",
"net": "^1.0.2",
"node-cache": "^4.2.0",
"node-ssdp": "^4.0.0",
+ "pngjs": "^7.0.0",
"pretty-bytes": "^5.6.0",
"roku-debug": "^0.18.3",
"roku-deploy": "^3.9.3",
- "roku-test-automation": "^2.0.0-beta.11",
+ "roku-test-automation": "^2.0.0-beta.14",
"semver": "^7.1.3",
"source-map": "^0.7.3",
"thenby": "^1.3.4",
@@ -197,6 +199,12 @@
"name": "SceneGraph Inspector",
"type": "webview"
},
+ {
+ "id": "rokuDeviceView",
+ "contextualTitle": "Roku",
+ "name": "Device View",
+ "type": "webview"
+ },
{
"id": "rokuRegistryView",
"name": "Roku Registry",
@@ -257,6 +265,31 @@
"command": "extension.brightscript.rokuRegistry.clearRegistry",
"when": "view == rokuRegistryView",
"group": "navigation"
+ },
+ {
+ "command": "extension.brightscript.rokuDeviceView.refreshScreenshot",
+ "when": "view == rokuDeviceView && !brightscript.rokuDeviceView.enableScreenshotCapture",
+ "group": "navigation@1"
+ },
+ {
+ "command": "extension.brightscript.rokuDeviceView.resumeScreenshotCapture",
+ "when": "view == rokuDeviceView && !brightscript.rokuDeviceView.enableScreenshotCapture && !brightscript.rokuDeviceView.isInspectingNodes",
+ "group": "navigation@2"
+ },
+ {
+ "command": "extension.brightscript.rokuDeviceView.pauseScreenshotCapture",
+ "when": "view == rokuDeviceView && brightscript.rokuDeviceView.enableScreenshotCapture && !brightscript.rokuDeviceView.isInspectingNodes",
+ "group": "navigation@2"
+ },
+ {
+ "command": "extension.brightscript.rokuDeviceView.enableNodeInspector",
+ "when": "view == rokuDeviceView && brightscript.isOnDeviceComponentAvailable && !brightscript.rokuDeviceView.isInspectingNodes",
+ "group": "navigation@3"
+ },
+ {
+ "command": "extension.brightscript.rokuDeviceView.disableNodeInspector",
+ "when": "view == rokuDeviceView && brightscript.isOnDeviceComponentAvailable && brightscript.rokuDeviceView.isInspectingNodes",
+ "group": "navigation@3"
}
],
"commandPalette": []
@@ -2264,6 +2297,11 @@
"title": "Press the PowerOff button on the Roku remote",
"category": "Brightscript"
},
+ {
+ "command": "extension.brightscript.pressPowerOn",
+ "title": "Press the PowerOn button on the Roku remote",
+ "category": "Brightscript"
+ },
{
"command": "extension.brightscript.pressChannelUp",
"title": "Press the ChannelUp button on the Roku remote",
@@ -2382,6 +2420,42 @@
"command": "extension.brightscript.languageServer.info",
"title": "View BrighterScript LanguageServer Info",
"category": "BrighterScript"
+ },
+ {
+ "command": "extension.brightscript.rokuDeviceView.enableNodeInspector",
+ "title": "Inspect nodes",
+ "category": "BrighterScript",
+ "icon": "$(inspect)"
+ },
+ {
+ "command": "extension.brightscript.rokuDeviceView.disableNodeInspector",
+ "title": "Stop inspecting nodes",
+ "category": "BrighterScript",
+ "icon": "./images/icons/inspect-active.svg"
+ },
+ {
+ "command": "extension.brightscript.rokuDeviceView.pauseScreenshotCapture",
+ "title": "Pause Screenshot Capture",
+ "category": "BrighterScript",
+ "icon": "$(debug-pause)"
+ },
+ {
+ "command": "extension.brightscript.rokuDeviceView.resumeScreenshotCapture",
+ "title": "Resume Screenshot Capture",
+ "category": "BrighterScript",
+ "icon": "$(debug-start)"
+ },
+ {
+ "command": "extension.brightscript.rokuDeviceView.refreshScreenshot",
+ "title": "Refresh Screenshot",
+ "category": "BrighterScript",
+ "icon": "$(refresh)"
+ },
+ {
+ "command": "extension.brightscript.sceneGraphInspectorView.refreshNodeTree",
+ "title": "Refresh Nodetree",
+ "category": "BrighterScript",
+ "icon": "$(refresh)"
}
],
"keybindings": [
diff --git a/src/BrightScriptCommands.ts b/src/BrightScriptCommands.ts
index a27c7adcd..b92f00b7d 100644
--- a/src/BrightScriptCommands.ts
+++ b/src/BrightScriptCommands.ts
@@ -169,6 +169,10 @@ export class BrightScriptCommands {
await this.sendRemoteCommand('PowerOff');
});
+ this.registerCommand('pressPowerOn', async () => {
+ await this.sendRemoteCommand('PowerOn');
+ });
+
this.registerCommand('pressChannelUp', async () => {
await this.sendRemoteCommand('ChannelUp');
});
diff --git a/src/LanguageServerManager.spec.ts b/src/LanguageServerManager.spec.ts
index 06225aa03..eec0d7992 100644
--- a/src/LanguageServerManager.spec.ts
+++ b/src/LanguageServerManager.spec.ts
@@ -175,7 +175,7 @@ describe('extension', () => {
).to.eql(embeddedPath);
});
- it('returns value from worksapce when specified', async () => {
+ it('returns value from workspace when specified', async () => {
vscode.workspace.workspaceFile = URI.file(s`${tempDir}/workspace.code-workspace`);
setConfig(vscode.workspace.workspaceFile.fsPath, {
diff --git a/src/commands/VscodeCommand.ts b/src/commands/VscodeCommand.ts
new file mode 100644
index 000000000..a93713927
--- /dev/null
+++ b/src/commands/VscodeCommand.ts
@@ -0,0 +1,11 @@
+export enum VscodeCommand {
+ rokuDeviceViewRefreshScreenshot = 'extension.brightscript.rokuDeviceView.refreshScreenshot',
+ rokuDeviceViewResumeScreenshotCapture = 'extension.brightscript.rokuDeviceView.resumeScreenshotCapture',
+ rokuDeviceViewPauseScreenshotCapture = 'extension.brightscript.rokuDeviceView.pauseScreenshotCapture',
+ rokuDeviceViewEnableNodeInspector = 'extension.brightscript.rokuDeviceView.enableNodeInspector',
+ rokuDeviceViewDisableNodeInspector = 'extension.brightscript.rokuDeviceView.disableNodeInspector',
+ rokuRegistryExportRegistry = 'extension.brightscript.rokuRegistry.exportRegistry',
+ rokuRegistryImportRegistry = 'extension.brightscript.rokuRegistry.importRegistry',
+ rokuRegistryClearRegistry = 'extension.brightscript.rokuRegistry.clearRegistry',
+ rokuRegistryRefreshRegistry = 'extension.brightscript.rokuRegistry.refreshRegistry'
+}
diff --git a/src/extension.spec.ts b/src/extension.spec.ts
index 00c940a7e..830323e71 100644
--- a/src/extension.spec.ts
+++ b/src/extension.spec.ts
@@ -5,8 +5,6 @@ import * as extension from './extension';
import { vscode, vscodeLanguageClient } from './mockVscode.spec';
import { BrightScriptCommands } from './BrightScriptCommands';
import { languageServerManager } from './LanguageServerManager';
-import * as rta from 'roku-test-automation';
-import type { BrightScriptLaunchConfiguration } from './DebugConfigurationProvider';
const sinon = createSandbox();
@@ -49,7 +47,6 @@ describe('extension', () => {
});
afterEach(() => {
- extensionInstance.odc = undefined;
extensionInstance['webviews'] = originalWebviews;
sinon.restore();
});
@@ -101,62 +98,4 @@ describe('extension', () => {
await extension.activate(context);
expect(spy.getCalls().length).to.be.greaterThan(0);
});
-
- describe('RDB', () => {
- const config = {} as BrightScriptLaunchConfiguration;
- let context;
- beforeEach(() => {
- context = { ...vscode.context };
- config.host = '86.75.30.9';
- config.password = 'jenny';
- });
-
- describe('setupODC', () => {
- it('sets up the underlying RokuDevice instance', () => {
- const odc = extensionInstance['setupODC'](config);
- expect(odc.device).to.be.instanceOf(rta.RokuDevice);
- });
-
- it('has the correct config values passed from the extension', () => {
- const odc = extensionInstance['setupODC'](config);
- const deviceConfig = odc.device.getCurrentDeviceConfig();
- expect(deviceConfig.host).to.equal(config.host);
- expect(deviceConfig.password).to.equal(config.password);
- });
- });
-
- describe('registerWebViewProviders', () => {
- it('initializes webview providers and calls registerWebviewViewProvider for each', () => {
- extensionInstance['webviews'] = originalWebviews;
- const spy = sinon.spy(vscode.window, 'registerWebviewViewProvider');
- extensionInstance['registerWebviewProviders'](context);
- expect(spy.callCount).to.equal(Object.keys(originalWebviews).length);
- });
- });
-
- describe('debugSessionCustomEventHandler', () => {
- describe('ChannelPublishedEvent', () => {
- const e = {
- event: 'ChannelPublishedEvent',
- body: {
- launchConfiguration: config
- }
- };
-
- it('calls setupODC to create the odc instance if enabled', async () => {
- config.injectRdbOnDeviceComponent = true;
- const spy = sinon.stub(extensionInstance as any, 'setupODC').returns(undefined);
- await extensionInstance['debugSessionCustomEventHandler'](e, {} as any, {} as any, {} as any, {} as any);
- expect(spy.calledOnce).to.be.true;
- });
-
- it('does not call setupODC if not enabled', async () => {
- config.injectRdbOnDeviceComponent = false;
- const spy = sinon.stub(extensionInstance as any, 'setupODC').returns(undefined);
- await extensionInstance['debugSessionCustomEventHandler'](e, {} as any, {} as any, {} as any, {} as any);
- expect(spy.callCount).to.equal(0);
- });
- });
- });
- });
});
diff --git a/src/extension.ts b/src/extension.ts
index 293e93253..2858fd3e8 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -1,7 +1,6 @@
import * as vscode from 'vscode';
import * as prettyBytes from 'pretty-bytes';
import { extensions } from 'vscode';
-import * as rta from 'roku-test-automation';
import * as path from 'path';
import * as fsExtra from 'fs-extra';
import { util } from './util';
@@ -23,10 +22,10 @@ import { languageServerManager } from './LanguageServerManager';
import { TelemetryManager } from './managers/TelemetryManager';
import { RemoteControlManager } from './managers/RemoteControlManager';
import { WhatsNewManager } from './managers/WhatsNewManager';
-import { SceneGraphInspectorViewProvider } from './viewProviders/SceneGraphInspectorViewProvider';
-import { RokuCommandsViewProvider } from './viewProviders/RokuCommandsViewProvider';
-import { RokuRegistryViewProvider } from './viewProviders/RokuRegistryViewProvider';
import { isChannelPublishedEvent, isChanperfEvent, isDiagnosticsEvent, isDebugServerLogOutputEvent, isLaunchStartEvent, isRendezvousEvent } from 'roku-debug';
+import { RtaManager } from './managers/RtaManager';
+import { WebviewViewProviderManager } from './managers/WebviewViewProviderManager';
+import { ViewProviderId } from './viewProviders/ViewProviderId';
const EXTENSION_ID = 'RokuCommunity.brightscript';
@@ -43,19 +42,8 @@ export class Extension {
private telemetryManager: TelemetryManager;
private remoteControlManager: RemoteControlManager;
private brightScriptCommands: BrightScriptCommands;
-
- public odc?: rta.OnDeviceComponent;
-
- private webviews = [{
- constructor: SceneGraphInspectorViewProvider,
- provider: undefined as SceneGraphInspectorViewProvider
- }, {
- constructor: RokuRegistryViewProvider,
- provider: undefined as RokuRegistryViewProvider
- }, {
- constructor: RokuCommandsViewProvider,
- provider: undefined as RokuCommandsViewProvider
- }];
+ private rtaManager: RtaManager;
+ private webviewViewProviderManager: WebviewViewProviderManager;
public async activate(context: vscode.ExtensionContext) {
const currentExtensionVersion = extensions.getExtension(EXTENSION_ID)?.packageJSON.version as string;
@@ -83,6 +71,12 @@ export class Extension {
activeDeviceManager
);
+ context.subscriptions.push(
+ this.rtaManager = new RtaManager(),
+ this.webviewViewProviderManager = new WebviewViewProviderManager(context, this.rtaManager)
+ );
+ this.rtaManager.setWebviewViewProviderManager(this.webviewViewProviderManager);
+
//update the tracked version of the extension
this.globalStateManager.lastRunExtensionVersion = currentExtensionVersion;
@@ -106,14 +100,11 @@ export class Extension {
//register a tree data provider for this extension's "RENDEZVOUS" view in the debug area
let rendezvousViewProvider = new RendezvousViewProvider(context);
- vscode.window.registerTreeDataProvider('rendezvousView', rendezvousViewProvider);
+ vscode.window.registerTreeDataProvider(ViewProviderId.rendezvousView, rendezvousViewProvider);
//register a tree data provider for this extension's "Online Devices" view
let onlineDevicesViewProvider = new OnlineDevicesViewProvider(context, activeDeviceManager);
- vscode.window.registerTreeDataProvider('onlineDevicesView', onlineDevicesViewProvider);
-
- // register our webview providers
- this.registerWebviewProviders(context);
+ vscode.window.registerTreeDataProvider(ViewProviderId.onlineDevicesView, onlineDevicesViewProvider);
context.subscriptions.push(vscode.commands.registerCommand('extension.brightscript.rendezvous.clearHistory', async () => {
try {
@@ -223,19 +214,9 @@ export class Extension {
const config = e.body as BrightScriptLaunchConfiguration;
await docLinkProvider.setLaunchConfig(config);
logOutputManager.setLaunchConfig(config);
-
} else if (isChannelPublishedEvent(e)) {
- const config = e.body.launchConfiguration as BrightScriptLaunchConfiguration;
- if (!config.injectRdbOnDeviceComponent) {
- void this.odc?.shutdown();
- this.odc = undefined;
- } else {
- this.odc = this.setupODC(config);
- }
- this.setupRdbViewProviders();
- void this.odc?.disableScreenSaver({ disableScreensaver: config.disableScreenSaver });
+ this.webviewViewProviderManager.onChannelPublishedEvent(e);
//write debug server log statements to the DebugServer output channel
-
} else if (isDebugServerLogOutputEvent(e)) {
this.extensionOutputChannel.appendLine(e.body.line);
@@ -280,49 +261,6 @@ export class Extension {
fsExtra.appendFileSync(extensionLogfilePath, text);
}
}
-
- private setupODC(config: BrightScriptLaunchConfiguration) {
- const rtaConfig = this.getRtaConfig(config);
- rta.odc.setConfig(rtaConfig);
- return rta.odc;
- }
-
- private getRtaConfig(config: BrightScriptLaunchConfiguration) {
- const enableDebugging = ['info', 'debug', 'trace'].includes(config.logLevel);
- const rtaConfig: rta.ConfigOptions = {
- RokuDevice: {
- devices: [{
- host: config.host,
- password: config.password
- }]
- },
- OnDeviceComponent: {
- logLevel: enableDebugging ? 'verbose' : undefined,
- clientDebugLogging: enableDebugging,
- disableTelnet: true,
- disableCallOriginationLine: true
- }
- };
- return rtaConfig;
- }
-
- private registerWebviewProviders(context) {
- for (const webview of this.webviews) {
- if (!webview.provider) {
- webview.provider = new webview.constructor(context);
- vscode.window.registerWebviewViewProvider(webview.provider.id, webview.provider);
- }
- }
- }
-
- private setupRdbViewProviders() {
- for (const webview of this.webviews) {
- if (typeof webview.provider.setOnDeviceComponent === 'function') {
- webview.provider.setOnDeviceComponent(this.odc);
- }
- }
- }
-
}
export const extension = new Extension();
export async function activate(context: vscode.ExtensionContext) {
diff --git a/src/managers/RtaManager.ts b/src/managers/RtaManager.ts
new file mode 100644
index 000000000..66fd06367
--- /dev/null
+++ b/src/managers/RtaManager.ts
@@ -0,0 +1,102 @@
+import * as rta from 'roku-test-automation';
+import { ViewProviderEvent } from '../viewProviders/ViewProviderEvent';
+import { ViewProviderId } from '../viewProviders/ViewProviderId';
+import { vscodeContextManager } from './VscodeContextManager';
+import type { WebviewViewProviderManager } from './WebviewViewProviderManager';
+
+export class RtaManager {
+ public onDeviceComponent?: rta.OnDeviceComponent;
+ public device?: rta.RokuDevice;
+
+ private webviewViewProviderManager?: WebviewViewProviderManager;
+ private lastStoreNodesResponse: Awaited>;
+
+ public setupRtaWithConfig(config: { host: string; password: string; logLevel?: string; disableScreenSaver?: boolean; injectRdbOnDeviceComponent?: boolean }) {
+ const enableDebugging = ['info', 'debug', 'trace'].includes(config.logLevel);
+ rta.odc.setConfig({
+ RokuDevice: {
+ devices: [{
+ host: config.host,
+ password: config.password
+ }]
+ },
+ OnDeviceComponent: {
+ logLevel: enableDebugging ? 'verbose' : undefined,
+ clientDebugLogging: enableDebugging,
+ disableTelnet: true,
+ disableCallOriginationLine: true
+ }
+ });
+
+ this.device = rta.device;
+
+ if (config.injectRdbOnDeviceComponent) {
+ this.onDeviceComponent = rta.odc;
+ } else {
+ void this.onDeviceComponent?.shutdown();
+ this.onDeviceComponent = undefined;
+ }
+ void vscodeContextManager.set('brightscript.isOnDeviceComponentAvailable', !!this.onDeviceComponent);
+
+ for (const webviewProvider of this.webviewViewProviderManager.getWebviewViewProviders()) {
+ if (typeof webviewProvider.updateDeviceAvailability === 'function') {
+ webviewProvider.updateDeviceAvailability();
+ }
+ }
+
+ if (config.disableScreenSaver) {
+ void this.onDeviceComponent?.disableScreenSaver({ disableScreensaver: true });
+ }
+ }
+
+ public async sendOdcRequest(requestorId: string, command: string, context: { args: any; options: any }) {
+ const { args, options } = context;
+
+ if (command === rta.RequestType.findNodesAtLocation) {
+ if (!this.lastStoreNodesResponse) {
+ args.includeBoundingRectInfo = true;
+ await this.sendOdcRequest(requestorId, rta.RequestType.storeNodeReferences, args);
+ }
+ context.args.nodeTreeResponse = this.lastStoreNodesResponse;
+ let { matches } = await rta.odc.findNodesAtLocation(args, options);
+ if (requestorId === ViewProviderId.rokuDeviceView) {
+ if (matches.length) {
+ const match = { ...matches[0] };
+ // Remove children as this is where most of the payload is and we don't need this info
+ match.children = [];
+ matches = [match];
+ }
+ }
+ return {
+ matches: matches
+ };
+ } else if (command === rta.RequestType.storeNodeReferences) {
+ this.lastStoreNodesResponse = await rta.odc.storeNodeReferences(args, options);
+
+ const viewIds = [];
+ if (requestorId === ViewProviderId.rokuDeviceView) {
+ viewIds.push(ViewProviderId.sceneGraphInspectorView);
+ } else if (requestorId === ViewProviderId.sceneGraphInspectorView) {
+ viewIds.push(ViewProviderId.rokuDeviceView);
+ }
+ this.webviewViewProviderManager.sendMessageToWebviews(viewIds, {
+ event: ViewProviderEvent.onStoredNodeReferencesUpdated
+ });
+ return this.lastStoreNodesResponse;
+ } else {
+ return this.onDeviceComponent[command](args, options);
+ }
+ }
+
+ public setWebviewViewProviderManager(manager: WebviewViewProviderManager) {
+ this.webviewViewProviderManager = manager;
+ }
+
+ public getStoredNodeReferences() {
+ return this.lastStoreNodesResponse;
+ }
+
+ public dispose() {
+ void this.onDeviceComponent?.shutdown?.();
+ }
+}
diff --git a/src/managers/WebviewViewProviderManager.spec.ts b/src/managers/WebviewViewProviderManager.spec.ts
new file mode 100644
index 000000000..907bc1439
--- /dev/null
+++ b/src/managers/WebviewViewProviderManager.spec.ts
@@ -0,0 +1,94 @@
+import { expect } from 'chai';
+import { createSandbox } from 'sinon';
+import { vscode } from '../mockVscode.spec';
+import type { BrightScriptLaunchConfiguration } from '../DebugConfigurationProvider';
+import { WebviewViewProviderManager } from './WebviewViewProviderManager';
+import { RtaManager } from './RtaManager';
+
+
+const sinon = createSandbox();
+
+describe('WebviewViewProviderManager', () => {
+ let context: any;
+
+ const config = {} as BrightScriptLaunchConfiguration;
+ let webviewViewProviderManager: WebviewViewProviderManager;
+ let rtaManager: RtaManager;
+
+ before(() => {
+ context = {
+ ...vscode.context,
+ extensionPath: '',
+ subscriptions: [],
+ asAbsolutePath: () => { },
+ globalState: {
+ get: () => {
+
+ },
+ update: () => {
+
+ }
+ }
+ };
+
+ config.host = '86.75.30.9';
+ config.password = 'jenny';
+
+ });
+
+ afterEach(() => {
+ sinon.restore();
+ });
+
+
+ describe('constructor', () => {
+ let spy;
+ before(() => {
+ spy = sinon.spy(vscode.window, 'registerWebviewViewProvider');
+ rtaManager = new RtaManager();
+ webviewViewProviderManager = new WebviewViewProviderManager(context, rtaManager);
+ });
+
+ it('initializes webview providers and calls registerWebviewViewProvider for each', () => {
+ expect(spy.callCount).to.equal(webviewViewProviderManager.getWebviewViewProviders().length);
+ });
+
+ it('assigns RtaManager to each webviewViewProvider', () => {
+ for (const webviewViewProvider of webviewViewProviderManager.getWebviewViewProviders()) {
+ expect(webviewViewProvider['rtaManager']).to.equal(rtaManager);
+ }
+ expect(spy.callCount).to.equal(webviewViewProviderManager.getWebviewViewProviders().length);
+ });
+ });
+
+
+ describe('onChannelPublishedEvent', () => {
+ let event: any;
+
+ before(() => {
+ event = {
+ event: 'ChannelPublishedEvent',
+ body: {
+ launchConfiguration: config
+ }
+ };
+
+ rtaManager = new RtaManager();
+ webviewViewProviderManager = new WebviewViewProviderManager(context, rtaManager);
+ rtaManager.setWebviewViewProviderManager(webviewViewProviderManager);
+ });
+
+ it('calls setupRtaWithConfig', () => {
+ const spy = sinon.stub(rtaManager, 'setupRtaWithConfig').returns(undefined);
+ webviewViewProviderManager.onChannelPublishedEvent(event);
+ expect(spy.calledOnce).to.be.true;
+ });
+
+ it('has the correct config values passed from the extension', () => {
+ webviewViewProviderManager.onChannelPublishedEvent(event);
+ const deviceConfig = rtaManager.device.getCurrentDeviceConfig();
+ expect(deviceConfig.host).to.equal(config.host);
+ expect(deviceConfig.password).to.equal(config.password);
+ });
+ });
+});
diff --git a/src/managers/WebviewViewProviderManager.ts b/src/managers/WebviewViewProviderManager.ts
new file mode 100644
index 000000000..d74782ed4
--- /dev/null
+++ b/src/managers/WebviewViewProviderManager.ts
@@ -0,0 +1,77 @@
+import type { ChannelPublishedEvent } from 'roku-debug';
+import type { BrightScriptLaunchConfiguration } from '../DebugConfigurationProvider';
+import type { RtaManager } from './RtaManager';
+import * as vscode from 'vscode';
+import { RokuCommandsViewProvider } from '../viewProviders/RokuCommandsViewProvider';
+import { RokuDeviceViewViewProvider } from '../viewProviders/RokuDeviceViewViewProvider';
+import { RokuRegistryViewProvider } from '../viewProviders/RokuRegistryViewProvider';
+import { SceneGraphInspectorViewProvider } from '../viewProviders/SceneGraphInspectorViewProvider';
+
+
+export class WebviewViewProviderManager {
+ constructor(context: vscode.ExtensionContext, rtaManager: RtaManager) {
+ this.rtaManager = rtaManager;
+
+ for (const webview of this.webviewViews) {
+ if (!webview.provider) {
+ webview.provider = new webview.constructor(context);
+ vscode.window.registerWebviewViewProvider(webview.provider.id, webview.provider);
+
+ webview.provider.setWebviewViewProviderManager(this);
+
+ if (typeof webview.provider.setRtaManager === 'function') {
+ webview.provider.setRtaManager(this.rtaManager);
+ }
+ }
+ }
+ }
+
+ private rtaManager?: RtaManager;
+
+ private webviewViews = [{
+ constructor: SceneGraphInspectorViewProvider,
+ provider: undefined as SceneGraphInspectorViewProvider
+ }, {
+ constructor: RokuRegistryViewProvider,
+ provider: undefined as RokuRegistryViewProvider
+ }, {
+ constructor: RokuCommandsViewProvider,
+ provider: undefined as RokuCommandsViewProvider
+ }, {
+ constructor: RokuDeviceViewViewProvider,
+ provider: undefined as RokuDeviceViewViewProvider
+ }];
+
+ public getWebviewViewProviders() {
+ const providers = [];
+ for (const webview of this.webviewViews) {
+ providers.push(webview.provider);
+ }
+ return providers;
+ }
+
+ // Notification from extension
+ public onChannelPublishedEvent(e: ChannelPublishedEvent) {
+ const config = e.body.launchConfiguration as BrightScriptLaunchConfiguration;
+ this.rtaManager.setupRtaWithConfig(config);
+ }
+
+ // Mainly for communicating between webviews
+ public sendMessageToWebviews(viewIds: string | string[], message) {
+ if (typeof viewIds === 'string') {
+ viewIds = [viewIds];
+ }
+
+ for (const webviewView of this.webviewViews) {
+ if (viewIds.includes(webviewView.provider.id)) {
+ webviewView.provider.postOrQueueMessage(message);
+ }
+ }
+ }
+
+ public dispose() {
+ for (const webviewView of this.webviewViews) {
+ webviewView?.provider?.dispose();
+ }
+ }
+}
diff --git a/src/util.ts b/src/util.ts
index a9954749d..447b8333a 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -188,10 +188,6 @@ class Util {
this.debounceByKey[key]();
}
- public isExtensionHostRunning() {
- return process.argv.includes('--type=extensionHost');
- }
-
/**
* Wraps a function and calls a callback before calling the original function
*/
diff --git a/src/viewProviders/BaseRdbViewProvider.ts b/src/viewProviders/BaseRdbViewProvider.ts
index 7cab0c756..89fb8921a 100644
--- a/src/viewProviders/BaseRdbViewProvider.ts
+++ b/src/viewProviders/BaseRdbViewProvider.ts
@@ -1,72 +1,145 @@
import * as rta from 'roku-test-automation';
-
+import type * as vscode from 'vscode';
+import type { RequestType } from 'roku-test-automation';
+import * as fsExtra from 'fs-extra';
+import * as path from 'path';
+import * as childProcess from 'child_process';
import { BaseWebviewViewProvider } from './BaseWebviewViewProvider';
+import type { RtaManager } from '../managers/RtaManager';
+import { ViewProviderEvent } from './ViewProviderEvent';
+import { ViewProviderCommand } from './ViewProviderCommand';
+import { standardizePath as s } from 'brighterscript';
+import { PNG } from 'pngjs';
export abstract class BaseRdbViewProvider extends BaseWebviewViewProvider {
- protected onDeviceComponent?: rta.OnDeviceComponent;
+ protected rtaManager?: RtaManager;
+
+ protected odcCommands: Array;
- protected odcCommands: Array = [
- 'callFunc',
- 'deleteEntireRegistry',
- 'deleteRegistrySections',
- 'getFocusedNode',
- 'getValue',
- 'getValues',
- 'getNodesInfo',
- 'hasFocus',
- 'isInFocusChain',
- 'onFieldChangeOnce',
- 'readRegistry',
- 'setValue',
- 'writeRegistry',
- 'storeNodeReferences',
- 'deleteNodeReferences'
- ];
+ constructor(context: vscode.ExtensionContext) {
+ super(context);
+ const requestTypesPath = path.join(rta.utils.getClientFilesPath(), 'requestTypes.schema.json');
+ const json = JSON.parse(fsExtra.readFileSync(requestTypesPath, 'utf8'));
+ this.odcCommands = json.enum;
+ }
- // @param odc - The OnDeviceComponent class instance. If undefined existing instance will be removed. Used to notify webview of change in ODC status
- public setOnDeviceComponent(onDeviceComponent?: rta.OnDeviceComponent) {
- this.onDeviceComponent = onDeviceComponent;
+ public setRtaManager(rtaManager?: RtaManager) {
+ this.rtaManager = rtaManager;
+ }
+ public updateDeviceAvailability() {
this.postOrQueueMessage({
- name: 'onDeviceComponentStatus',
- available: onDeviceComponent ? true : false
+ event: ViewProviderEvent.onDeviceAvailabilityChange,
+ odcAvailable: !!this.rtaManager.onDeviceComponent,
+ deviceAvailable: !!this.rtaManager.device
});
}
protected onViewReady() {
- // Always post back the ODC status so we make sure the client doesn't miss it if it got refreshed
- this.setOnDeviceComponent(this.onDeviceComponent);
+ // Always post back the device status so we make sure the client doesn't miss it if it got refreshed
+ this.updateDeviceAvailability();
+ }
+
+ private isFfmpegScreenshotEnabled = true;
+
+ private ffmpegProcess: childProcess.ChildProcessWithoutNullStreams;
+
+ private latestScreenshotBuffer = Buffer.alloc(0);
+
+ private async getScreenshot(): Promise {
+ if (this.isFfmpegScreenshotEnabled) {
+ const imagesDir = s`${__dirname}/../../.tmp/__ffmpegImages`;
+ if (!this.ffmpegProcess) {
+ fsExtra.emptyDirSync(imagesDir);
+ //have ffmpeg generate screenshots at regular interval:
+ this.ffmpegProcess = childProcess.spawn('ffmpeg', [
+ '-f',
+ 'dshow',
+ '-i',
+ `video="usb video"`,
+ '-vf',
+ 'fps=1',
+ '-s',
+ '480x270',
+ '-c:v',
+ 'png',
+ '-f',
+ 'image2pipe',
+ '-'
+ ], {
+ shell: true,
+ //image2pipe sends the images over channel 1 (stdout)
+ stdio: ['ignore', 'pipe', 'ignore']
+ });
+
+ let buffer = Buffer.alloc(0);
+ this.ffmpegProcess.stdout.on('data', (data) => {
+ buffer = Buffer.concat([buffer, data]);
+ try {
+ const png = new PNG(buffer);
+ this.latestScreenshotBuffer = png.imgData;
+ buffer = buffer.slice(png.imgData.length);
+ } catch (e) {
+ }
+ });
+ }
+ return this.latestScreenshotBuffer.buffer;
+ } else {
+ return (
+ await this.rtaManager.device.getScreenshot()
+ ).buffer.buffer;
+ }
}
protected async handleViewMessage(message) {
const { command, context } = message;
if (this.odcCommands.includes(command)) {
- const response = await this.onDeviceComponent[command](context.args, context.options);
- this.postMessage({
+ const response = await this.rtaManager.sendOdcRequest(this.id, command, context);
+ this.postOrQueueMessage({
...message,
response: response
});
return true;
- } else if (command === 'setManualIpAddress') {
- const onDeviceComponent = rta.odc;
-
- const rtaConfig: rta.ConfigOptions = {
- RokuDevice: {
- devices: [{
- host: context.ipAddress,
- password: ''
- }]
- },
- OnDeviceComponent: {
- disableTelnet: true,
- disableCallOriginationLine: true
- }
- };
+ } else if (command === ViewProviderCommand.getStoredNodeReferences) {
+ const response = this.rtaManager.getStoredNodeReferences();
+ this.postOrQueueMessage({
+ ...message,
+ response: response
+ });
- onDeviceComponent.setConfig(rtaConfig);
- this.setOnDeviceComponent(onDeviceComponent);
+ return true;
+ } else if (command === ViewProviderCommand.setManualIpAddress) {
+ this.rtaManager.setupRtaWithConfig({
+ ...message.context,
+ injectRdbOnDeviceComponent: true
+ });
+ return true;
+ } else if (command === ViewProviderCommand.getScreenshot) {
+ try {
+ const buffer = await this.getScreenshot();
+ this.postOrQueueMessage({
+ ...message,
+ response: {
+ success: true,
+ arrayBuffer: buffer
+ }
+ });
+ } catch (e) {
+ this.postOrQueueMessage({
+ ...message,
+ response: {
+ success: false
+ }
+ });
+ }
+ return true;
}
return false;
}
+
+ public dispose(): void {
+ super.dispose();
+ this.ffmpegProcess.kill();
+ }
}
diff --git a/src/viewProviders/BaseWebviewViewProvider.ts b/src/viewProviders/BaseWebviewViewProvider.ts
index 61a9e83b2..18358644a 100644
--- a/src/viewProviders/BaseWebviewViewProvider.ts
+++ b/src/viewProviders/BaseWebviewViewProvider.ts
@@ -2,8 +2,11 @@ import * as vscode from 'vscode';
import * as path from 'path';
import * as fsExtra from 'fs-extra';
import type { AsyncSubscription, Event } from '@parcel/watcher';
-
+import { vscodeContextManager } from '../managers/VscodeContextManager';
import { util } from '../util';
+import type { WebviewViewProviderManager } from '../managers/WebviewViewProviderManager';
+import { ViewProviderEvent } from './ViewProviderEvent';
+import { ViewProviderCommand } from './ViewProviderCommand';
export abstract class BaseWebviewViewProvider implements vscode.WebviewViewProvider, vscode.Disposable {
constructor(context: vscode.ExtensionContext) {
@@ -22,11 +25,24 @@ export abstract class BaseWebviewViewProvider implements vscode.WebviewViewProvi
private outDirWatcher: AsyncSubscription;
private viewReady = false;
private queuedMessages = [];
+ private webviewViewProviderManager: WebviewViewProviderManager;
public dispose() {
void this.outDirWatcher?.unsubscribe();
}
+ public setWebviewViewProviderManager(manager: WebviewViewProviderManager) {
+ this.webviewViewProviderManager = manager;
+ }
+
+ public postOrQueueMessage(message) {
+ if (this.viewReady) {
+ this.postMessage(message);
+ } else {
+ this.queuedMessages.push(message);
+ }
+ }
+
protected postMessage(message) {
this.view?.webview.postMessage(message).then(null, (reason) => {
console.log('postMessage failed: ', reason);
@@ -39,30 +55,31 @@ export abstract class BaseWebviewViewProvider implements vscode.WebviewViewProvi
}
}
- protected postOrQueueMessage(message) {
- if (this.viewReady) {
- this.postMessage(message);
- } else {
- this.queuedMessages.push(message);
- }
- }
-
private setupViewMessageObserver(webview: vscode.Webview) {
webview.onDidReceiveMessage(async (message) => {
try {
const command = message.command;
- if (command === 'viewReady') {
+ if (command === ViewProviderCommand.viewReady) {
this.viewReady = true;
this.onViewReady();
this.postQueuedMessages();
+ } else if (command === ViewProviderCommand.setVscodeContext) {
+ await vscodeContextManager.set(message.key, message.value);
+ } else if (command === ViewProviderCommand.sendMessageToWebviews) {
+ this.webviewViewProviderManager.sendMessageToWebviews(message.viewIds, message.message);
} else {
- await this.handleViewMessage(message);
+ if (!await this.handleViewMessage(message)) {
+ console.warn('Did not handle message', message);
+ }
}
} catch (e) {
this.postMessage({
...message,
- error: e.message
+ error: {
+ message: e.message,
+ stack: e.stack
+ }
});
}
});
@@ -72,6 +89,15 @@ export abstract class BaseWebviewViewProvider implements vscode.WebviewViewProvi
return false;
}
+ protected registerCommandWithWebViewNotifier(context: vscode.ExtensionContext, command: string) {
+ context.subscriptions.push(vscode.commands.registerCommand(command, () => {
+ this.postOrQueueMessage({
+ event: ViewProviderEvent.onVscodeCommandReceived,
+ commandName: command
+ });
+ }));
+ }
+
protected onViewReady() { }
/** Adds ability to add additional script contents to the main webview html */
@@ -81,11 +107,18 @@ export abstract class BaseWebviewViewProvider implements vscode.WebviewViewProvi
protected async getHtmlForWebview() {
try {
- if (util.isExtensionHostRunning() && !this.outDirWatcher) {
+ let watcher;
+ try {
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
+ watcher = require('@parcel/watcher');
+ } catch (e) {
+ // Doing nothing if watcher does not exist
+ }
+
+ if (watcher && !this.outDirWatcher) {
// When in dev mode, spin up a watcher to auto-reload the webview whenever the files have changed.
- // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
- this.outDirWatcher = await require('@parcel/watcher').subscribe(this.webviewBasePath, (err, events: Event[]) => {
- //only refresh when the index.html page is changed. Since vite rewrites the file on every cycle, this is enough to know to reload the page
+ this.outDirWatcher = await watcher.subscribe(this.webviewBasePath, (err, events: Event[]) => {
+ //only refresh when the index.html page is changed. Since vite rewrites the file on every build, this is enough to know to reload the page
if (
events.find(x => (x.type === 'create' || x.type === 'update') && x.path?.toLowerCase()?.endsWith('index.html'))
) {
diff --git a/src/viewProviders/OnlineDevicesViewProvider.ts b/src/viewProviders/OnlineDevicesViewProvider.ts
index 8cec8c833..5e1e22b83 100644
--- a/src/viewProviders/OnlineDevicesViewProvider.ts
+++ b/src/viewProviders/OnlineDevicesViewProvider.ts
@@ -5,10 +5,11 @@ import { icons } from '../icons';
import { firstBy } from 'thenby';
import { Cache } from 'brighterscript/dist/Cache';
import { util } from '../util';
+import { ViewProviderId } from './ViewProviderId';
export class OnlineDevicesViewProvider implements vscode.TreeDataProvider {
- public readonly id = 'onlineDevicesView';
+ public readonly id = ViewProviderId.onlineDevicesView;
constructor(
private context: vscode.ExtensionContext,
diff --git a/src/viewProviders/RendezvousViewProvider.ts b/src/viewProviders/RendezvousViewProvider.ts
index 27ce3df12..485399692 100644
--- a/src/viewProviders/RendezvousViewProvider.ts
+++ b/src/viewProviders/RendezvousViewProvider.ts
@@ -2,10 +2,11 @@ import * as arraySort from 'array-sort';
import * as vscode from 'vscode';
import type { RendezvousHistory } from 'roku-debug';
import { isRendezvousEvent } from 'roku-debug';
+import { ViewProviderId } from './ViewProviderId';
export class RendezvousViewProvider implements vscode.TreeDataProvider {
- public readonly id = 'rendezvousView';
+ public readonly id = ViewProviderId.rendezvousView;
constructor(context: vscode.ExtensionContext) {
this.toggleSmartSorting();
diff --git a/src/viewProviders/RokuCommandsViewProvider.spec.ts b/src/viewProviders/RokuCommandsViewProvider.spec.ts
index 2bb01ea66..c8c2f79bf 100644
--- a/src/viewProviders/RokuCommandsViewProvider.spec.ts
+++ b/src/viewProviders/RokuCommandsViewProvider.spec.ts
@@ -16,6 +16,7 @@ Module.prototype.require = function hijacked(file) {
let sinon: sinonImport.SinonSandbox;
let view;
let callback;
+let provider: RokuCommandsViewProvider;
beforeEach(() => {
sinon = sinonImport.createSandbox();
view = {
@@ -27,30 +28,30 @@ beforeEach(() => {
},
show: () => { }
};
+
+ provider = new RokuCommandsViewProvider(vscode.context) as any;
});
afterEach(() => {
+ provider.dispose();
sinon.restore();
});
describe('RokuCommandsViewProvider', () => {
describe('getHtmlForWebview', () => {
- const provider = new RokuCommandsViewProvider(vscode.context) as any;
-
it('includes the contents of additionalScriptContents', async () => {
- const html = await provider.getHtmlForWebview();
- for (const line of provider.additionalScriptContents()) {
+ const html = await provider['getHtmlForWebview']();
+ for (const line of provider['additionalScriptContents']()) {
expect(html).to.contain(line);
}
});
});
describe('resolveWebviewView', () => {
- it('sets up observer to handle messages from the ui', () => {
- const provider = new RokuCommandsViewProvider(vscode.context) as any;
- provider.resolveWebviewView(view, {}, {});
+ it('sets up observer to handle messages from the ui', async () => {
+ await provider['resolveWebviewView'](view, {} as any, {} as any);
expect(typeof callback).to.equal('function');
- const spy = sinon.spy(provider, 'handleViewMessage');
+ const spy = sinon.spy(provider as any, 'handleViewMessage');
callback({
command: 'importRegistry',
context: {}
diff --git a/src/viewProviders/RokuCommandsViewProvider.ts b/src/viewProviders/RokuCommandsViewProvider.ts
index 66bbf0b3f..a0d4cb45b 100644
--- a/src/viewProviders/RokuCommandsViewProvider.ts
+++ b/src/viewProviders/RokuCommandsViewProvider.ts
@@ -2,9 +2,10 @@ import * as path from 'path';
import * as rta from 'roku-test-automation';
import * as fs from 'fs';
import { BaseRdbViewProvider } from './BaseRdbViewProvider';
+import { ViewProviderId } from './ViewProviderId';
export class RokuCommandsViewProvider extends BaseRdbViewProvider {
- public readonly id = 'rokuCommandsView';
+ public readonly id = ViewProviderId.rokuCommandsView;
protected additionalScriptContents() {
const requestArgsPath = path.join(rta.utils.getClientFilesPath(), 'requestArgs.schema.json');
diff --git a/src/viewProviders/RokuDeviceViewViewProvider.ts b/src/viewProviders/RokuDeviceViewViewProvider.ts
new file mode 100644
index 000000000..9cab6128d
--- /dev/null
+++ b/src/viewProviders/RokuDeviceViewViewProvider.ts
@@ -0,0 +1,18 @@
+import type * as vscode from 'vscode';
+import { VscodeCommand } from '../commands/VscodeCommand';
+import { BaseRdbViewProvider } from './BaseRdbViewProvider';
+import { ViewProviderId } from './ViewProviderId';
+
+export class RokuDeviceViewViewProvider extends BaseRdbViewProvider {
+ public readonly id = ViewProviderId.rokuDeviceView;
+
+ constructor(context: vscode.ExtensionContext) {
+ super(context);
+
+ this.registerCommandWithWebViewNotifier(context, VscodeCommand.rokuDeviceViewEnableNodeInspector);
+ this.registerCommandWithWebViewNotifier(context, VscodeCommand.rokuDeviceViewDisableNodeInspector);
+ this.registerCommandWithWebViewNotifier(context, VscodeCommand.rokuDeviceViewRefreshScreenshot);
+ this.registerCommandWithWebViewNotifier(context, VscodeCommand.rokuDeviceViewPauseScreenshotCapture);
+ this.registerCommandWithWebViewNotifier(context, VscodeCommand.rokuDeviceViewResumeScreenshotCapture);
+ }
+}
diff --git a/src/viewProviders/RokuRegistryViewProvider.spec.ts b/src/viewProviders/RokuRegistryViewProvider.spec.ts
index 67691ece3..7f093de89 100644
--- a/src/viewProviders/RokuRegistryViewProvider.spec.ts
+++ b/src/viewProviders/RokuRegistryViewProvider.spec.ts
@@ -1,5 +1,6 @@
import { expect } from 'chai';
import * as sinonImport from 'sinon';
+import { RtaManager } from '../managers/RtaManager';
import { vscode } from '../mockVscode.spec';
import { RokuRegistryViewProvider } from './RokuRegistryViewProvider';
@@ -35,6 +36,8 @@ afterEach(() => {
describe('RokuRegistryViewProvider', () => {
const provider = new RokuRegistryViewProvider(vscode.context);
+ const rtaManager = new RtaManager();
+ provider.setRtaManager(rtaManager);
describe('sendRegistryUpdated', () => {
it('Triggers postOrQueueMessage to send message to web view that the registry was updated', async () => {
diff --git a/src/viewProviders/RokuRegistryViewProvider.ts b/src/viewProviders/RokuRegistryViewProvider.ts
index 9da9662f1..187b31e3d 100644
--- a/src/viewProviders/RokuRegistryViewProvider.ts
+++ b/src/viewProviders/RokuRegistryViewProvider.ts
@@ -1,22 +1,25 @@
import * as vscode from 'vscode';
+import { VscodeCommand } from '../commands/VscodeCommand';
import { BaseRdbViewProvider } from './BaseRdbViewProvider';
+import { ViewProviderEvent } from './ViewProviderEvent';
+import { ViewProviderId } from './ViewProviderId';
export class RokuRegistryViewProvider extends BaseRdbViewProvider {
- public readonly id = 'rokuRegistryView';
+ public readonly id = ViewProviderId.rokuRegistryView;
constructor(context: vscode.ExtensionContext) {
super(context);
const subscriptions = context.subscriptions;
- subscriptions.push(vscode.commands.registerCommand('extension.brightscript.rokuRegistry.exportRegistry', async () => {
+ subscriptions.push(vscode.commands.registerCommand(VscodeCommand.rokuRegistryExportRegistry, async () => {
await vscode.window.showSaveDialog({ saveLabel: 'Save' }).then(async (uri) => {
- const result = await this.onDeviceComponent?.readRegistry();
+ const result = await this.rtaManager.onDeviceComponent?.readRegistry();
await vscode.workspace.fs.writeFile(uri, Buffer.from(JSON.stringify(result?.values), 'utf8'));
});
}));
- subscriptions.push(vscode.commands.registerCommand('extension.brightscript.rokuRegistry.importRegistry', async () => {
+ subscriptions.push(vscode.commands.registerCommand(VscodeCommand.rokuRegistryImportRegistry, async () => {
const options: vscode.OpenDialogOptions = {
canSelectMany: false,
openLabel: 'Select',
@@ -26,12 +29,12 @@ export class RokuRegistryViewProvider extends BaseRdbViewProvider {
await vscode.window.showOpenDialog(options).then(this.importContentsToRegistry.bind(this));
}));
- subscriptions.push(vscode.commands.registerCommand('extension.brightscript.rokuRegistry.clearRegistry', async () => {
- await this.onDeviceComponent.deleteEntireRegistry();
+ subscriptions.push(vscode.commands.registerCommand(VscodeCommand.rokuRegistryClearRegistry, async () => {
+ await this.rtaManager.onDeviceComponent.deleteEntireRegistry();
await this.sendRegistryUpdated();
}));
- subscriptions.push(vscode.commands.registerCommand('extension.brightscript.rokuRegistry.refreshRegistry', async () => {
+ subscriptions.push(vscode.commands.registerCommand(VscodeCommand.rokuRegistryRefreshRegistry, async () => {
await this.sendRegistryUpdated();
}));
}
@@ -41,7 +44,7 @@ export class RokuRegistryViewProvider extends BaseRdbViewProvider {
const input = await vscode.workspace.fs.readFile(uri[0]);
const data = JSON.parse(Buffer.from(input).toString('utf8'));
- await this.onDeviceComponent?.writeRegistry({
+ await this.rtaManager.onDeviceComponent?.writeRegistry({
values: data
});
await this.sendRegistryUpdated();
@@ -49,7 +52,7 @@ export class RokuRegistryViewProvider extends BaseRdbViewProvider {
}
protected async sendRegistryUpdated() {
- const result = await this.onDeviceComponent?.readRegistry();
- this.postOrQueueMessage({ name: 'registryUpdated', values: result?.values });
+ const result = await this.rtaManager.onDeviceComponent?.readRegistry();
+ this.postOrQueueMessage({ name: ViewProviderEvent.onRegistryUpdated, values: result?.values });
}
}
diff --git a/src/viewProviders/SceneGraphInspectorViewProvider.ts b/src/viewProviders/SceneGraphInspectorViewProvider.ts
index 3265b9075..53ef47de6 100644
--- a/src/viewProviders/SceneGraphInspectorViewProvider.ts
+++ b/src/viewProviders/SceneGraphInspectorViewProvider.ts
@@ -1,5 +1,13 @@
+import type * as vscode from 'vscode';
import { BaseRdbViewProvider } from './BaseRdbViewProvider';
+import { ViewProviderId } from './ViewProviderId';
export class SceneGraphInspectorViewProvider extends BaseRdbViewProvider {
- public readonly id = 'sceneGraphInspectorView';
+ public readonly id = ViewProviderId.sceneGraphInspectorView;
+
+ constructor(context: vscode.ExtensionContext) {
+ super(context);
+
+ this.registerCommandWithWebViewNotifier(context, 'extension.brightscript.sceneGraphInspectorView.refreshNodeTree');
+ }
}
diff --git a/src/viewProviders/ViewProviderCommand.ts b/src/viewProviders/ViewProviderCommand.ts
new file mode 100644
index 000000000..329d3e08c
--- /dev/null
+++ b/src/viewProviders/ViewProviderCommand.ts
@@ -0,0 +1,8 @@
+export enum ViewProviderCommand {
+ viewReady = 'viewReady',
+ getStoredNodeReferences = 'getStoredNodeReferences',
+ getScreenshot = 'getScreenshot',
+ setManualIpAddress = 'setManualIpAddress',
+ sendMessageToWebviews = 'sendMessageToWebviews',
+ setVscodeContext = 'setVscodeContext'
+}
diff --git a/src/viewProviders/ViewProviderEvent.ts b/src/viewProviders/ViewProviderEvent.ts
new file mode 100644
index 000000000..b761ccedd
--- /dev/null
+++ b/src/viewProviders/ViewProviderEvent.ts
@@ -0,0 +1,7 @@
+export enum ViewProviderEvent {
+ onTreeNodeFocused = 'onTreeNodeFocused',
+ onDeviceAvailabilityChange = 'onDeviceAvailabilityChange',
+ onVscodeCommandReceived = 'onVscodeCommandReceived',
+ onRegistryUpdated = 'onRegistryUpdated',
+ onStoredNodeReferencesUpdated = 'onStoredNodeReferencesUpdated'
+}
diff --git a/src/viewProviders/ViewProviderId.ts b/src/viewProviders/ViewProviderId.ts
new file mode 100644
index 000000000..a5d76c393
--- /dev/null
+++ b/src/viewProviders/ViewProviderId.ts
@@ -0,0 +1,8 @@
+export enum ViewProviderId {
+ onlineDevicesView = 'onlineDevicesView',
+ rendezvousView = 'rendezvousView',
+ sceneGraphInspectorView = 'sceneGraphInspectorView',
+ rokuDeviceView = 'rokuDeviceView',
+ rokuRegistryView = 'rokuRegistryView',
+ rokuCommandsView = 'rokuCommandsView'
+}
diff --git a/webviews/index.html b/webviews/index.html
index 23a7bf10d..e78580350 100644
--- a/webviews/index.html
+++ b/webviews/index.html
@@ -7,6 +7,7 @@
@@ -15,4 +16,4 @@