├── .gitignore ├── LICENSE ├── README.md ├── babel.config.js ├── package-lock.json ├── package.json ├── src ├── app │ ├── App.vue │ ├── components │ │ ├── Map.vue │ │ ├── NavBar.vue │ │ └── WebMetrics.vue │ ├── containers │ │ └── MainContainer.vue │ ├── index.js │ └── styles │ │ └── styles.css └── extension │ └── build │ ├── app.bundle.js │ ├── app.bundle.js.LICENSE.txt │ ├── assets │ └── oVUElord_logo.png │ ├── background.bundle.js │ ├── background.js │ ├── content.bundle.js │ ├── content_script.js │ ├── devtools.html │ ├── devtools.js │ ├── fontAwesome.js │ ├── manifest.json │ ├── panel.html │ ├── script.bundle.js │ └── script.js └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | bundles/* 4 | src/extension/build/app.bundle.js 5 | src/extension/build/app.bundle.js.LICENSE.txt 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Reactime-Vue 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KangaVue 2 | 3 |

KangaVue is a performance and debugging tool for Vue developers (currently in Beta version). It displays a snapshot whenever a target application's state is changed. It also displays key web metrics from loading of the application for SEO strategies and overal app performance.

4 | 5 | After installing KangaVue, you can test its functionalities with your Vue application in development mode. 6 | 7 | ## Installation 8 | 9 | 10 | 11 | 12 | NOTE: The Vue Developer Tools [extension](https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd?hl=en) is also required for KangaVue to run, if you do not already have it installed on your browser. 13 | 14 | ## How to Use 15 | 16 | After installing the Chrome extension, just open up your project in the browser. 17 | 18 | Then open up your Chrome DevTools and navigate to the KangaVue panel. 19 | 20 | ## Troubleshooting 21 | 22 | ### ❓ I found a bug in KangaVue 23 | 24 | KangaVue is an open source project, and we’d really appreciate your help with improving user experience. Please, create a pull request (or issue) to propose and collaborate on changes to a repository. 25 | 26 | ## Features 27 | 28 | ### 🔹 Component Hierarchy 29 | 30 | You can click on the component tree tab in the navigation bar to view your app's component heirarchy. State can be visualized in a Component Graph. 31 | 32 | ### 🔹 Web Metrics 33 | 34 | When your app loads or refreshes, the initial loading metrics will be rendered in a visual display under the Web Metrics Panel. Use these metrics to pinpoint performance issues that may affect your user experience and/or search optimization efforts. 35 | 36 | 37 | ## Read More 38 | 39 | - [KangaVue — Vue.js web metrics in a devtool pouch](https://dxbernstein.medium.com/kangavue-vue-js-web-metrics-in-a-devtool-pouch-3aee7f128a3f) 40 | 41 | ## Authors 42 | - **Harry Fox** - [@StackOverFlowWhereArtThou](https://github.com/StackOverFlowWhereArtThou) 43 | - **Nathan Richardson** - [@BagelEnthusiast](https://github.com/BagelEnthusiast) 44 | - **David Bernstein** - [@dangitbobbeh](https://github.com/dangitbobbeh) 45 | - **Joseph Stern** - [@josephiswhere](https://github.com/josephiswhere)) 46 | 47 | ## License 48 | 49 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details 50 | -------------------------------------------------------------------------------- /babel.config.js : -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | useBuiltIns: "usage", 7 | corejs: 3, 8 | }, 9 | ], 10 | ], 11 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "KangaVue", 3 | "version": "1.0.0", 4 | "description": "Developer tool for vue", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "webpack --mode production", 8 | "start": "webpack serve --mode development" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/StackOverFlowWhereArtThou/KangaVue.git" 13 | }, 14 | "author": "", 15 | "license": "ISC", 16 | "bugs": { 17 | "url": "https://github.com/StackOverFlowWhereArtThou/KangaVue/issues" 18 | }, 19 | "homepage": "https://github.com/StackOverFlowWhereArtThou/KangaVue#readme", 20 | "dependencies": { 21 | "apexcharts": "^3.26.0", 22 | "core-js": "^3.10.0", 23 | "d3": "^6.6.2", 24 | "sass": "^1.32.8", 25 | "vue": "^2.6.12", 26 | "vue-apexcharts": "^1.6.1", 27 | "vue-router": "^3.5.1", 28 | "vued3tree": "^5.1.0", 29 | "vuetify": "^2.4.9", 30 | "web-vitals": "^1.1.1" 31 | }, 32 | "devDependencies": { 33 | "@babel/core": "^7.13.14", 34 | "@babel/preset-env": "^7.13.12", 35 | "autoprefixer": "^9.0.2", 36 | "babel-loader": "^8.2.2", 37 | "css-loader": "^5.2.1", 38 | "mini-css-extract-plugin": "^1.4.1", 39 | "node-sass": "^5.0.0", 40 | "postcss-loader": "^3.0.0", 41 | "sass-loader": "^11.0.1", 42 | "style-loader": "^2.0.0", 43 | "vue-loader": "^15.9.6", 44 | "vue-template-compiler": "^2.6.12", 45 | "webpack": "^5.30.0", 46 | "webpack-cli": "^4.6.0", 47 | "webpack-dev-server": "^3.11.2" 48 | }, 49 | "browserslist": [ 50 | "> 1%", 51 | "last 2 versions", 52 | "not dead" 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /src/app/App.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | 39 | -------------------------------------------------------------------------------- /src/app/components/Map.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 46 | 68 | -------------------------------------------------------------------------------- /src/app/components/NavBar.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 37 | 38 | -------------------------------------------------------------------------------- /src/app/components/WebMetrics.vue: -------------------------------------------------------------------------------- 1 | 10 | 108 | 109 | 111 | -------------------------------------------------------------------------------- /src/app/containers/MainContainer.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 37 | 38 | -------------------------------------------------------------------------------- /src/app/index.js: -------------------------------------------------------------------------------- 1 | // import Vue from 'vue'; 2 | 3 | // const app = new Vue({ 4 | // el: '#vue-root', 5 | // data: { 6 | // message: 'Hello Vue!', 7 | // }, 8 | // }); 9 | 10 | // document.addEventListener("DOMContentLoaded", () => { 11 | // console.log('index.js has been triggered!!!'); 12 | // const NAME = 'Bobby Schmurda'; 13 | // const rootApp = document.getElementById('app'); 14 | // rootApp.innerHTML = `

Hello, ${NAME}

`; 15 | // }); 16 | 17 | import Vue from 'vue'; 18 | import App from './App.vue'; 19 | import './styles/styles.css'; 20 | 21 | import VueApexCharts from 'vue-apexcharts' 22 | 23 | Vue.component('apexchart', VueApexCharts); 24 | 25 | new Vue({ 26 | el: '#app', 27 | data:{ 28 | treemap: {}, 29 | }, 30 | render: h => h(App) 31 | }); -------------------------------------------------------------------------------- /src/app/styles/styles.css: -------------------------------------------------------------------------------- 1 | html { 2 | margin: 0; 3 | padding: 0; 4 | height: 100%; 5 | } 6 | 7 | body { 8 | margin: 0; 9 | height: 100%; 10 | padding: 0px; 11 | background-color: white; 12 | } 13 | 14 | #app { 15 | height: 100%; 16 | 17 | } -------------------------------------------------------------------------------- /src/extension/build/app.bundle.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /*! 2 | * ApexCharts v3.26.0 3 | * (c) 2018-2021 Juned Chhipa 4 | * Released under the MIT License. 5 | */ 6 | 7 | /*! 8 | * Vue.js v2.6.12 9 | * (c) 2014-2020 Evan You 10 | * Released under the MIT License. 11 | */ 12 | 13 | /*! svg.draggable.js - v2.2.2 - 2019-01-08 14 | * https://github.com/svgdotjs/svg.draggable.js 15 | * Copyright (c) 2019 Wout Fierens; Licensed MIT */ 16 | 17 | /*! svg.filter.js - v2.0.2 - 2016-02-24 18 | * https://github.com/wout/svg.filter.js 19 | * Copyright (c) 2016 Wout Fierens; Licensed MIT */ 20 | 21 | /**! 22 | * @fileOverview Kickass library to create and place poppers near their reference elements. 23 | * @version 1.15.0 24 | * @license 25 | * Copyright (c) 2016 Federico Zivolo and contributors 26 | * 27 | * Permission is hereby granted, free of charge, to any person obtaining a copy 28 | * of this software and associated documentation files (the "Software"), to deal 29 | * in the Software without restriction, including without limitation the rights 30 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 31 | * copies of the Software, and to permit persons to whom the Software is 32 | * furnished to do so, subject to the following conditions: 33 | * 34 | * The above copyright notice and this permission notice shall be included in all 35 | * copies or substantial portions of the Software. 36 | * 37 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 38 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 39 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 40 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 41 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 42 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 43 | * SOFTWARE. 44 | */ 45 | -------------------------------------------------------------------------------- /src/extension/build/assets/oVUElord_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/KangaVue/f4afae0c6f41e21aca96d7e55460aca07308d4a0/src/extension/build/assets/oVUElord_logo.png -------------------------------------------------------------------------------- /src/extension/build/background.bundle.js: -------------------------------------------------------------------------------- 1 | !function(){var e={};chrome.tabs.query({active:!0,currentWindow:!0},(function(e){console.log("backgroundjs tabs check:",e)})),chrome.runtime.onConnect.addListener((function(n){var o=function(o,t,s){if("init"==o.name)return e[o.tabId]=n,void console.log("message.tabId background.js ln 27 = ",o.tabId)};n.onMessage.addListener(o),n.onDisconnect.addListener((function(n){n.onMessage.removeListener(o);for(var t=Object.keys(e),s=0,i=t.length;s { 80 | // console.log("Reqeust and Sender \n", req, sender); 81 | 82 | // if (req.name) { 83 | // tabsObj[req.name] = req.value; 84 | // console.log(tabsObj); 85 | // } 86 | // }); 87 | -------------------------------------------------------------------------------- /src/extension/build/content.bundle.js: -------------------------------------------------------------------------------- 1 | !function(){"use strict";var e,t,n,i,a=function(e,t){return{name:e,value:void 0===t?-1:t,delta:0,entries:[],id:"v1-".concat(Date.now(),"-").concat(Math.floor(8999999999999*Math.random())+1e12)}},r=function(e,t){try{if(PerformanceObserver.supportedEntryTypes.includes(e)){var n=new PerformanceObserver((function(e){return e.getEntries().map(t)}));return n.observe({type:e,buffered:!0}),n}}catch(e){}},o=function(e,t){var n=function n(i){"pagehide"!==i.type&&"hidden"!==document.visibilityState||(e(i),t&&(removeEventListener("visibilitychange",n,!0),removeEventListener("pagehide",n,!0)))};addEventListener("visibilitychange",n,!0),addEventListener("pagehide",n,!0)},c=function(e){addEventListener("pageshow",(function(t){t.persisted&&e(t)}),!0)},u="function"==typeof WeakSet?new WeakSet:new Set,s=function(e,t,n){var i;return function(){t.value>=0&&(n||u.has(t)||"hidden"===document.visibilityState)&&(t.delta=t.value-(i||0),(t.delta||void 0===i)&&(i=t.value,e(t)))}},m=-1,d=function(){return"hidden"===document.visibilityState?0:1/0},f=function(){o((function(e){var t=e.timeStamp;m=t}),!0)},p=function(){return m<0&&(m=d(),f(),c((function(){setTimeout((function(){m=d(),f()}),0)}))),{get timeStamp(){return m}}},v={passive:!0,capture:!0},l=new Date,h=function(i,a){e||(e=a,t=i,n=new Date,S(removeEventListener),g())},g=function(){if(t>=0&&t1e12?new Date:performance.now())-e.timeStamp;"pointerdown"==e.type?function(e,t){var n=function(){h(e,t),a()},i=function(){a()},a=function(){removeEventListener("pointerup",n,v),removeEventListener("pointercancel",i,v)};addEventListener("pointerup",n,v),addEventListener("pointercancel",i,v)}(t,e):h(t,e)}},S=function(e){["mousedown","keydown","touchstart","pointerdown"].forEach((function(t){return e(t,y,v)}))};let E=document.createElement("script");E.src=chrome.runtime.getURL("script.bundle.js"),E.type="module",E.onload=function(){this.remove()},(document.head||document.documentElement).appendChild(E);const w={},L=({name:e,value:t})=>{w[e]=t,chrome.runtime.sendMessage({type:"performance:metric",name:e,value:t,id:"webMetric"})};!function(e){var t,n=a("TTFB");t=function(){try{var t=performance.getEntriesByType("navigation")[0]||function(){var e=performance.timing,t={entryType:"navigation",startTime:0};for(var n in e)"navigationStart"!==n&&"toJSON"!==n&&(t[n]=Math.max(e[n]-e.navigationStart,0));return t}();n.value=n.delta=t.responseStart,n.entries=[t],e(n)}catch(e){}},"complete"===document.readyState?setTimeout(t,0):addEventListener("pageshow",t)}(L),function(e,t){var n,i=p(),m=a("LCP"),d=function(e){var t=e.startTime;t { 16 | metrics[name] = value; 17 | 18 | chrome.runtime.sendMessage({ 19 | type: 'performance:metric', 20 | name, 21 | value, 22 | id: 'webMetric', 23 | }); 24 | }; 25 | 26 | // Functions that calculate web metric values. 27 | getTTFB(gatherMetrics); 28 | getLCP(gatherMetrics); 29 | getFID(gatherMetrics); 30 | getFCP(gatherMetrics); 31 | getCLS(gatherMetrics); 32 | 33 | 34 | // chrome.runtime.onConnect.addListener(function(port) { 35 | // console.assert(port.name == "content_script / NavBar"); 36 | // port.onMessage.addListener(function(msg) { 37 | // console.log("message recieved on Nav Bar" + msg); 38 | // }); 39 | // }); 40 | 41 | window.addEventListener('message', function(event) { 42 | // Only accept messages from the same frame 43 | if (event.source !== window) { 44 | return; 45 | } 46 | 47 | var message = event.data; 48 | 49 | // Only accept messages that we know are ours 50 | if (typeof message !== 'object' || message === null || 51 | !message.source === 'KangaVUE') { 52 | return; 53 | } 54 | console.log("content script sending message, ln45:", message) 55 | chrome.runtime.sendMessage(message); 56 | }); -------------------------------------------------------------------------------- /src/extension/build/devtools.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | KangaVue Devtools Page 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/extension/build/devtools.js: -------------------------------------------------------------------------------- 1 | chrome.devtools.panels.create('KangaVue', 'assets/oVUElord_logo.png', 'panel.html', () => {}); 2 | 3 | var backgroundPageConnection = chrome.runtime.connect({ 4 | name: "panel" 5 | }); 6 | 7 | let frontEndConnection = chrome.runtime.connect({ 8 | name: "frontEnd" 9 | }); 10 | 11 | backgroundPageConnection.postMessage({ 12 | name: 'init', 13 | tabId: chrome.devtools.inspectedWindow.tabId 14 | }); 15 | 16 | // let backgroundPageConnection = chrome.runtime.connect({ 17 | // name: "devtools-page" 18 | // }); 19 | 20 | backgroundPageConnection.onMessage.addListener(function (message,) { 21 | chrome.runtime.onMessage.addListener((message, callback) => { 22 | if (message == "runContentScript"){ 23 | chrome.scripting.executeScript({ 24 | file: 'content_script.js' 25 | }); 26 | }else if(message.id === 'filteredMap'){ 27 | // console.log("backpagecon:", backgroundPageConnection) 28 | // console.log('frontendcon:', frontEndConnection) 29 | console.log("msg:", message) 30 | console.log("message.tabId:", message.tabId) 31 | localStorage.setItem('treemap', JSON.stringify(message.map)) 32 | console.log("localstorage:", window.localStorage) 33 | } else if(message.id === 'webMetric'){ 34 | console.log('Web Metric Received!', message.name, message.value); 35 | localStorage.setItem(message.name, message.value); 36 | } 37 | });}); 38 | 39 | 40 | // // Relay the tab ID to the background page 41 | // chrome.runtime.sendMessage({ 42 | // tabId: chrome.devtools.inspectedWindow.tabId, 43 | // scriptToInject: "content_script.js" 44 | // }); 45 | 46 | -------------------------------------------------------------------------------- /src/extension/build/fontAwesome.js: -------------------------------------------------------------------------------- 1 | window.FontAwesomeKitConfig = {"asyncLoading":{"enabled":false},"autoA11y":{"enabled":true},"baseUrl":"https://ka-f.fontawesome.com","baseUrlKit":"https://kit.fontawesome.com","detectConflictsUntil":null,"iconUploads":{},"id":45213814,"license":"free","method":"css","minify":{"enabled":true},"token":"2babf6893c","v4FontFaceShim":{"enabled":true},"v4shim":{"enabled":true},"version":"5.15.3"}; 2 | !function(t){"function"==typeof define&&define.amd?define("kit-loader",t):t()}((function(){"use strict";function t(e){return(t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(e)}function e(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}function n(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(t);e&&(r=r.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),n.push.apply(n,r)}return n}function r(t){for(var r=1;rt.length)&&(e=t.length);for(var n=0,r=new Array(e);n2&&void 0!==arguments[2]?arguments[2]:function(){},o=e.document||o,i=u.bind(u,o,["fa","fab","fas","far","fal","fad","fak"]),f=Object.keys(t.iconUploads||{}).length>0;t.autoA11y.enabled&&n(i);var s=[{id:"fa-main",addOn:void 0}];t.v4shim.enabled&&s.push({id:"fa-v4-shims",addOn:"-v4-shims"}),t.v4FontFaceShim.enabled&&s.push({id:"fa-v4-font-face",addOn:"-v4-font-face"}),f&&s.push({id:"fa-kit-upload",customCss:!0});var d=s.map((function(n){return new _((function(o,i){P(n.customCss?a(t):c(t,{addOn:n.addOn,minify:t.minify.enabled}),e).then((function(i){o(U(i,r(r({},e),{},{baseUrl:t.baseUrl,version:t.version,id:n.id,contentFilter:function(t,e){return C(t,e.baseUrl,e.version)}})))})).catch(i)}))}));return _.all(d)}function U(t,e){var n=e.contentFilter||function(t,e){return t},r=document.createElement("style"),o=document.createTextNode(n(t,e));return r.appendChild(o),r.media="all",e.id&&r.setAttribute("id",e.id),e&&e.detectingConflicts&&e.detectionIgnoreAttr&&r.setAttributeNode(document.createAttribute(e.detectionIgnoreAttr)),r}function k(t,e){e.autoA11y=t.autoA11y.enabled,"pro"===t.license&&(e.autoFetchSvg=!0,e.fetchSvgFrom=t.baseUrl+"/releases/"+("latest"===t.version?"latest":"v".concat(t.version))+"/svgs",e.fetchUploadedSvgFrom=t.uploadsUrl);var n=[];return t.v4shim.enabled&&n.push(new _((function(n,o){P(c(t,{addOn:"-v4-shims",minify:t.minify.enabled}),e).then((function(t){n(I(t,r(r({},e),{},{id:"fa-v4-shims"})))})).catch(o)}))),n.push(new _((function(n,o){P(c(t,{minify:t.minify.enabled}),e).then((function(t){var o=I(t,r(r({},e),{},{id:"fa-main"}));n(function(t,e){var n=e&&void 0!==e.autoFetchSvg?e.autoFetchSvg:void 0,r=e&&void 0!==e.autoA11y?e.autoA11y:void 0;void 0!==r&&t.setAttribute("data-auto-a11y",r?"true":"false");n&&(t.setAttributeNode(document.createAttribute("data-auto-fetch-svg")),t.setAttribute("data-fetch-svg-from",e.fetchSvgFrom),t.setAttribute("data-fetch-uploaded-svg-from",e.fetchUploadedSvgFrom));return t}(o,e))})).catch(o)}))),_.all(n)}function I(t,e){var n=document.createElement("SCRIPT"),r=document.createTextNode(t);return n.appendChild(r),n.referrerPolicy="strict-origin",e.id&&n.setAttribute("id",e.id),e&&e.detectingConflicts&&e.detectionIgnoreAttr&&n.setAttributeNode(document.createAttribute(e.detectionIgnoreAttr)),n}function L(t){var e,n=[],r=document,o=r.documentElement.doScroll,i=(o?/^loaded|^c/:/^loaded|^i|^c/).test(r.readyState);i||r.addEventListener("DOMContentLoaded",e=function(){for(r.removeEventListener("DOMContentLoaded",e),i=1;e=n.shift();)e()}),i?setTimeout(t,0):n.push(t)}function T(t){"undefined"!=typeof MutationObserver&&new MutationObserver(t).observe(document,{childList:!0,subtree:!0})}try{if(window.FontAwesomeKitConfig){var x=window.FontAwesomeKitConfig,M={detectingConflicts:x.detectConflictsUntil&&new Date<=new Date(x.detectConflictsUntil),detectionIgnoreAttr:"data-fa-detection-ignore",fetch:window.fetch,token:x.token,XMLHttpRequest:window.XMLHttpRequest,document:document},D=document.currentScript,N=D?D.parentElement:document.head;(function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return"js"===t.method?k(t,e):"css"===t.method?F(t,e,(function(t){L(t),T(t)})):void 0})(x,M).then((function(t){t.map((function(t){try{N.insertBefore(t,D?D.nextSibling:null)}catch(e){N.appendChild(t)}})),M.detectingConflicts&&D&&L((function(){D.setAttributeNode(document.createAttribute(M.detectionIgnoreAttr));var t=function(t,e){var n=document.createElement("script");return e&&e.detectionIgnoreAttr&&n.setAttributeNode(document.createAttribute(e.detectionIgnoreAttr)),n.src=c(t,{baseFilename:"conflict-detection",fileSuffix:"js",subdir:"js",minify:t.minify.enabled}),n}(x,M);document.body.appendChild(t)}))})).catch((function(t){console.error("".concat("Font Awesome Kit:"," ").concat(t))}))}}catch(t){console.error("".concat("Font Awesome Kit:"," ").concat(t))}})); 3 | 4 | // script for manifest file 5 | // "content_security_policy": "script-src 'self' https://kit.fontawesome.com/2babf6893c.js; object-src 'self'", -------------------------------------------------------------------------------- /src/extension/build/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "KangaVue", 3 | "description": "Developer Tool for Vue", 4 | "version": "1.0", 5 | "permissions": [ 6 | "contextMenus", 7 | "tabs", 8 | "activeTab", 9 | "storage" 10 | ], 11 | "host_permissions": [ 12 | "http://localhost/*", 13 | "https://localhost/*" 14 | ], 15 | "manifest_version": 3, 16 | "minimum_chrome_version": "10.0", 17 | "devtools_page": "devtools.html", 18 | "background": { 19 | "service_worker": "background.bundle.js" 20 | }, 21 | "content_scripts": [ 22 | { 23 | "matches": [ 24 | "http://localhost/*", 25 | "https://localhost/*" 26 | ], 27 | "js": [ 28 | "content.bundle.js" 29 | ], 30 | "run_at": "document_start" 31 | } 32 | ], 33 | "web_accessible_resources": [ 34 | { 35 | "resources": [ 36 | "script.bundle.js" 37 | ], 38 | "matches": [ 39 | "http://localhost/*", 40 | "https://localhost/*" 41 | ] 42 | } 43 | ] 44 | 45 | } -------------------------------------------------------------------------------- /src/extension/build/panel.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | KangaVue Extension Panel 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/extension/build/script.bundle.js: -------------------------------------------------------------------------------- 1 | window.addEventListener("keydown",(()=>{let e=function e(n,t={children:[]}){if(!n.$children)return t;let i=n.$vnode.tag;return i.startsWith("vue-component-")&&(i=i.slice(14)),t.name=i,n.$children.forEach((n=>t.children.push(e(n)))),t}(window.__VUE_DEVTOOLS_INSTANCE_MAP__.get("1:3"));window.postMessage({map:e,source:"kangaVUE",id:"filteredMap"},"*")})); -------------------------------------------------------------------------------- /src/extension/build/script.js: -------------------------------------------------------------------------------- 1 | window.addEventListener('keydown', () => { 2 | function getMap(c, output = { children: [] }) { 3 | if (!c.$children) { 4 | return output; 5 | } 6 | let tag = c.$vnode.tag 7 | if(tag.startsWith("vue-component-")){ 8 | tag = tag.slice(14) 9 | } 10 | output.name = tag; 11 | c.$children.forEach((child) => output.children.push(getMap(child))); 12 | return output; 13 | } 14 | let map = getMap(window.__VUE_DEVTOOLS_INSTANCE_MAP__.get('1:3')) 15 | window.postMessage({ 16 | map: map, 17 | source: 'kangaVUE', 18 | id: 'filteredMap' 19 | }, '*') 20 | // chrome.storage.sync.set( 21 | // {currTab: window.__VUE_DEVTOOLS_INSTANCE_MAP__}, () => {console.log("stored on", currTab)} 22 | // ) 23 | }); -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const { VueLoaderPlugin } = require('vue-loader'); 3 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 4 | // const autoprefixer = require("autoprefixer"); 5 | // const ChromeExtensionReloader = require('webpack-chrome-extension-reloader'); // enable hot reloading while developing a chrome extension 6 | 7 | const isDevelopment = process.env.NODE_ENV !== 'production'; 8 | 9 | module.exports = { 10 | entry: { 11 | app: './src/app/index.js', 12 | background: './src/extension/build/background.js', 13 | content: './src/extension/build/content_script.js', 14 | script: './src/extension/build/script.js', 15 | // backend: './src/backend/index.ts', 16 | }, 17 | output: { 18 | path: path.resolve(__dirname, 'src/extension/build'), 19 | filename: '[name].bundle.js', 20 | }, 21 | module: { 22 | rules: [ 23 | { 24 | test: /\.txt$/, 25 | use: 'raw-loader', 26 | }, 27 | { 28 | test: /\.js$/, 29 | exclude: /node_modules/, 30 | use: { 31 | loader: 'babel-loader', 32 | }, 33 | }, 34 | { 35 | test: /\.vue$/, 36 | loader: 'vue-loader', 37 | options: { 38 | loaders: { 39 | // Since sass-loader (weirdly) has SCSS as its default parse mode, we map 40 | // the "scss" and "sass" values for the lang attribute to the right configs here. 41 | // other preprocessors should work out of the box, no loader config like this nessessary. 42 | scss: 'vue-style-loader!css-loader!sass-loader', 43 | sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax', 44 | }, 45 | // other vue-loader options go here 46 | }, 47 | }, 48 | { 49 | test: /\.css$/, 50 | use: ['vue-style-loader', 'css-loader'], 51 | }, 52 | // { 53 | // test: /\.s?css$/, 54 | // use: [ 55 | // "style-loader", 56 | // MiniCssExtractPlugin.loader, 57 | // "css-loader", 58 | // { 59 | // loader: "postcss-loader", 60 | // options: { 61 | // plugins: () => [autoprefixer()], 62 | // }, 63 | // }, 64 | // "sass-loader", 65 | // ], 66 | // }, 67 | // { 68 | // test: /\.module\.s(a|c)ss$/, 69 | // loader: [ 70 | // isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader, 71 | // { 72 | // loader: 'css-loader', 73 | // options: { 74 | // modules: true, 75 | // sourceMap: isDevelopment, 76 | // }, 77 | // }, 78 | // { 79 | // loader: 'sass-loader', 80 | // options: { 81 | // sourceMap: isDevelopment, 82 | // }, 83 | // }, 84 | // ], 85 | // }, 86 | // { 87 | // test: /\.s(a|c)ss$/, 88 | // exclude: /\.module.(s(a|c)ss)$/, 89 | // loader: [ 90 | // isDevelopment ? 'style-loader' : MiniCssExtractPlugin.loader, 91 | // 'css-loader', 92 | // { 93 | // loader: 'sass-loader', 94 | // options: { 95 | // sourceMap: isDevelopment, 96 | // }, 97 | // }, 98 | // ], 99 | // }, 100 | ], 101 | }, 102 | plugins: [ 103 | new VueLoaderPlugin(), 104 | // new MiniCssExtractPlugin(), 105 | new MiniCssExtractPlugin({ 106 | filename: isDevelopment ? '[name].css' : '[name].[hash].css', 107 | chunkFilename: isDevelopment ? '[id].css' : '[id].[hash].css', 108 | }), 109 | ], 110 | // resolve: { 111 | // // alias: { 112 | // // vue$: "vue/dist/vue.runtime.esm.js", 113 | // // }, 114 | // // extensions: ["*", ".js", ".vue", ".json"], 115 | // extensions: ['.js', '.jsx', '.scss'], 116 | // }, 117 | }; 118 | --------------------------------------------------------------------------------