[
  {
    "path": ".eslintrc.js",
    "content": "module.exports = {\n  env: {\n    browser: true,\n    es2020: true,\n  },\n  extends: [\n    'plugin:react/recommended',\n    'prettier',\n    'prettier/flowtype', // if you are using flow\n    'prettier/react',\n  ],\n  parser: 'babel-eslint',\n  parserOptions: {\n    ecmaFeatures: {\n      jsx: true,\n    },\n    ecmaVersion: 11,\n    sourceType: 'module',\n  },\n  plugins: ['flowtype', 'react', 'jsx-a11y', 'prettier'],\n  rules: {\n    'prettier/prettier': ['error'],\n    'react/prop-types': 'off',\n  },\n  settings: {\n    react: {\n      version: 'detect',\n    },\n  },\n};\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules\npackage-lock.json\n.vscode\nsrc/extension/build/bundles\n.DS_Store\n\npackage/node_modules/"
  },
  {
    "path": ".prettierrc",
    "content": "{\n  \"arrowParens\": \"avoid\",\n  \"singleQuote\": true,\n  \"trailingComma\": \"all\",\n  \"bracketSpacing\": false,\n  \"jsxBracketSameLine\": false,\n  \"endOfLine\": \"auto\",\n  \"overrides\": [\n    {\n      \"files\": [\"*.js\", \"*.jsx\"],\n      \"options\": {\n        \"parser\": \"flow\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 OSLabs Beta\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<meta name='keywords' content='Recoil, Recoil.js, Recoil Dev Tool, Recoilize, Chrome Dev Tool, Recoil Chrome'>\n\n<p align='center'>\n<img src='./src/extension/build/assets/cover-photo-logo-recoilize.jpg' width=100%>\n</p>\n\n<h1>Debugger for Recoil Applications</h1>\n\n# [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/oslabs-beta/Recoilize/blob/staging/LICENSE) [![npm version](https://img.shields.io/npm/v/recoilize)](https://www.npmjs.com/package/recoilize) ![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)\n\n[Korean README 한국어](README_KO.md)\n\n<h1> About</h1>\n<p>\nRecoilize is a Chrome Dev Tool meant for debugging applications built with the experimental Recoil.js state management library.\n\nThe tool records Recoil state and allows users to easily debug their applications with features such as: time travel to previous states, visualization of the component graph and display of the atom selector network.\n\n</p>\n\n<p>\nDownload Recoilize from the <a href='https://chrome.google.com/webstore/detail/recoilize/jhfmmdhbinleghabnblahfjfalfgidik'>Chrome Store</a>\n</p>\n\n<p>Visit the Recoilize <a href='https://www.recoilize.io/'>landing page</a> to demo</p>\n\n<h2>\n** STILL IN BETA **\n</h2>\n\n<p>Please note that Recoilize is in BETA. We will continue to make improvements and implement fixes but if you find any issues, please dont hesitate to report them in the issues tab or submit a PR and we'll happily take a look.</p>\n\n<h1>\nInstallation\n</h1>\n\n#### Install Recoilize Module\n\n```js\nnpm install recoilize\n```\n\n### ** IMPORTANT **\n\n#### Import RecoilizeDebugger from the Recoilize module\n\n```js\nimport RecoilizeDebugger from 'recoilize';\n```\n\n#### Integrate RecoilizeDebugger as a React component within the recoil root:\n\n```js\nimport RecoilizeDebugger from 'recoilize';\nimport RecoilRoot from 'recoil';\n\nReactDOM.render(\n  <RecoilRoot>\n    <RecoilizeDebugger />\n    <App />\n  </RecoilRoot>,\n  document.getElementById('root'),\n);\n```\n\n#### Please note, Recoilize assumes that the HTML element used to inject your React application has an ID of 'root'. If it does not the HTML element must be passed in as an attribute called 'root' to the RecoilizeDebugger component\n\n#### Example:\n\n```js\nimport RecoilizeDebugger from 'recoilize';\nimport RecoilRoot from 'recoil';\n\n//If your app injects on an element with ID of 'app'\nconst app = document.getElementById('app');\n\nReactDOM.render(\n  <RecoilRoot>\n    <RecoilizeDebugger root={app} />\n    <App />\n  </RecoilRoot>,\n  app,\n);\n```\n\n### In order to integrate Next.js applications with RecoilizeDebugger, follow the example below. \n\n```js\n//If your application uses Next.js modify the _app.js as follows\nimport dynamic from 'next/dynamic';\nimport { useEffect, useState } from 'react';\nimport { RecoilRoot } from 'recoil';\n\nfunction MyApp({ Component, pageProps }) {\n\n  const [root, setRoot] = useState(null)\n  const RecoilizeDebugger = dynamic(\n\t() => {\n\t  return import('recoilize');\n\t},\n\t{ ssr: false}\n  );\n\n  useEffect(() => {\n\n    if (typeof window.document !== 'undefined') {\n      setRoot(document.getElementById('__next'));\n    }\n  }, [root]);\n \n  return (\n    <>\n    <RecoilRoot>\n      <RecoilizeDebugger root = {root}/>\n      <Component {...pageProps} />\n    </RecoilRoot>\n    </>\n  );\n}\n\n\nexport default MyApp;\n\n```\n\n#### Open your application on the Chrome Browser and start debugging with Recoilize!\n\n##### (Only supported with React applications using Recoil as state management)\n\n<h1>New Features for Version 3.0.0</h1>\n<h3>Support for Recoil 0.1.3</h3>\n<p>Recoilize now supports the most recent update to the Recoil library and is backwards compatible with older versions of Recoil.</p>\n\n<h3>Time Travel with ease</h3>\n<p>If you had used Recoilize before, you would have noticed an annoying bug that sometimes breaks the app and won’t allow you to be productive. With the new version of Recoilize, that issue is forever gone. Users can now use the tool with confidence and worry-free.</p>\n\n<p>The main mission of Recoilize 3.0 is to make it more user-friendly, so you will enjoy our brand new time travel feature — the time travel slider! Why click and scroll through snapshots when you can do it with a slider and some buttons, right?</p>\n\n<p align='center'> \n<img src='./src/extension/build/assets/timeslider-gif.gif' width=600 height=300/>\n</p>\n\n<h3>Customizable Component Graph</h3>\n<p>This is one of the coolest updates of Recoilize 3.0. We understand that different users have different ways of thinking and visualizing, and for that reason, the component tree now is fully customizable. You can expand or collapse the components, choose vertical or horizontal displays or adjust the spacing between elements.</p>\n\n<p align='center'> \n<img src='./src/extension/build/assets/componentTree-gif.gif' width=600 height=300/>\n</p>\n\n<h3>Better User Experience with Atom Network</h3>\n<p>The atom network is one of the key features that differentiate Recoil.js from other alternative state management libraries. However, the atom network will grow bigger together with the app. At some points, it will be unmanageable and hard to keep track of all of the atoms. To make this easier and pain-free, the new Atom Network will allow you to freely move and arrange them anywhere you want.</p>\n\n<p align='center'> \n<img src='./src/extension/build/assets/atomNetwork-gif.gif' width=600 height=300/>\n</p>\n\n<h3>Snapshot Comparison</h3>\n<p>We understand that developers always develop an app with an optimization philosophy in mind. Component rendering time can be difficult to measure for two reasons. First, your computer and your web browser are not the same as mine, so the run-time can be vastly different. Second, it’s really hard to keep track of a long series of snapshots. You definitely don’t want to waste all of your time calculating the rendering time by hand.</p>\n\n<p>With the new Recoilize, users can now save a series of state snapshots and use it later to analyze/compare with the current series.</p>\n\n<p align='center'> \n<img src='./src/extension/build/assets/snapshotcomparison-gif.gif' width=600 height=300/>\n</p>\n\n<h1>Features</h1>\n<h3>Support for Concurrent Mode</h3>\n<p>If a Suspense component was used as a placeholder during component renderings, those suspense components will display with a red border in the expanded component graph. This indicates that a component was suspended during the render of the selected snapshot.</p>\n\n<p align='center'> \n<img src='./src/extension/build/assets/suspenseMode.gif' width=600 height=300/>\n</p>\n\n<h3>Performance Metrics</h3>\n<p>In 'Metrics' tab, two graphs display component render times.\n\nThe flame graph displays the time a component took to render itself, and all of its child components. The bar graph displays the individual render times of each component.<p>\n\n<!-- <p align='center'>\n<img src='./src/extension/build/assets/metrics.gif' width=600 height=300/>\n</p> -->\n\n<h3>Throttle</h3>\n<p>In the settings tab, users are able to set throttle (in milliseconds) for large scale applications or any applications that changes state rapidly. The default is set at 70ms.<p>\n\n<h3>State Persistence</h3>\n<p>Recoilize allows the users to persist their application's state through a refresh or reload. At this time, the user is able to view the previous states in the dev tool, but cannot time travel to the states before refresh.</p>\n\n<h3>Additional Features</h3>\n<ul><li>legend to see relationship between component graph and state</li></ul>\n<ul><li>toggle to view raw component graph</li></ul>\n<ul><li>filter atom/selector network relationship</li></ul>\n<ul><li>filter snapshots by atom/selector keys</li></ul>\n\n<h2> We will continue updating Recoilize alongside Recoil's updates!</h2>\n\n<h1>\n Contributors\n</h1>\n\n<h4>Bren Yamaguchi <a href='https://github.com/brenyama' target=“_blank”>@github </a><a  href='https://www.linkedin.com/in/brenyamaguchi/' target=“_blank”>@linkedin</a></h4>\n\n<h4>Saejin Kang <a  href='https://github.com/skang1004' target=“_blank”>@github </a><a  href='https://www.linkedin.com/in/saejinkang1004/' target=“_blank”>@linkedin</a></h4>\n\n<h4>Jonathan Escamila <a  href='https://github.com/jonescamilla' target=“_blank”>@github </a><a  href='https://www.linkedin.com/in/jon-escamilla/' target=“_blank”>@linkedin</a> </h4>\n\n<h4>Sean Smith <a  href='https://github.com/SmithSean17' target=“_blank”>@github </a><a  href='https://www.linkedin.com/in/sean-smith17/' target=“_blank”>@linkedin</a> </h4>\n\n<h4>Justin Choo <a href='https://github.com/justinchoo93' target=“_blank”>@github </a><a  href='https://www.linkedin.com/in/justinchoo93/' target=“_blank”>@linkedin</a></h4>\n\n<h4>Anthony Lin <a  href='https://github.com/anthonylin198' target=“_blank”>@github </a><a  href='https://www.linkedin.com/in/anthony-lin/' target=“_blank”>@linkedin</a></h4>\n\n<h4>Spenser Schwartz <a  href='https://github.com/spenserschwartz' target=“_blank”>@github </a><a  href='https://www.linkedin.com/in/spenser-schwartz/' target=“_blank”>@linkedin</a> </h4>\n\n<h4>Steven Nguyen <a  href='https://github.com/Steven-Nguyen-T' target=“_blank”>@github </a><a  href='https://www.linkedin.com/in/steven-nguyen-t/' target=“_blank”>@linkedin</a> </h4>\n\n<h4>Henry Taing <a  href='https://github.com/henrytaing' target=“_blank”>@github </a><a  href='https://www.linkedin.com/in/henrytaing/' target=“_blank”>@linkedin</a> </h4>\n\n<h4>Seungho Baek <a  href='https://github.com/hobaek' target=“_blank”>@github </a><a  href='https://www.linkedin.com/in/s2unghobaek/' target=“_blank”>@linkedin</a> </h4>\n\n<h4>Aaron Yang <a  href='https://github.com/aaronyang24' target=“_blank”>@github </a><a  href='https://www.linkedin.com/in/aaronyang24/' target=“_blank”>@linkedin</a> </h4>\n\n<h4>Jesus Vargas <a  href='https://github.com/jmodestov' target=“_blank”>@github </a><a  href='https://www.linkedin.com/in/jesus-modesto-vargas/' target=“_blank”>@linkedin</a> </h4>\n\n<h4>Davide Molino <a  href='https://github.com/davidemmolino' target=“_blank”>@github </a><a  href='https://www.linkedin.com/in/davide-molino/' target=“_blank”>@linkedin</a> </h4>\n\n<h4>Taven Shumaker <a  href='https://github.com/TavenShumaker' target=“_blank”>@github </a><a  href='https://www.linkedin.com/in/Taven-Shumaker/' target=“_blank”>@linkedin</a> </h4>\n\n<h4>Janis Hernandez <a  href='https://github.com/Janis-H' target=“_blank”>@github </a><a  href='https://www.linkedin.com/in/janis-h/' target=“_blank”>@linkedin</a> </h4>\n\n<h4>Jaime Baik <a  href='https://github.com/jaimebaik' target=“_blank”>@github </a><a  href='https://www.linkedin.com/in/jaime-baik/' target=“_blank”>@linkedin</a> </h4>\n\n<h4>Anthony Magallanes <a  href='https://github.com/amagalla' target=“_blank”>@github </a><a  href='https://www.linkedin.com/in/anthony-magallanes/' target=“_blank”>@linkedin</a> </h4>\n\n<h4>Edward Shei <a  href='https://github.com/calibeach' target=“_blank”>@github </a><a  href='https://www.linkedin.com/in/edwardshei/' target=“_blank”>@linkedin</a> </h4>\n\n<h4>Nathan Bargers <a  href='https://github.com/nbargers' target=“_blank”>@github </a><a  href='https://www.linkedin.com/in/nathan-bargers/' target=“_blank”>@linkedin</a> </h4>\n\n<h4>Scott Campbell <a  href='https://github.com/thisisscottcampbell' target=“_blank”>@github </a><a  href='https://www.linkedin.com/in/thisisscottcampbell/' target=“_blank”>@linkedin</a> </h4>\n\n<h4>Steve Hong <a  href='https://github.com/stevehong423' target=“_blank”>@github </a><a  href='https://www.linkedin.com/in/stevehongpa/' target=“_blank”>@linkedin</a> </h4>\n\n<h4>Jason Lee <a  href='https://github.com/j4s0n1020' target=“_blank”>@github </a><a  href='https://www.linkedin.com/in/jasonjml/' target=“_blank”>@linkedin</a> </h4>\n\n<h4>Razana Nisathar <a  href='https://github.com/razananisathar' target=“_blank”>@github </a><a  href='http://www.linkedin.com/in/razananisathar' target=“_blank”>@linkedin</a> </h4>\n\n<h4>Harvey Nguyen <a  href='https://github.com/harveynwynn' target=“_blank”>@github </a><a  href='https://www.linkedin.com/in/harveynwynn' target=“_blank”>@linkedin</a> </h4>\n\n<h4>Joey Ma <a  href='https://github.com/yoyoyojoe' target=“_blank”>@github </a><a  href='https://www.linkedin.com/in/joeyma' target=“_blank”>@linkedin</a> </h4>\n\n<h4>Leonard Lew <a  href='https://github.com/leolew97' target=“_blank”>@github </a><a  href='https://www.linkedin.com/in/leonardlew' target=“_blank”>@linkedin</a> </h4>\n\n<h4>Victor Wang <a  href='https://github.com/wangvwr' target=“_blank”>@github </a><a  href='https://www.linkedin.com/in/wangvwr' target=“_blank”>@linkedin</a> </h4>"
  },
  {
    "path": "README_KO.md",
    "content": "<meta name='keywords' content='Recoil, Recoil.js, Recoil Dev Tool, Recoilize, Chrome Dev Tool, Recoil Chrome'>\n\n<p align='center'>\n<img src='./src/extension/build/assets/cover-photo-logo-recoilize.jpg' width=100%>\n</p>\n\n<h1>Recoil 애플리케이션을 위한 디버깅 개발도구</h1>\n\n# [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/oslabs-beta/Recoilize/blob/staging/LICENSE) [![npm version](https://img.shields.io/npm/v/recoilize)](https://www.npmjs.com/package/recoilize) ![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)\n\n[영어 README](README.md)\n\n<h1>Recoilize에 대해서</h1>\n<p>\nRecoilize는 Recoil 상태관리 라이브러리를 사용하여 만들어진 애플리케이션을 디버깅 할수있는 Chrome Dev Tool입니다.\n\nRecoil 상태를 기록하여 유저들이 애플리케이션을 편하게 디버깅 할수 있도록 도와주는 기능들을 가지고 있습니다. 리액트 컴포넌트를 시각화 하여 그래프로 보여줌과 동시에 스냅샷을 이요하여 이전 상태로 시간이동을 가능하게 만들어줄수있는 도구입니다.\n\n</p>\n<p>\n<a href='https://chrome.google.com/webstore/detail/recoilize/jhfmmdhbinleghabnblahfjfalfgidik'>크롬 스토어</a> 에서 다운로드 받으실수 있습니다.\n</p>\n\n<!-- <p>데모  <a href='https://github.com/justinchoo93/recoil-paint'>페인트 애플리케이션</a></p> -->\n<p>데모를 위해서는 <a href='https://www.recoilize.io/'>Recoilize</a> 웹사이트를 방문하십시오.</p>\n\n<h2>\n** 현재는 베타 버젼입니다 **\n</h2>\n\n<p>Recoilize는 현재 베타버젼 입니다. 툴을 계속 개선하고 새로운 이슈들을 수정해 나갈것이고, 혹시 다른 버그들이나 이슈들이 나타난다면 언제든지 이슈 탭에 글을 작성하시거나 PR을 해주시면 감사하겠습니다.</p>\n\n<h1>\n설치 방법\n</h1>\n\n#### Recoilize 모듈 설치\n\n```js\nnpm install recoilize\n```\n\n### ** 중요 **\n\n#### Recoilize모듈에서 RecoilizeDebugger를 import해줘야 합니다\n\n```js\nimport RecoilizeDebugger from 'recoilize';\n```\n\n#### RecoilizeDebugger를 recoil root 안에 리액트 컴포넌트로 넣어야 합니다\n\n```js\nimport RecoilizeDebugger from 'recoilize';\nimport RecoilRoot from 'recoil';\n\nReactDOM.render(\n  <RecoilRoot>\n    <RecoilizeDebugger />\n    <App />\n  </RecoilRoot>,\n  document.getElementById('root'),\n);\n```\n\n#### Recoilize는 리액트 애플리케이션을 주입시키기 위해 쓴 HTML 엘리먼트의 아이디를 'root'으로 가정합니다.아닐경우 RecoilizeDebugger에 'root'속성을 만들고 HTML 엘리먼트를 패스하십시오.\n\n```js\nimport RecoilizeDebugger from 'recoilize';\nimport RecoilRoot from 'recoil';\n\n//If your app injects on an element with ID of 'app'\nconst app = document.getElementById('app');\n\nReactDOM.render(\n  <RecoilRoot>\n    <RecoilizeDebugger root={app} />\n    <App />\n  </RecoilRoot>,\n  app,\n);\n```\n\n#### 애플리케이션을 크롬 브라우저에서 열고 Recoilize 디버깅툴을 실행하시면 됩니다.\n\n##### (현재 Recoil을 상태관리 라이브러리로 사용하는 리액트 애플리케이션만 지원합니다.)\n\n<h1>새로운 기능</h1>\n<h3>Recoil 0.1.2를 지원합니다</h3>\n<p>Recoilize는 최신 버전과 구버전의 Recoil과 호환이 됩니다</p>\n\n<h3>스냅샷 클리어</h3>\n<p>Previous와 Forward 버튼을 넣어 선택된 스냅샵의 전이나 후에 있는 스냅샷을 지울 수 있게 했습니다</p>\n\n<p align='center'> \n<img src='./src/extension/build/assets/clearButtons.gif' width=600 height=300/>\n</p>\n\n<h3>컴포넌트 그래프</h3>\n<h4>호버</h4>\n<p>그래프의 노드를 호버했을 때 안의 텍스트가 보이는 형태를 개선하였습니다</p>\n<h4>atom 범례</h4>\n<p>범례의 텍스트가 클릭되면 드롭다운 형태의 atom이나 selector 리스트가 보이게 하였습니다</p>\n<p>드롭다운 리스트에 있는 각각의 atom이나 selector를 누를 경우 해당 atom이나 selector를 쓰는 컴포넌트가 하이라이트되도록 바꾸었습니다</p>\n\n<p align='center'> \n<img src='./src/extension/build/assets/componentGraph.gif' width=600 height=300/>\n</p>\n\n<h3>atom 네트워크</h3>\n<h4>atom 범례</h4>\n<p>범례의 텍스트가 클릭되면 드롭다운 형태의 atom이나 selector 리스트가 보이게 하였습니다</p>\n<p>드롭다운 리스트에 있는 각각의 atom이나 selector를 누를 경우 관련 atom이나 selector 노드가 보이도록 했습니다</p>\n<h4>그래프</h4>\n<p>여러개의 그래프가 겹치지 않도록 조정하였습니다</p>\n<h4>검색 창</h4>\n<p>검색 창이 탐색 버튼과 겹치지 않도록 변경하였습니다</p>\n\n<p align='center'> \n<img src='./src/extension/build/assets/atomNetwork.gif' width=600 height=300/>\n</p>\n\n<h3>Ranked 그래프</h3>\n<p>애니메이션을 없애서 전과 후 상태비교가 쉽게 보이도록 바꾸었습니다</p>\n\n<p align='center'> \n<img src='./src/extension/build/assets/rankedGraph.gif' width=600 height=300/>\n</p>\n\n<h1>기능</h1>\n<h3>Concurrent 모드 지원</h3>\n<p>만약 컴포넌트를 보류시키기 위해 Suspense 컴포넌트가 사용됐을 경우, 해당 컴포넌트의 노드 주위에 빨간 테두리로 표시하여 컴포넌트가 나타나기까지 지연되었음을 알려줄 것입니다</p>\n\n<p align='center'> \n<img src='./src/extension/build/assets/suspenseMode.gif' width=600 height=300/>\n</p>\n\n<h3>퍼포먼스 측정 그래프</h3>\n<p>'Metrics' 탭에 있는 두가지 그래프는 렌더링 시간을 보여줍니다</p>\n<p>Flame 그래프는 각각의 컴포넌트와 자식 컴포넌트가 나타나기까지 걸린 합산된 시간을 보여주고 Ranked 그래프는 각각의 컴포넌트가 나오기까지 걸린 시간을 보여줍니다</p>\n\n<h3>시간 이동</h3>\n<p>Recoilize의 주요 기능 중 하나로, 이 도구는 사용자가 이전의 모든 스냅샷으로 이동할 수 있게 해줍니다. 각 스냅샷 옆에 있는 점프 버튼을 누르면 해당 스냅샷으로 상태를 설정하여 DOM이 변경됩니다.<p>\n\n<p align='center'> \n<img src='./src/extension/build/assets/timeTravel.gif' width=600 height=300/>\n</p>\n\n<h3>시각화</h3>\n<p>사용자는 개별 스냅샷을 클릭하여 애플리케이션 상태에 대한 시각화된 그래프를 볼수있고, 컴포넌트 트리와 다른 그래프 뿐만 아니라 State tree를 JSON 형식으로 지원합니다<p>\n\n<h3>쓰로틀링</h3>\n<p>대규모 애플리케이션 또는 상태를 빠르게 변경하는 모든 애플리케이션에 대해 쓰로틀링(ms)을 설정할 수 있습니다. 기본값은 70ms로 설정되어 있습니다.<p>\n\n<h3>상태 유지 (베타)</h3>\n<p>Recoilize는 사용자가 새로 고침을 했을 경우에도 응용 프로그램의 상태를 유지할 수 있도록 해줍니다. 이때 사용자는 개발 도구에서는 이전 상태를 볼 수 있지만, 새로고침 전에 상태로의 시간 이동은 할 수 없습니다. 우리 팀은 여전히 이 기능을 완성하기 위해 노력하고 있습니다.</p>\n\n<h3>부가 기능</h3>\n<ul><li>컴포넌트 그래프에 마우스를 올렸을때 관련있는 atom과 selector들이 나타납니다</li></ul>\n<ul><li>컴포넌트 그래프 안에 오른쪽 작은 창에서 관련된 상태들을 선택하여 볼  있습니다</li></ul>\n<ul><li>컴포넌트 그래프 안에 Expand 버튼을 누르면 확장된 컴포넌트 그래프를 볼 수 있습니다</li></ul>\n<ul><li>네트워크 그래프 안에 atom과 selector들을 볼수있고 필터링도 가능합니다.</li></ul>\n<ul><li>설정탭에서 atom과 selector key를 사용하여 관련된 스냅샷들을 필터링 할 수 있습니다</li></ul>\n\n<h2>우리는 Recoil의 업데이트와 함께 Recoilize를 계속 업데이트 할 것 입니다</h2>\n\n<h1>\n 기여\n</h1>\n\n<h4>Bren Yamaguchi <a href='https://github.com/brenyama' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/brenyamaguchi/' target=\"_blank\">@linkedin</a></h4>\n\n<h4>Saejin Kang <a  href='https://github.com/skang1004' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/saejinkang1004/' target=\"_blank\">@linkedin</a></h4>\n\n<h4>Jonathan Escamila <a  href='https://github.com/jonescamilla' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/jon-escamilla/' target=\"_blank\">@linkedin</a> </h4>\n\n<h4>Sean Smith <a  href='https://github.com/SmithSean17' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/sean-smith17/' target=\"_blank\">@linkedin</a> </h4>\n\n<h4>Justin Choo <a href='https://github.com/justinchoo93' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/justinchoo93/' target=\"_blank\">@linkedin</a></h4>\n\n<h4>Anthony Lin <a  href='https://github.com/anthonylin198' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/anthony-lin/' target=\"_blank\">@linkedin</a></h4>\n\n<h4>Spenser Schwartz <a  href='https://github.com/spenserschwartz' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/spenser-schwartz/' target=\"_blank\">@linkedin</a> </h4>\n\n<h4>Steven Nguyen <a  href='https://github.com/Steven-Nguyen-T' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/steven-nguyen-t/' target=\"_blank\">@linkedin</a> </h4>\n\n<h4>Henry Taing <a  href='https://github.com/henrytaing' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/henrytaing/' target=\"_blank\">@linkedin</a> </h4>\n\n<h4>Seungho Baek <a  href='https://github.com/hobaek' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/s2unghobaek/' target=\"_blank\">@linkedin</a> </h4>\n\n<h4>Aaron Yang <a  href='https://github.com/aaronyang24' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/aaronyang24/' target=\"_blank\">@linkedin</a> </h4>\n\n<h4>Jesus Vargas <a  href='https://github.com/jmodestov' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/jesus-modesto-vargas/' target=\"_blank\">@linkedin</a> </h4>\n\n<h4>Davide Molino <a  href='https://github.com/davidemmolino' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/davide-molino/' target=\"_blank\">@linkedin</a> </h4>\n\n<h4>Taven Shumaker <a  href='https://github.com/TavenShumaker' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/Taven-Shumaker/' target=\"_blank\">@linkedin</a> </h4>\n\n<h4>Janis Hernandez <a  href='https://github.com/Janis-H' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/janis-hernandez-aguilar/' target=\"_blank\">@linkedin</a> </h4>\n\n<h4>Jaime Baik <a  href='https://github.com/jaimebaik' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/jaime-baik/' target=\"_blank\">@linkedin</a> </h4>\n\n<h4>Anthony Magallanes <a  href='https://github.com/amagalla' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/anthony-magallanes/' target=\"_blank\">@linkedin</a> </h4>\n\n<h4>Edward Shei <a  href='https://github.com/calibeach' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/edwardshei/' target=\"_blank\">@linkedin</a> </h4>\n"
  },
  {
    "path": "babel.config.js",
    "content": "module.exports = {\n  presets: [\n    '@babel/preset-env',\n    '@babel/preset-react',\n    '@babel/preset-typescript',\n  ],\n  plugins: ['@babel/plugin-proposal-class-properties'],\n};\n"
  },
  {
    "path": "docs/pull_request_template.md",
    "content": "## Types of changes\n<!--- What types of changes does your code introduce to Scratch Project? Put an `x` in the boxes that apply. -->\n- [ ] Bugfix (change which fixes an issue)\n- [ ] New feature (change which adds functionality)\n- [ ] Refactor (change which changes the codebase without affecting its external behavior)\n- [ ] Non-breaking change (fix or feature that would causes existing functionality to work as expected)\n- [ ] Breaking change (fix or feature that would cause existing functionality to __not__ work as expected)\n## Purpose\n<!--- Describe the problem or feature. Link to the issue(s) fixed by this pull request if applicable. -->\n## Approach\n<!--- How does your change address the problem? -->\n## Resources\n<!--- Describe the research stage. Link to any blog posts, video, patterns, libraries, addons, or other resources that helped you to solve this problem. -->\n## Screenshot(s)\n<!--- (if applicable--you can delete otherwise) -->\n<!--- Include a screenshot here if the change you made changes the look of the site in any way! -->\n"
  },
  {
    "path": "mock/snapshot.js",
    "content": "export const filteredCurSnapMock = {\n  dummyAtom1: {\n    contents: {hello: [], hi: []},\n    nodeDeps: [],\n    nodeToNodeSubscriptions: [],\n    type: 'RecoilState',\n  },\n  listState: {\n    contents: [{text: 'list item'}, {text: 'list item'}, {text: 'list item'}],\n    nodeDeps: [],\n    nodeToNodeSubscriptions: ['selectorTest', 'stateLengths'],\n    type: 'RecoilState',\n  },\n  listState2: {\n    contents: [{text: 'list item'}, {text: 'list item'}, {text: 'list item'}],\n    nodeDeps: [],\n    nodeToNodeSubscriptions: ['stateLengths'],\n    type: 'RecoilState',\n  },\n  selectorTest: {\n    contents: 'test',\n    nodeDeps: ['listState'],\n    nodeToNodeSubscriptions: [],\n    type: 'RecoilValueReadOnly',\n  },\n  stateLengths: {\n    contents: 6,\n    nodeDeps: ['listState', 'listState2'],\n    nodeToNodeSubscriptions: [],\n    type: 'RecoilValueReadOnly',\n  },\n};\n\nexport const filteredPrevSnapMock = {\n  dummyAtom1: {\n    contents: {hello: [], hi: []},\n    nodeDeps: [],\n    nodeToNodeSubscriptions: [],\n    type: 'RecoilState',\n  },\n  listState: {\n    contents: [{text: 'list item'}, {text: 'list item'}, {text: 'list item'}],\n    nodeDeps: [],\n    nodeToNodeSubscriptions: ['selectorTest', 'stateLengths'],\n    type: 'RecoilState',\n  },\n  listState2: {\n    contents: [\n      {text: 'list item'},\n      {text: 'list item'},\n      {text: 'list item'},\n      {text: 'list item'},\n    ],\n    nodeDeps: [],\n    nodeToNodeSubscriptions: ['stateLengths'],\n    type: 'RecoilState',\n  },\n  selectorTest: {\n    contents: 'test',\n    nodeDeps: ['listState'],\n    nodeToNodeSubscriptions: [],\n    type: 'RecoilValueReadOnly',\n  },\n  stateLengths: {\n    contents: 6,\n    nodeDeps: ['listState', 'listState2'],\n    nodeToNodeSubscriptions: [],\n    type: 'RecoilValueReadOnly',\n  },\n};\n\nexport const componentAtomTreeMock = {\n  children: [\n    {\n      children: [],\n      name: '',\n      tag: 0,\n    },\n    {\n      children: [\n        {\n          children: [],\n          name: '',\n          tag: 0,\n        },\n        {\n          children: [\n            {\n              children: [],\n              name: '',\n              tag: 0,\n            },\n            {\n              children: [\n                {\n                  children: [],\n                  name: '',\n                  tag: 0,\n                },\n              ],\n              name: '',\n              tag: 0,\n            },\n          ],\n          name: '',\n          tag: 0,\n        },\n      ],\n      name: '',\n      tag: 0,\n    },\n    {\n      children: [\n        {\n          children: [],\n          name: '',\n          tag: 0,\n        },\n        {\n          children: [],\n          name: '',\n          tag: 0,\n        },\n      ],\n      name: '',\n      tag: 0,\n    },\n  ],\n  name: '',\n  tag: 0,\n};\n"
  },
  {
    "path": "mock/state-snapshot.js",
    "content": "export const snapshotHistoryMock = {\n  snapshotHistory: [\n    {\n      componentAtomTree: {\n        actualDuration: 1.6750000004321919,\n        children: [\n          {\n            actualDuration: 1.6650000006848131,\n            children: [\n              {\n                actualDuration: 1.6650000006848131,\n                children: [\n                  {\n                    actualDuration: 1.6650000006848131,\n                    children: [\n                      {\n                        actualDuration: 0,\n                        children: [],\n                        name: 'Batcher',\n                        recoilNodes: [],\n                        tag: 0,\n                        treeBaseDuration: 0.15499999972234946,\n                        wasSuspended: false,\n                      },\n                      {\n                        actualDuration: 1.6650000006848131,\n                        children: [\n                          {\n                            actualDuration: 0,\n                            children: [],\n                            name: 'RecoilizeDebugger',\n                            recoilNodes: [],\n                            tag: 0,\n                            treeBaseDuration: 1.4600000004065805,\n                            wasSuspended: false,\n                          },\n                          {\n                            actualDuration: 1.6650000006848131,\n                            children: [\n                              {\n                                actualDuration: 0.8450000004813774,\n                                children: [\n                                  {\n                                    actualDuration: 0.6400000002031447,\n                                    children: [\n                                      {\n                                        actualDuration: 0.24500000017724233,\n                                        children: [],\n                                        name: 'p',\n                                        recoilNodes: [],\n                                        tag: 5,\n                                        treeBaseDuration: 0.03500000002532033,\n                                        wasSuspended: false,\n                                      },\n                                      {\n                                        actualDuration: 0.009999999747378752,\n                                        children: [],\n                                        name: 'p',\n                                        recoilNodes: [],\n                                        tag: 5,\n                                        treeBaseDuration: 0.004999999873689376,\n                                        wasSuspended: false,\n                                      },\n                                      {\n                                        actualDuration: 0.12000000060652383,\n                                        children: [\n                                          {\n                                            actualDuration: 0.054999999520077836,\n                                            children: [],\n                                            name: 'button',\n                                            recoilNodes: [],\n                                            tag: 5,\n                                            treeBaseDuration: 0.030000000151630957,\n                                            wasSuspended: false,\n                                          },\n                                          {\n                                            actualDuration: 0.03500000002532033,\n                                            children: [],\n                                            name: 'button',\n                                            recoilNodes: [],\n                                            tag: 5,\n                                            treeBaseDuration: 0.004999999873689376,\n                                            wasSuspended: false,\n                                          },\n                                        ],\n                                        name: 'div',\n                                        recoilNodes: [],\n                                        tag: 5,\n                                        treeBaseDuration: 0.05500000042957254,\n                                        wasSuspended: false,\n                                      },\n                                    ],\n                                    name: 'div',\n                                    recoilNodes: [],\n                                    tag: 5,\n                                    treeBaseDuration: 0.3500000002532033,\n                                    wasSuspended: false,\n                                  },\n                                ],\n                                name: 'PlaygroundStart',\n                                recoilNodes: [],\n                                tag: 0,\n                                treeBaseDuration: 0.5550000005314359,\n                                wasSuspended: false,\n                              },\n                            ],\n                            name: 'PlaygroundRender',\n                            recoilNodes: ['playStart'],\n                            tag: 0,\n                            treeBaseDuration: 1.3750000007348717,\n                            wasSuspended: false,\n                          },\n                        ],\n                        name: 'Fragment',\n                        recoilNodes: [],\n                        tag: 7,\n                        treeBaseDuration: 2.8550000006362097,\n                        wasSuspended: false,\n                      },\n                    ],\n                    name: 'react.provider',\n                    recoilNodes: [],\n                    tag: 10,\n                    treeBaseDuration: 3.050000000257569,\n                    wasSuspended: false,\n                  },\n                ],\n                name: 'react.provider',\n                recoilNodes: [],\n                tag: 10,\n                treeBaseDuration: 3.0849999993733945,\n                wasSuspended: false,\n              },\n            ],\n            name: 'RecoilRoot',\n            recoilNodes: [],\n            tag: 0,\n            treeBaseDuration: 3.2199999996009865,\n            wasSuspended: false,\n          },\n        ],\n        name: 'HR',\n        recoilNodes: [],\n        tag: 3,\n        treeBaseDuration: 3.3249999996769475,\n        wasSuspended: false,\n      },\n      filteredSnapshot: {\n        currentPlayerState: {\n          contents: 'X',\n          nodeDeps: [],\n          nodeToNodeSubscriptions: ['gameEndSelector'],\n          type: 'RecoilState',\n        },\n        gameEndSelector: {\n          contents: false,\n          nodeDeps: [\n            'square-0',\n            'square-1',\n            'square-2',\n            'square-3',\n            'square-4',\n            'square-5',\n            'square-6',\n            'square-7',\n            'square-8',\n            'currentPlayerState',\n          ],\n          nodeToNodeSubscriptions: [],\n          type: 'RecoilValueReadOnly',\n        },\n        playStart: {\n          contents: false,\n          nodeDeps: [],\n          nodeToNodeSubscriptions: [],\n          type: 'RecoilState',\n        },\n        'square-0': {\n          contents: '-',\n          nodeDeps: [],\n          nodeToNodeSubscriptions: ['gameEndSelector'],\n          type: 'RecoilState',\n        },\n        'square-1': {\n          contents: '-',\n          nodeDeps: [],\n          nodeToNodeSubscriptions: ['gameEndSelector'],\n          type: 'RecoilState',\n        },\n        'square-2': {\n          contents: '-',\n          nodeDeps: [],\n          nodeToNodeSubscriptions: ['gameEndSelector'],\n          type: 'RecoilState',\n        },\n        'square-3': {\n          contents: '-',\n          nodeDeps: [],\n          nodeToNodeSubscriptions: ['gameEndSelector'],\n          type: 'RecoilState',\n        },\n        'square-4': {\n          contents: '-',\n          nodeDeps: [],\n          nodeToNodeSubscriptions: ['gameEndSelector'],\n          type: 'RecoilState',\n        },\n        'square-5': {\n          contents: '-',\n          nodeDeps: [],\n          nodeToNodeSubscriptions: ['gameEndSelector'],\n          type: 'RecoilState',\n        },\n        'square-6': {\n          contents: '-',\n          nodeDeps: [],\n          nodeToNodeSubscriptions: ['gameEndSelector'],\n          type: 'RecoilState',\n        },\n        'square-7': {\n          contents: '-',\n          nodeDeps: [],\n          nodeToNodeSubscriptions: ['gameEndSelector'],\n          type: 'RecoilState',\n        },\n        'square-8': {\n          contents: '-',\n          nodeDeps: [],\n          nodeToNodeSubscriptions: ['gameEndSelector'],\n          type: 'RecoilState',\n        },\n      },\n      indexDiff: 0,\n    },\n    {\n      componentAtomTree: {\n        actualDuration: 1.6750000004321919,\n        children: [\n          {\n            actualDuration: 1.6650000006848131,\n            children: [\n              {\n                actualDuration: 1.6650000006848131,\n                children: [\n                  {\n                    actualDuration: 1.6650000006848131,\n                    children: [\n                      {\n                        actualDuration: 0,\n                        children: [],\n                        name: 'Batcher',\n                        recoilNodes: [],\n                        tag: 0,\n                        treeBaseDuration: 0.15499999972234946,\n                        wasSuspended: false,\n                      },\n                      {\n                        actualDuration: 1.6650000006848131,\n                        children: [\n                          {\n                            actualDuration: 0,\n                            children: [],\n                            name: 'RecoilizeDebugger',\n                            recoilNodes: [],\n                            tag: 0,\n                            treeBaseDuration: 1.4600000004065805,\n                            wasSuspended: false,\n                          },\n                          {\n                            actualDuration: 1.6650000006848131,\n                            children: [\n                              {\n                                actualDuration: 0.8450000004813774,\n                                children: [\n                                  {\n                                    actualDuration: 0.6400000002031447,\n                                    children: [\n                                      {\n                                        actualDuration: 0.24500000017724233,\n                                        children: [],\n                                        name: 'p',\n                                        recoilNodes: [],\n                                        tag: 5,\n                                        treeBaseDuration: 0.03500000002532033,\n                                        wasSuspended: false,\n                                      },\n                                      {\n                                        actualDuration: 0.009999999747378752,\n                                        children: [],\n                                        name: 'p',\n                                        recoilNodes: [],\n                                        tag: 5,\n                                        treeBaseDuration: 0.004999999873689376,\n                                        wasSuspended: false,\n                                      },\n                                      {\n                                        actualDuration: 0.12000000060652383,\n                                        children: [\n                                          {\n                                            actualDuration: 0.054999999520077836,\n                                            children: [],\n                                            name: 'button',\n                                            recoilNodes: [],\n                                            tag: 5,\n                                            treeBaseDuration: 0.030000000151630957,\n                                            wasSuspended: false,\n                                          },\n                                          {\n                                            actualDuration: 0.03500000002532033,\n                                            children: [],\n                                            name: 'button',\n                                            recoilNodes: [],\n                                            tag: 5,\n                                            treeBaseDuration: 0.004999999873689376,\n                                            wasSuspended: false,\n                                          },\n                                        ],\n                                        name: 'div',\n                                        recoilNodes: [],\n                                        tag: 5,\n                                        treeBaseDuration: 0.05500000042957254,\n                                        wasSuspended: false,\n                                      },\n                                    ],\n                                    name: 'div',\n                                    recoilNodes: [],\n                                    tag: 5,\n                                    treeBaseDuration: 0.3500000002532033,\n                                    wasSuspended: false,\n                                  },\n                                ],\n                                name: 'PlaygroundStart',\n                                recoilNodes: [],\n                                tag: 0,\n                                treeBaseDuration: 0.5550000005314359,\n                                wasSuspended: false,\n                              },\n                            ],\n                            name: 'PlaygroundRender',\n                            recoilNodes: ['playStart'],\n                            tag: 0,\n                            treeBaseDuration: 1.3750000007348717,\n                            wasSuspended: false,\n                          },\n                        ],\n                        name: 'Fragment',\n                        recoilNodes: [],\n                        tag: 7,\n                        treeBaseDuration: 2.8550000006362097,\n                        wasSuspended: false,\n                      },\n                    ],\n                    name: 'react.provider',\n                    recoilNodes: [],\n                    tag: 10,\n                    treeBaseDuration: 3.050000000257569,\n                    wasSuspended: false,\n                  },\n                ],\n                name: 'react.provider',\n                recoilNodes: [],\n                tag: 10,\n                treeBaseDuration: 3.0849999993733945,\n                wasSuspended: false,\n              },\n            ],\n            name: 'RecoilRoot',\n            recoilNodes: [],\n            tag: 0,\n            treeBaseDuration: 3.2199999996009865,\n            wasSuspended: false,\n          },\n        ],\n        name: 'HR',\n        recoilNodes: [],\n        tag: 3,\n        treeBaseDuration: 3.3249999996769475,\n        wasSuspended: false,\n      },\n      filteredSnapshot: {\n        currentPlayerState: {\n          contents: 'X',\n          nodeDeps: [],\n          nodeToNodeSubscriptions: ['gameEndSelector'],\n          type: 'RecoilState',\n        },\n        gameEndSelector: {\n          contents: false,\n          nodeDeps: [\n            'square-0',\n            'square-1',\n            'square-2',\n            'square-3',\n            'square-4',\n            'square-5',\n            'square-6',\n            'square-7',\n            'square-8',\n            'currentPlayerState',\n          ],\n          nodeToNodeSubscriptions: [],\n          type: 'RecoilValueReadOnly',\n        },\n        playStart: {\n          contents: false,\n          nodeDeps: [],\n          nodeToNodeSubscriptions: [],\n          type: 'RecoilState',\n        },\n        'square-0': {\n          contents: '-',\n          nodeDeps: [],\n          nodeToNodeSubscriptions: ['gameEndSelector'],\n          type: 'RecoilState',\n        },\n        'square-1': {\n          contents: '-',\n          nodeDeps: [],\n          nodeToNodeSubscriptions: ['gameEndSelector'],\n          type: 'RecoilState',\n        },\n        'square-2': {\n          contents: '-',\n          nodeDeps: [],\n          nodeToNodeSubscriptions: ['gameEndSelector'],\n          type: 'RecoilState',\n        },\n        'square-3': {\n          contents: '-',\n          nodeDeps: [],\n          nodeToNodeSubscriptions: ['gameEndSelector'],\n          type: 'RecoilState',\n        },\n        'square-4': {\n          contents: '-',\n          nodeDeps: [],\n          nodeToNodeSubscriptions: ['gameEndSelector'],\n          type: 'RecoilState',\n        },\n        'square-5': {\n          contents: '-',\n          nodeDeps: [],\n          nodeToNodeSubscriptions: ['gameEndSelector'],\n          type: 'RecoilState',\n        },\n        'square-6': {\n          contents: '-',\n          nodeDeps: [],\n          nodeToNodeSubscriptions: ['gameEndSelector'],\n          type: 'RecoilState',\n        },\n        'square-7': {\n          contents: '-',\n          nodeDeps: [],\n          nodeToNodeSubscriptions: ['gameEndSelector'],\n          type: 'RecoilState',\n        },\n        'square-8': {\n          contents: '-',\n          nodeDeps: [],\n          nodeToNodeSubscriptions: ['gameEndSelector'],\n          type: 'RecoilState',\n        },\n      },\n      indexDiff: 0,\n    },\n    {\n      componentAtomTree: {\n        name: 'HR',\n        tag: 3,\n        children: [\n          {\n            name: 'RecoilRoot',\n            tag: 0,\n            children: [\n              {\n                name: 'react.provider',\n                tag: 10,\n                children: [\n                  {\n                    name: 'react.provider',\n                    tag: 10,\n                    children: [\n                      {\n                        name: 'Batcher',\n                        tag: 0,\n                        children: [],\n                        recoilNodes: [],\n                        actualDuration: 0.004999999873689376,\n                        treeBaseDuration: 0.03500000002532033,\n                        wasSuspended: false,\n                      },\n                      {\n                        name: 'Fragment',\n                        tag: 7,\n                        children: [\n                          {\n                            name: 'RecoilizeDebugger',\n                            tag: 0,\n                            children: [],\n                            recoilNodes: [],\n                            actualDuration: 0.5049999999755528,\n                            treeBaseDuration: 0.5049999999755528,\n                            wasSuspended: false,\n                          },\n                          {\n                            name: 'PlaygroundRender',\n                            tag: 0,\n                            children: [\n                              {\n                                name: 'App',\n                                tag: 1,\n                                children: [\n                                  {\n                                    name: 'div',\n                                    tag: 5,\n                                    children: [\n                                      {\n                                        name: 'h1',\n                                        tag: 5,\n                                        children: [],\n                                        recoilNodes: [],\n                                        actualDuration: 0.05999999939376721,\n                                        treeBaseDuration: 0,\n                                        wasSuspended: false,\n                                      },\n                                      {\n                                        name: 'Board',\n                                        tag: 0,\n                                        children: [\n                                          {\n                                            name: 'div',\n                                            tag: 5,\n                                            children: [\n                                              {\n                                                name: 'Fragment',\n                                                tag: 7,\n                                                children: [\n                                                  {\n                                                    name: 'Row',\n                                                    tag: 0,\n                                                    children: [\n                                                      {\n                                                        name: 'div',\n                                                        tag: 5,\n                                                        children: [\n                                                          {\n                                                            name: 'Box',\n                                                            tag: 0,\n                                                            children: [\n                                                              {\n                                                                name: 'div',\n                                                                tag: 5,\n                                                                children: [],\n                                                                recoilNodes: [],\n                                                                actualDuration: 0.06000000030326191,\n                                                                treeBaseDuration: 0.004999999873689376,\n                                                                wasSuspended: false,\n                                                              },\n                                                            ],\n                                                            recoilNodes: [\n                                                              'square-0',\n                                                              'currentPlayerState',\n                                                              'gameEndSelector',\n                                                            ],\n                                                            actualDuration: 0.17500000012660166,\n                                                            treeBaseDuration: 0.11999999969702912,\n                                                            wasSuspended: false,\n                                                          },\n                                                          {\n                                                            name: 'Box',\n                                                            tag: 0,\n                                                            children: [\n                                                              {\n                                                                name: 'div',\n                                                                tag: 5,\n                                                                children: [],\n                                                                recoilNodes: [],\n                                                                actualDuration: 0.02499999936844688,\n                                                                treeBaseDuration: 0,\n                                                                wasSuspended: false,\n                                                              },\n                                                            ],\n                                                            recoilNodes: [\n                                                              'square-1',\n                                                              'currentPlayerState',\n                                                              'gameEndSelector',\n                                                            ],\n                                                            actualDuration: 0.07999999888852471,\n                                                            treeBaseDuration: 0.054999999520077836,\n                                                            wasSuspended: false,\n                                                          },\n                                                          {\n                                                            name: 'Box',\n                                                            tag: 0,\n                                                            children: [\n                                                              {\n                                                                name: 'div',\n                                                                tag: 5,\n                                                                children: [],\n                                                                recoilNodes: [],\n                                                                actualDuration: 0.02500000027794158,\n                                                                treeBaseDuration: 0.005000000783184078,\n                                                                wasSuspended: false,\n                                                              },\n                                                            ],\n                                                            recoilNodes: [\n                                                              'square-2',\n                                                              'currentPlayerState',\n                                                              'gameEndSelector',\n                                                            ],\n                                                            actualDuration: 0.06500000017695129,\n                                                            treeBaseDuration: 0.045000000682193786,\n                                                            wasSuspended: false,\n                                                          },\n                                                        ],\n                                                        recoilNodes: [],\n                                                        actualDuration: 0.3499999993437086,\n                                                        treeBaseDuration: 0.2299999996466795,\n                                                        wasSuspended: false,\n                                                      },\n                                                    ],\n                                                    recoilNodes: [],\n                                                    actualDuration: 0.40499999977328116,\n                                                    treeBaseDuration: 0.28500000007625204,\n                                                    wasSuspended: false,\n                                                  },\n                                                  {\n                                                    name: 'Row',\n                                                    tag: 0,\n                                                    children: [\n                                                      {\n                                                        name: 'div',\n                                                        tag: 5,\n                                                        children: [\n                                                          {\n                                                            name: 'Box',\n                                                            tag: 0,\n                                                            children: [\n                                                              {\n                                                                name: 'div',\n                                                                tag: 5,\n                                                                children: [],\n                                                                recoilNodes: [],\n                                                                actualDuration: 0.014999999621068127,\n                                                                treeBaseDuration: 0.004999999873689376,\n                                                                wasSuspended: false,\n                                                              },\n                                                            ],\n                                                            recoilNodes: [\n                                                              'square-3',\n                                                              'currentPlayerState',\n                                                              'gameEndSelector',\n                                                            ],\n                                                            actualDuration: 0.05999999939376721,\n                                                            treeBaseDuration: 0.04999999964638846,\n                                                            wasSuspended: false,\n                                                          },\n                                                          {\n                                                            name: 'Box',\n                                                            tag: 0,\n                                                            children: [\n                                                              {\n                                                                name: 'div',\n                                                                tag: 5,\n                                                                children: [],\n                                                                recoilNodes: [],\n                                                                actualDuration: 0.020000000404252205,\n                                                                treeBaseDuration: 0.005000000783184078,\n                                                                wasSuspended: false,\n                                                              },\n                                                            ],\n                                                            recoilNodes: [\n                                                              'square-4',\n                                                              'currentPlayerState',\n                                                              'gameEndSelector',\n                                                            ],\n                                                            actualDuration: 0.06000000030326191,\n                                                            treeBaseDuration: 0.045000000682193786,\n                                                            wasSuspended: false,\n                                                          },\n                                                          {\n                                                            name: 'Box',\n                                                            tag: 0,\n                                                            children: [\n                                                              {\n                                                                name: 'div',\n                                                                tag: 5,\n                                                                children: [],\n                                                                recoilNodes: [],\n                                                                actualDuration: 0.020000000404252205,\n                                                                treeBaseDuration: 0.004999999873689376,\n                                                                wasSuspended: false,\n                                                              },\n                                                            ],\n                                                            recoilNodes: [\n                                                              'square-5',\n                                                              'currentPlayerState',\n                                                              'gameEndSelector',\n                                                            ],\n                                                            actualDuration: 0.05500000042957254,\n                                                            treeBaseDuration: 0.03999999989900971,\n                                                            wasSuspended: false,\n                                                          },\n                                                        ],\n                                                        recoilNodes: [],\n                                                        actualDuration: 0.19500000053085387,\n                                                        treeBaseDuration: 0.1450000008844654,\n                                                        wasSuspended: false,\n                                                      },\n                                                    ],\n                                                    recoilNodes: [],\n                                                    actualDuration: 0.210000000151922,\n                                                    treeBaseDuration: 0.16000000050553354,\n                                                    wasSuspended: false,\n                                                  },\n                                                  {\n                                                    name: 'Row',\n                                                    tag: 0,\n                                                    children: [\n                                                      {\n                                                        name: 'div',\n                                                        tag: 5,\n                                                        children: [\n                                                          {\n                                                            name: 'Box',\n                                                            tag: 0,\n                                                            children: [\n                                                              {\n                                                                name: 'div',\n                                                                tag: 5,\n                                                                children: [],\n                                                                recoilNodes: [],\n                                                                actualDuration: 0.020000000404252205,\n                                                                treeBaseDuration: 0.005000000783184078,\n                                                                wasSuspended: false,\n                                                              },\n                                                            ],\n                                                            recoilNodes: [\n                                                              'square-6',\n                                                              'currentPlayerState',\n                                                              'gameEndSelector',\n                                                            ],\n                                                            actualDuration: 0.054999999520077836,\n                                                            treeBaseDuration: 0.03999999989900971,\n                                                            wasSuspended: false,\n                                                          },\n                                                          {\n                                                            name: 'Box',\n                                                            tag: 0,\n                                                            children: [\n                                                              {\n                                                                name: 'div',\n                                                                tag: 5,\n                                                                children: [],\n                                                                recoilNodes: [],\n                                                                actualDuration: 0.01500000053056283,\n                                                                treeBaseDuration: 0.004999999873689376,\n                                                                wasSuspended: false,\n                                                              },\n                                                            ],\n                                                            recoilNodes: [\n                                                              'square-7',\n                                                              'currentPlayerState',\n                                                              'gameEndSelector',\n                                                            ],\n                                                            actualDuration: 0.05500000042957254,\n                                                            treeBaseDuration: 0.044999999772699084,\n                                                            wasSuspended: false,\n                                                          },\n                                                          {\n                                                            name: 'Box',\n                                                            tag: 0,\n                                                            children: [\n                                                              {\n                                                                name: 'div',\n                                                                tag: 5,\n                                                                children: [],\n                                                                recoilNodes: [],\n                                                                actualDuration: 0.009999999747378752,\n                                                                treeBaseDuration: 0,\n                                                                wasSuspended: false,\n                                                              },\n                                                            ],\n                                                            recoilNodes: [\n                                                              'square-8',\n                                                              'currentPlayerState',\n                                                              'gameEndSelector',\n                                                            ],\n                                                            actualDuration: 0.03999999898951501,\n                                                            treeBaseDuration: 0.02499999936844688,\n                                                            wasSuspended: false,\n                                                          },\n                                                        ],\n                                                        recoilNodes: [],\n                                                        actualDuration: 0.15999999959603883,\n                                                        treeBaseDuration: 0.11499999982333975,\n                                                        wasSuspended: false,\n                                                      },\n                                                    ],\n                                                    recoilNodes: [],\n                                                    actualDuration: 0.17499999921710696,\n                                                    treeBaseDuration: 0.12999999944440788,\n                                                    wasSuspended: false,\n                                                  },\n                                                ],\n                                                recoilNodes: [],\n                                                actualDuration: 0.7999999988896889,\n                                                treeBaseDuration: 0.5849999997735722,\n                                                wasSuspended: false,\n                                              },\n                                              {\n                                                name: 'h2',\n                                                tag: 5,\n                                                children: [],\n                                                recoilNodes: [],\n                                                actualDuration: 0.01500000053056283,\n                                                treeBaseDuration: 0.005000000783184078,\n                                                wasSuspended: false,\n                                              },\n                                              {\n                                                name: 'button',\n                                                tag: 5,\n                                                children: [],\n                                                recoilNodes: [],\n                                                actualDuration: 0.03999999989900971,\n                                                treeBaseDuration: 0.004999999873689376,\n                                                wasSuspended: false,\n                                              },\n                                            ],\n                                            recoilNodes: [],\n                                            actualDuration: 0.879999999597203,\n                                            treeBaseDuration: 0.6050000010873191,\n                                            wasSuspended: false,\n                                          },\n                                        ],\n                                        recoilNodes: ['gameEndSelector'],\n                                        actualDuration: 1.059999999597494,\n                                        treeBaseDuration: 0.7850000010876101,\n                                        wasSuspended: false,\n                                      },\n                                    ],\n                                    recoilNodes: [],\n                                    actualDuration: 1.1449999992692028,\n                                    treeBaseDuration: 0.8000000007086783,\n                                    wasSuspended: false,\n                                  },\n                                ],\n                                recoilNodes: [],\n                                actualDuration: 1.2199999991935329,\n                                treeBaseDuration: 0.8750000006330083,\n                                wasSuspended: false,\n                              },\n                            ],\n                            recoilNodes: ['playStart'],\n                            actualDuration: 1.294999999117863,\n                            treeBaseDuration: 0.9500000005573384,\n                            wasSuspended: false,\n                          },\n                        ],\n                        recoilNodes: [],\n                        actualDuration: 1.7999999990934157,\n                        treeBaseDuration: 1.4750000000276486,\n                        wasSuspended: false,\n                      },\n                    ],\n                    recoilNodes: [],\n                    actualDuration: 1.804999998967105,\n                    treeBaseDuration: 1.5499999999519787,\n                    wasSuspended: false,\n                  },\n                ],\n                recoilNodes: [],\n                actualDuration: 1.804999998967105,\n                treeBaseDuration: 1.5849999990678043,\n                wasSuspended: false,\n              },\n            ],\n            recoilNodes: [],\n            actualDuration: 1.804999998967105,\n            treeBaseDuration: 1.7199999992953963,\n            wasSuspended: false,\n          },\n        ],\n        recoilNodes: [],\n        actualDuration: 1.804999998967105,\n        treeBaseDuration: 1.8249999993713573,\n        wasSuspended: false,\n      },\n      filteredSnapshot: {\n        playStart: {\n          type: 'RecoilState',\n          contents: true,\n          nodeDeps: [],\n          nodeToNodeSubscriptions: [],\n        },\n        'square-0': {\n          type: 'RecoilState',\n          contents: '-',\n          nodeDeps: [],\n          nodeToNodeSubscriptions: ['gameEndSelector'],\n        },\n        'square-1': {\n          type: 'RecoilState',\n          contents: '-',\n          nodeDeps: [],\n          nodeToNodeSubscriptions: ['gameEndSelector'],\n        },\n        'square-2': {\n          type: 'RecoilState',\n          contents: '-',\n          nodeDeps: [],\n          nodeToNodeSubscriptions: ['gameEndSelector'],\n        },\n        'square-3': {\n          type: 'RecoilState',\n          contents: '-',\n          nodeDeps: [],\n          nodeToNodeSubscriptions: ['gameEndSelector'],\n        },\n        'square-4': {\n          type: 'RecoilState',\n          contents: '-',\n          nodeDeps: [],\n          nodeToNodeSubscriptions: ['gameEndSelector'],\n        },\n        'square-5': {\n          type: 'RecoilState',\n          contents: '-',\n          nodeDeps: [],\n          nodeToNodeSubscriptions: ['gameEndSelector'],\n        },\n        'square-6': {\n          type: 'RecoilState',\n          contents: '-',\n          nodeDeps: [],\n          nodeToNodeSubscriptions: ['gameEndSelector'],\n        },\n        'square-7': {\n          type: 'RecoilState',\n          contents: '-',\n          nodeDeps: [],\n          nodeToNodeSubscriptions: ['gameEndSelector'],\n        },\n        'square-8': {\n          type: 'RecoilState',\n          contents: '-',\n          nodeDeps: [],\n          nodeToNodeSubscriptions: ['gameEndSelector'],\n        },\n        currentPlayerState: {\n          type: 'RecoilState',\n          contents: 'X',\n          nodeDeps: [],\n          nodeToNodeSubscriptions: ['gameEndSelector'],\n        },\n        gameEndSelector: {\n          type: 'RecoilValueReadOnly',\n          contents: false,\n          nodeDeps: [\n            'square-0',\n            'square-1',\n            'square-2',\n            'square-3',\n            'square-4',\n            'square-5',\n            'square-6',\n            'square-7',\n            'square-8',\n            'currentPlayerState',\n          ],\n          nodeToNodeSubscriptions: [],\n        },\n      },\n      indexDiff: 0,\n    },\n  ],\n  renderIndex: 2,\n  cleanComponentAtomTree: {\n    children: [\n      {\n        name: 'Board',\n        tag: 0,\n        children: [\n          {\n            name: 'Row',\n            tag: 0,\n            children: [\n              {\n                name: 'Box',\n                tag: 0,\n                children: [],\n                recoilNodes: [\n                  'square-0',\n                  'currentPlayerState',\n                  'gameEndSelector',\n                ],\n                actualDuration: 0.17500000012660166,\n                treeBaseDuration: 0.11999999969702912,\n                wasSuspended: false,\n              },\n              {\n                name: 'Box',\n                tag: 0,\n                children: [],\n                recoilNodes: [\n                  'square-1',\n                  'currentPlayerState',\n                  'gameEndSelector',\n                ],\n                actualDuration: 0.07999999888852471,\n                treeBaseDuration: 0.054999999520077836,\n                wasSuspended: false,\n              },\n              {\n                name: 'Box',\n                tag: 0,\n                children: [],\n                recoilNodes: [\n                  'square-2',\n                  'currentPlayerState',\n                  'gameEndSelector',\n                ],\n                actualDuration: 0.06500000017695129,\n                treeBaseDuration: 0.045000000682193786,\n                wasSuspended: false,\n              },\n            ],\n            recoilNodes: [],\n            actualDuration: 0.40499999977328116,\n            treeBaseDuration: 0.28500000007625204,\n            wasSuspended: false,\n          },\n          {\n            name: 'Row',\n            tag: 0,\n            children: [\n              {\n                name: 'Box',\n                tag: 0,\n                children: [],\n                recoilNodes: [\n                  'square-3',\n                  'currentPlayerState',\n                  'gameEndSelector',\n                ],\n                actualDuration: 0.05999999939376721,\n                treeBaseDuration: 0.04999999964638846,\n                wasSuspended: false,\n              },\n              {\n                name: 'Box',\n                tag: 0,\n                children: [],\n                recoilNodes: [\n                  'square-4',\n                  'currentPlayerState',\n                  'gameEndSelector',\n                ],\n                actualDuration: 0.06000000030326191,\n                treeBaseDuration: 0.045000000682193786,\n                wasSuspended: false,\n              },\n              {\n                name: 'Box',\n                tag: 0,\n                children: [],\n                recoilNodes: [\n                  'square-5',\n                  'currentPlayerState',\n                  'gameEndSelector',\n                ],\n                actualDuration: 0.05500000042957254,\n                treeBaseDuration: 0.03999999989900971,\n                wasSuspended: false,\n              },\n            ],\n            recoilNodes: [],\n            actualDuration: 0.210000000151922,\n            treeBaseDuration: 0.16000000050553354,\n            wasSuspended: false,\n          },\n          {\n            name: 'Row',\n            tag: 0,\n            children: [\n              {\n                name: 'Box',\n                tag: 0,\n                children: [],\n                recoilNodes: [\n                  'square-6',\n                  'currentPlayerState',\n                  'gameEndSelector',\n                ],\n                actualDuration: 0.054999999520077836,\n                treeBaseDuration: 0.03999999989900971,\n                wasSuspended: false,\n              },\n              {\n                name: 'Box',\n                tag: 0,\n                children: [],\n                recoilNodes: [\n                  'square-7',\n                  'currentPlayerState',\n                  'gameEndSelector',\n                ],\n                actualDuration: 0.05500000042957254,\n                treeBaseDuration: 0.044999999772699084,\n                wasSuspended: false,\n              },\n              {\n                name: 'Box',\n                tag: 0,\n                children: [],\n                recoilNodes: [\n                  'square-8',\n                  'currentPlayerState',\n                  'gameEndSelector',\n                ],\n                actualDuration: 0.03999999898951501,\n                treeBaseDuration: 0.02499999936844688,\n                wasSuspended: false,\n              },\n            ],\n            recoilNodes: [],\n            actualDuration: 0.17499999921710696,\n            treeBaseDuration: 0.12999999944440788,\n            wasSuspended: false,\n          },\n        ],\n        recoilNodes: ['gameEndSelector'],\n        actualDuration: 1.059999999597494,\n        treeBaseDuration: 0.7850000010876101,\n        wasSuspended: false,\n      },\n    ],\n    name: 'PlaygroundRender',\n    recoilNodes: ['playStart'],\n    tag: 0,\n    actualDuration: 1.804999998967105,\n  },\n};\n"
  },
  {
    "path": "package/.npmignore",
    "content": "__tests__\n./*.ts\nformatFiberNodes.ts\nindex.ts\nnode_modules/"
  },
  {
    "path": "package/README.md",
    "content": "<meta name='keywords' content='Recoil, Recoil.js, Recoil Dev Tool, Recoilize, Chrome Dev Tool, Recoil Chrome'>\n\n<h1>Debugger for Recoil Applications</h1>\n\n# [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/oslabs-beta/Recoilize/blob/staging/LICENSE) [![npm version](https://img.shields.io/npm/v/recoilize)](https://www.npmjs.com/package/recoilize) ![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)\n\n# [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/oslabs-beta/Recoilize/blob/staging/LICENSE) [![npm version](https://img.shields.io/npm/v/recoilize)](https://www.npmjs.com/package/recoilize) ![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)\n\n[Korean README 한국어](README_KO.md)\n\n<h1> About</h1>\n<p>\nRecoilize is a Chrome Dev Tool meant for debugging applications built with the experimental Recoil.js state management library.\n\nThe tool records Recoil state and allows users to easily debug their applications with features such as time travel to previous states, visualization of the component graph and display of the atom selector network.\n\n</p>\n\n<p>\nDownload Recoilize from the <a href='https://chrome.google.com/webstore/detail/recoilize/jhfmmdhbinleghabnblahfjfalfgidik'>Chrome Store</a>\n</p>\n\n<p>Visit the Recoilize <a href='https://www.recoilize.io/'>landing page</a> to demo</p>\n\n<h2>\n** STILL IN BETA **\n</h2>\n\n<p>Please note that Recoilize is in BETA. We will continue to make improvements and implement fixes but if you find any issues, please dont hesitate to report them in the issues tab or submit a PR and we'll happily take a look.</p>\n\n<h1>\nInstallation\n</h1>\n\n#### Install Recoilize Module\n\n```js\nnpm install recoilize\n```\n\n### ** IMPORTANT **\n\n#### Import RecoilizeDebugger from the Recoilize module\n\n```js\nimport RecoilizeDebugger from 'recoilize';\n```\n\n#### Integrate RecoilizeDebugger as a React component within the recoil root:\n\n```js\nimport RecoilizeDebugger from 'recoilize';\nimport RecoilRoot from 'recoil';\n\nReactDOM.render(\n  <RecoilRoot>\n    <RecoilizeDebugger />\n    <App />\n  </RecoilRoot>,\n  document.getElementById('root'),\n);\n```\n\n#### Please note, Recoilize assumes that the HTML element used to inject your React application has an ID of 'root'. If it does not the HTML element must be passed in as an attribute called 'root' to the RecoilizeDebugger component\n\n#### Example:\n\n```js\nimport RecoilizeDebugger from 'recoilize';\nimport RecoilRoot from 'recoil';\n\n//If your app injects on an element with ID of 'app'\nconst app = document.getElementById('app');\n\nReactDOM.render(\n  <RecoilRoot>\n    <RecoilizeDebugger root={app} />\n    <App />\n  </RecoilRoot>,\n  app,\n);\n```\n\n### In order to integrate Next.js applications with RecoilizeDebugger, follow the example below. \n\n```js\n//If your application uses Next.js modify the _app.js as follows\nimport dynamic from 'next/dynamic';\nimport { useEffect, useState } from 'react';\nimport { RecoilRoot } from 'recoil';\n\nfunction MyApp({ Component, pageProps }) {\n\n  const [root, setRoot] = useState(null)\n  const RecoilizeDebugger = dynamic(\n\t() => {\n\t  return import('recoilize');\n\t},\n\t{ ssr: false}\n  );\n\n  useEffect(() => {\n\n    if (typeof window.document !== 'undefined') {\n      setRoot(document.getElementById('__next'));\n    }\n  }, [root]);\n \n  return (\n    <>\n    <RecoilRoot>\n      <RecoilizeDebugger root = {root}/>\n      <Component {...pageProps} />\n    </RecoilRoot>\n    </>\n  );\n}\n\n\nexport default MyApp;\n\n```\n\n#### Open your application on the Chrome Browser and start debugging with Recoilize!\n\n##### (Only supported with React applications using Recoil as state management)\n\n<h1>New Features for Version 2.0.1</h1>\n\n<h3>Support for Recoil 0.1.3</h3>\n<p>Recoilize now supports the most recent update to the Recoil library.</p>\n\n<h3>Ease of Use</h3>\n<p>Recoilize nolonger requires atoms and selectors or the root HTML element to be passed into the RecoilizeDebugger React component. Simply import RecoilizeDubugger and integrate it within your app's RecoilRoot component.</p>\n\n<h3>Support for Concurrent Mode</h3>\n<p>Additonal functionality has been added for apps that utilize React's Suspense component. If a Suspense component was used to suspend component renderings those components will display with a red border in the component graph. This indicates that a component was suspended during the render of the selected snapshot.</p>\n\n<h3>Performance Metrics</h3>\n<p>A new tab, 'Metrics', has been incorperated into the dev tool. In this tab the user will find two graphs which display component render times.\n\nThe flame graph displays the time a component took to render itself, and all of its child components. The bar graph displays the individual render times of each component.<p>\n\n<h1>Features</h1>\n<h3>Time Travel</h3>\n<p>As one of the key features of Recoilize, the tool enables users to jump to any previous snapshots. Pressing the jump button next to each of the snapshots will change the DOM by setting the state to that snapshot.<p>\n\n<h3>Visualizations</h3>\n<p>Users are able to view visualizations for their application's state by clicking individual snapshots. Recoilize provides component trees and graphs, as well as the state trees in JSON format.<p>\n\n<h3>Throttle</h3>\n<p>In the settings tab, users are able to set throttle (in milliseconds) for large scale applications or any applications that changes state rapidly. The default is set at 70ms.<p>\n\n<h3>State Persistence</h3>\n<p>Recoilize allows the users to persist their application's state through a refresh or reload. At this time, the user is able to view the previous states in the dev tool, but cannot time travel to the states before refresh.</p>\n\n<h3>Additional Features</h3>\n<ul><li>component graph hover to view atoms and selectors</li></ul>\n<ul><li>legend to see relationship between component graph and state</li></ul>\n<ul><li>Toggle to view raw component graph</li></ul>\n<ul><li>filter atom/selector network relationship</li></ul>\n<ul><li>filter snapshots by atom/selector keys</li></ul>\n\n<h2> We will continue updating Recoilize alongside Recoil's updates!</h2>\n\n<h1>\n Contributors\n</h1>\n\n<h4>Bren Yamaguchi <a href='https://github.com/brenyama' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/brenyamaguchi/' target=\"_blank\">@linkedin</a></h4>\n\n<h4>Saejin Kang <a  href='https://github.com/skang1004' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/saejinkang1004/' target=\"_blank\">@linkedin</a></h4>\n\n<h4>Jonathan Escamila <a  href='https://github.com/jonescamilla' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/jon-escamilla/' target=\"_blank\">@linkedin</a> </h4>\n\n<h4>Sean Smith <a  href='https://github.com/SmithSean17' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/sean-smith17/' target=\"_blank\">@linkedin</a> </h4>\n\n<h4>Justin Choo <a href='https://github.com/justinchoo93' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/justinchoo93/' target=\"_blank\">@linkedin</a></h4>\n\n<h4>Anthony Lin <a  href='https://github.com/anthonylin198' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/anthony-lin/' target=\"_blank\">@linkedin</a></h4>\n\n<h4>Spenser Schwartz <a  href='https://github.com/spenserschwartz' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/spenser-schwartz/' target=\"_blank\">@linkedin</a> </h4>\n\n<h4>Steven Nguyen <a  href='https://github.com/Steven-Nguyen-T' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/steven-nguyen-t/' target=\"_blank\">@linkedin</a> </h4>\n\n<h4>Henry Taing <a  href='https://github.com/henrytaing' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/henrytaing/' target=\"_blank\">@linkedin</a> </h4>\n\n<h4>Seungho Baek <a  href='https://github.com/hobaek' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/s2unghobaek/' target=\"_blank\">@linkedin</a> </h4>\n\n<h4>Aaron Yang <a  href='https://github.com/aaronyang24' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/aaronyang24/' target=\"_blank\">@linkedin</a> </h4>\n\n<h4>Jesus Vargas <a  href='https://github.com/jmodestov' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/jesus-modesto-vargas/' target=\"_blank\">@linkedin</a> </h4>\n\n<h4>Davide Molino <a  href='https://github.com/davidemmolino' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/davide-molino/' target=\"_blank\">@linkedin</a> </h4>\n\n<h4>Taven Shumaker <a  href='https://github.com/TavenShumaker' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/Taven-Shumaker/' target=\"_blank\">@linkedin</a> </h4>\n\n<h4>Janis Hernandez <a  href='https://github.com/Janis-H' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/janis-h/' target=\"_blank\">@linkedin</a> </h4>\n\n<h4>Jaime Baik <a  href='https://github.com/jaimebaik' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/jaime-baik/' target=\"_blank\">@linkedin</a> </h4>\n\n<h4>Anthony Magallanes <a  href='https://github.com/amagalla' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/anthony-magallanes/' target=\"_blank\">@linkedin</a> </h4>\n\n<h4>Edward Shei <a  href='https://github.com/calibeach' target=\"_blank\">@github </a><a  href='https://www.linkedin.com/in/edwardshei/' target=\"_blank\">@linkedin</a> </h4>\n\n<h4>Nathan Bargers <a  href='https://github.com/nbargers' target=“_blank”>@github </a><a  href='https://www.linkedin.com/in/nathan-bargers/' target=“_blank”>@linkedin</a> </h4>\n\n<h4>Scott Campbell <a  href='https://github.com/thisisscottcampbell' target=“_blank”>@github </a><a  href='https://www.linkedin.com/in/thisisscottcampbell/' target=“_blank”>@linkedin</a> </h4>\n\n<h4>Steve Hong <a  href='https://github.com/stevehong423' target=“_blank”>@github </a><a  href='https://www.linkedin.com/in/stevehongpa/' target=“_blank”>@linkedin</a> </h4>\n\n<h4>Jason Lee <a  href='https://github.com/j4s0n1020' target=“_blank”>@github </a><a  href='https://www.linkedin.com/in/jasonjml/' target=“_blank”>@linkedin</a> </h4>\n\n<h4>Razana Nisathar <a  href='https://github.com/razananisathar' target=“_blank”>@github </a><a  href='http://www.linkedin.com/in/razananisathar' target=“_blank”>@linkedin</a> </h4>"
  },
  {
    "path": "package/formatFiberNodes.js",
    "content": "// node parameter should be root of the fiber node tree, can be grapped with startNode from below\n// const startNode = document.getElementById('root')._reactRootContainer._internalRoot.current;\n\nconst formatFiberNodes = node => {\n  const formattedNode = {\n    // this function grabs a 'name' based on the tag of the node\n    name: assignName(node),\n    tag: node.tag,\n    children: [],\n    recoilNodes: createAtomsSelectorArray(node),\n    actualDuration: node.actualDuration,\n    treeBaseDuration: node.treeBaseDuration,\n    wasSuspended: node.return && node.return.tag === 13 ? true : false,\n  };\n\n  // loop through and recursively call all nodes to format their 'sibling' and 'child' properties to our desired tree shape\n  let currentNode = node.child;\n  while (currentNode) {\n    formattedNode.children.push(formatFiberNodes(currentNode));\n    currentNode = currentNode.sibling;\n  }\n\n  return formattedNode;\n};\n\nconst createAtomsSelectorArray = node => {\n  // initialize empty array for all atoms and selectors.  Elements will be all atom and selector names, as strings\n  const recoilNodes = [];\n\n  //start the pointer at node.memoizedState. All nodes should have this key.\n  let currentNode = node.memoizedState;\n\n  // Traverse through the memoizedStates and look for the deps key which holds selectors or state.\n  while (currentNode) {\n    // if the memoizedState has a deps key, and that deps key is an array\n    // then the first value of that array will be an atom or selector\n    if (\n      typeof(currentNode) === 'object' &&\n      currentNode.hasOwnProperty('memoizedState') &&\n      typeof currentNode.memoizedState === 'object' &&\n      currentNode.memoizedState !== null &&\n      !Array.isArray(currentNode.memoizedState) &&\n      currentNode.memoizedState.hasOwnProperty('deps')\n    ) {\n      if (\n        Array.isArray(currentNode.memoizedState.deps) &&\n        typeof currentNode.memoizedState.deps[0] === 'object' &&\n        currentNode.memoizedState.deps[0] !== null\n      ) {\n        // if recoilNodes (arr) includes the current atom or selector\n        if (!recoilNodes.includes(currentNode.memoizedState.deps[0].key)) {\n          // otherwise push atom/selector to recoilNodes\n          recoilNodes.push(currentNode.memoizedState.deps[0].key);\n        }\n      }\n    }\n    // move onto next node\n    currentNode = currentNode.next;\n  }\n  // return atom and selectors array\n  return recoilNodes;\n};\n\n// keep an eye on this section as we test bigger and bigger applications SEAN\nconst assignName = node => {\n  // Returns symbol key if $$typeof is defined. Some components, such as context providers, will have this value.\n  if (node.type && node.type.$$typeof) return Symbol.keyFor(node.type.$$typeof);\n  // Return suspense if tag is equal to 13, which is associated with Suspense components.\n  if (node.tag === 13) return 'Suspense';\n  // Find name of a class component\n  if (node.type && node.type.name) return node.type.name;\n  // Tag 5 === HostComponent\n  if (node.tag === 5) return `${node.type}`;\n  // Tag 3 === HostRoot\n  if (node.tag === 3) return 'HR';\n  // Tag 6 === HostText\n  if (node.tag === 6) return node.memoizedProps;\n  // Tag 7 === Fragment\n  if (node.tag === 7) return 'Fragment';\n};\n\nmodule.exports = { formatFiberNodes };\n\n// if testing this function on the browser, use line below to log the formatted tree in the console\n//let formattedFiberNodes = formatFiberNodes(document.getElementById('root')._reactRootContainer._internalRoot.current)\n"
  },
  {
    "path": "package/formatFiberNodes.ts",
    "content": "// node parameter should be root of the fiber node tree, can be grapped with startNode from below\n// const startNode = document.getElementById('root')._reactRootContainer._internalRoot.current;\n\ntype node = {\n  tag: number;\n  key: any;\n  elementType: string;\n  child: any;\n  sibling: any;\n  actualDuration: number;\n  treeBaseDuration: number;\n  return: any;\n};\n\ntype formattedNode = {\n  name: string;\n  tag: number;\n  children: any[];\n  recoilNodes: any[];\n  // adding in render time for fiber node\n  actualDuration: number;\n  treeBaseDuration: number;\n  wasSuspended: boolean;\n};\n\nconst formatFiberNodes = (node: node) => {\n  const formattedNode: formattedNode = {\n    actualDuration: node.actualDuration,\n    treeBaseDuration: node.treeBaseDuration,\n    // this function grabs a 'name' based on the tag of the node\n    name: assignName(node),\n    tag: node.tag,\n    children: [],\n    recoilNodes: createAtomsSelectorArray(node),\n    wasSuspended: node.return && node.return.tag === 13 ? true : false,\n  };\n\n  // loop through and recursively call all nodes to format their 'sibling' and 'child' properties to our desired tree shape\n  let currentNode = node.child;\n  while (currentNode) {\n    formattedNode.children.push(formatFiberNodes(currentNode));\n    currentNode = currentNode.sibling;\n  }\n\n  return formattedNode;\n};\n\nconst createAtomsSelectorArray = (node: any) => {\n  // initialize empty array for all atoms and selectors.  Elements will be all atom and selector names, as strings\n  const recoilNodes = [];\n\n  //start the pointer at node.memoizedState. All nodes should have this key.\n  let currentNode = node.memoizedState;\n\n  // Traverse through the memoizedStates and look for the deps key which holds selectors or state.\n\n  while (currentNode) {\n    // if the memoizedState has a deps key, and that deps key is an array of length 2 then the first value of that array will be an atom or selector\n    if (\n      currentNode.deps &&\n      Array.isArray(currentNode.deps) &&\n      currentNode.deps.length === 2\n    ) {\n      // if the atom/selector already exist in the recoilNodes array then break from this while loop. At this point you are traversing through previous atom/selector deps.\n      if (recoilNodes.includes(currentNode.deps[0].key)) break;\n      recoilNodes.push(currentNode.deps[0].key);\n\n      // if an atom/selector was successfully pushed into the recoilNodes array then the pointer should now point to the next key, which will have its own deps key if there is another atom/selector\n      currentNode = currentNode.next;\n    } else {\n      // This is the case where there is no atom/selector in the memoizedState. Look into the memoized state of the next key. If that doesn't exist then break from the while loop because there are no atoms/selectors at this point.\n      if (!currentNode.next) break;\n      if (!currentNode.next.memoizedState) break;\n      currentNode = currentNode.next.memoizedState;\n    }\n  }\n  return recoilNodes;\n};\n\n// keep an eye on this section as we test bigger and bigger applications\nconst assignName = (node: any) => {\n  // Returns symbol key if $$typeof is defined. Some components, such as context providers, will have this value.\n  if (node.type && node.type.$$typeof) return Symbol.keyFor(node.type.$$typeof);\n  // Return suspense if tag is equal to 13, which is associated with Suspense components.\n  if (node.tag === 13) return 'Suspense';\n  // Find name of a class component\n  if (node.type && node.type.name) return node.type.name;\n  // Tag 5 === HostComponent\n  if (node.tag === 5) return `${node.type}`;\n  // Tag 3 === HostRoot\n  if (node.tag === 3) return 'HR';\n  // Tag 3 === HostText\n  if (node.tag === 6) {\n    return node.memoizedProps;\n  }\n  if (node.tag === 7) return 'Fragment';\n};\n\nexport default formatFiberNodes;\n\n// if testing this function on the browser, use line below to log the formatted tree in the console\n//let formattedFiberNodes = formatFiberNodes(document.getElementById('root')._reactRootContainer._internalRoot.current)\n"
  },
  {
    "path": "package/index.js",
    "content": "import React, {useState, useEffect} from 'react';\nimport {\n  useRecoilTransactionObserver_UNSTABLE,\n  useRecoilSnapshot,\n  useGotoRecoilSnapshot,\n  useRecoilState,\n  useGetRecoilValueInfo_UNSTABLE\n} from 'recoil';\nimport {formatFiberNodes} from './formatFiberNodes';\n\n// grabs isPersistedState from sessionStorage\nlet isPersistedState = sessionStorage.getItem('isPersistedState');\n\n// isRestored state disables snapshots from being recorded\n// when we jump backwards\nlet isRestoredState = false;\n\n// set default throttle to 70, throttle timer changes with every snapshot\nlet throttleTimer = 0;\nlet throttleLimit = 70;\n\n// assign the value of selectorsObject in formatRecoilizeSelectors function\n// will contain the selectors from a user application\nlet selectorsObject;\n\nexport default function RecoilizeDebugger(props) {\n\n  // We should ask for Array of atoms and selectors.\n  // Captures all atoms that were defined to get the initial state\n\n  // Define a recoilizeRoot variable which will be assigned based on whether a root is passed in as a prop\n  let recoilizeRoot;\n\n  // Check if a root was passed to props.\n  if (props.root) {\n    const {root} = props;\n    recoilizeRoot = root;\n  } else {\n    recoilizeRoot = document.getElementById('root');\n  }\n\n  const snapshot = useRecoilSnapshot();\n\n  // getNodes_UNSTABLE will return an iterable that contains atom and selector objects.\n  const nodes = [...snapshot.getNodes_UNSTABLE()];\n  // Local state of all previous snapshots to use for time traveling when requested by dev tools.\n  const [snapshots, setSnapshots] = useState([snapshot]);\n  // const [isRestoredState, setRestoredState] = useState(false);\n  const gotoSnapshot = useGotoRecoilSnapshot();\n\n  const filteredSnapshot = {};\n\n  /*\n  A nodeDeps object is constructed using getDeps_UNSTABLE. \n  This object will then be used to construct a nodeSubscriptions object. \n  After continuous testing, getSubscriptions_UNSTABLE was deemed too unreliable. \n  */\n\n  const nodeDeps = {};\n  const nodeSubscriptions = {};\n\n  nodes.forEach(node => {\n    const getDeps = [...snapshot.getInfo_UNSTABLE(node).deps];\n    nodeDeps[node.key] = getDeps.map(dep => dep.key);\n  });\n\n  for (let key in nodeDeps) {\n    nodeDeps[key].forEach(node => {\n      if (nodeSubscriptions[node]) {\n        nodeSubscriptions[node].push(key);\n      } else {\n        nodeSubscriptions[node] = [key];\n      }\n    });\n  }\n\n  // Traverse all atoms and selector state nodes and get value\n  nodes.forEach((node, index) => {\n    const type = node.__proto__.constructor.name;\n    const contents = snapshot.getLoadable(node).contents;\n    // Construct node data structure for dev tool to consume\n    filteredSnapshot[node.key] = {\n      type,\n      contents,\n      nodeDeps: nodeDeps[node.key],\n      nodeToNodeSubscriptions: nodeSubscriptions[node.key]\n        ? nodeSubscriptions[node.key]\n        : [],\n    };\n  });\n  \n  // React lifecycle hook on re-render\n  useEffect(() => {\n    // Window listener for messages from dev tool UI & background.js\n    window.addEventListener('message', onMessageReceived);\n\n    if (!isRestoredState) {\n      const devToolData = createDevToolDataObject(filteredSnapshot);\n      // Post message to content script on every re-render of the developers application only if content script has started\n      sendWindowMessage('recordSnapshot', devToolData);\n    } else {\n      isRestoredState = false;\n    }\n\n    // Clears the window event listener.\n    return () => window.removeEventListener('message', onMessageReceived);\n  });\n\n  // Listener callback for messages sent to windowf\n  const onMessageReceived = msg => {\n    // Add other actions from dev tool here\n    switch (msg.data.action) {\n      // Checks to see if content script has started before sending initial snapshot\n      case 'contentScriptStarted':\n        if (isPersistedState === 'false' || isPersistedState === null) {\n          const initialFilteredSnapshot = formatAtomSelectorRelationship(\n            filteredSnapshot,\n          );\n          // once application renders, grab the array of atoms and array of selectors\n          const appsKnownAtomsArray = [...snapshot._store.getState().knownAtoms]\n          // console.log('Store State.getState: Atoms', appsKnownAtomsArray);\n          const appsKnownSelectorsArray = [...snapshot._store.getState().knownSelectors]\n          // console.log('Store State.getState: Selectors', appsKnownSelectorsArray);\n  \n          const atomsAndSelectorsMsg = {\n            atoms: appsKnownAtomsArray,\n            selectors: appsKnownSelectorsArray,\n            $selectors: selectorsObject         // the selectors object that contain key and set / get methods as strings\n          }\n\n          //creating a indexDiff variable\n          //only created on initial creation of devToolData\n          //determines difference in length of backend snapshots array and frontend snapshotHistoryLength to avoid off by one error\n          const indexDiff = snapshots.length - 1;\n\n          const devToolData = createDevToolDataObject(\n            initialFilteredSnapshot,\n            indexDiff,\n            atomsAndSelectorsMsg,\n          );\n          sendWindowMessage('moduleInitialized', devToolData);\n        } else {\n          setProperIndexForPersistedState();\n          sendWindowMessage('persistSnapshots', null);\n        }\n        \n        break;\n      // Listens for a request from dev tool to time travel to previous state of the app.\n      case 'snapshotTimeTravel':\n        timeTravelToSnapshot(msg);\n        break;\n      case 'persistState':\n        switchPersistMode();\n        break;\n      // Implementing the throttle change\n      case 'throttleEdit':\n        throttleLimit = parseInt(msg.data.payload.value);\n        break;\n      default:\n        break;\n    }\n  };\n\n  // assigns or switches isPersistedState in sessionStorage\n  const switchPersistMode = () => {\n    if (isPersistedState === 'false' || isPersistedState === null) {\n      // switch isPersistedState in sessionStorage to true\n      sessionStorage.setItem('isPersistedState', true);\n\n      // stores the length of current list of snapshots in sessionStorage\n      sessionStorage.setItem('persistedSnapshots', snapshots.length);\n    } else {\n      // switch isPersistedState in sessionStorage to false\n      sessionStorage.setItem('isPersistedState', false);\n    }\n  };\n\n  // function retreives length and fills snapshot array\n  const setProperIndexForPersistedState = () => {\n    const retreived = sessionStorage.getItem('persistedSnapshots');\n    const snapshotsArray = new Array(Number(retreived) + 1).fill({});\n    setSnapshots(snapshotsArray);\n  };\n\n  // Sends window an action and payload message.\n  const sendWindowMessage = (action, payload) => {\n    window.postMessage(\n      JSON.parse(JSON.stringify({\n        action,\n        payload,\n      })),\n      '*',\n    );\n  };\n\n  const createDevToolDataObject = (filteredSnapshot, diff, atomsAndSelectors) => {\n    if (diff === undefined) {\n      return {\n        filteredSnapshot: filteredSnapshot,\n        componentAtomTree: formatFiberNodes(\n          recoilizeRoot._reactRootContainer._internalRoot.current,\n        ),\n        atomsAndSelectors,\n      };\n    } else {\n      return {\n        filteredSnapshot: filteredSnapshot,\n        componentAtomTree: formatFiberNodes(\n          recoilizeRoot._reactRootContainer._internalRoot.current,\n        ),\n        indexDiff: diff,\n        atomsAndSelectors,\n      };\n    }\n  };\n\n  const formatAtomSelectorRelationship = filteredSnapshot => {\n    if (\n      window.$recoilDebugStates &&\n      Array.isArray(window.$recoilDebugStates) &&\n      window.$recoilDebugStates.length\n    ) {\n      let snapObj =\n        window.$recoilDebugStates[window.$recoilDebugStates.length - 1];\n      if (snapObj.hasOwnProperty('nodeDeps')) {\n        for (let [key, value] of snapObj.nodeDeps) {\n          filteredSnapshot[key].nodeDeps = Array.from(value);\n        }\n      }\n      if (snapObj.hasOwnProperty('nodeToNodeSubscriptions')) {\n        for (let [key, value] of snapObj.nodeToNodeSubscriptions) {\n          filteredSnapshot[key].nodeToNodeSubscriptions = Array.from(value);\n        }\n      }\n    }\n    return filteredSnapshot;\n  };\n\n  // Will add hover effect over highlighted component\n  // Takes an argument of msg.data which contains name and payload\n  const activateHover = payload => {\n    let name = payload.name;\n  };\n\n  // FOR TIME TRAVEL: time travels to a given snapshot, re renders application.\n  const timeTravelToSnapshot = async msg => {\n\n    isRestoredState = true;\n    await gotoSnapshot(snapshots[msg.data.payload.snapshotIndex]);\n  };\n\n  // FOR TIME TRAVEL: Recoil hook to fire a callback on every atom/selector change -- research Throttle\n  useRecoilTransactionObserver_UNSTABLE(({snapshot}) => {\n    const now = new Date().getTime();\n    if (now - throttleTimer < throttleLimit) {\n      isRestoredState = true;\n    } else {\n      throttleTimer = now;\n    }\n\n    if (!isRestoredState) {\n      setSnapshots([...snapshots, snapshot]);\n    }\n  });\n\n  return null;\n}\n\n// function that receives objects to be passed into selector constructor function to post a message to the window\n// cannot send an object with a property that contains a function to the window - need to stringify the set and get methods\nexport function formatRecoilizeSelectors(...selectors){\n  // create object to be sent via window message from target recoil application\n  selectorsObject = {};\n  // iterate through our array of objects\n  selectors.forEach(selector => {\n    // check if the current selector object contains a set method, if so, reassign it to a stringified version\n    if (selector.hasOwnProperty('set')){\n      selector.set = selector.set.toString();\n    }\n    // check if the current selector object contains a get method, if so, reassign it to a stringified version\n    if (selector.hasOwnProperty('get')){\n      selector.get = selector.get.toString();\n    }\n    // store the selector in the payload object - providing its property name as the 'key' property of the current selector object\n    // providing the object the property name of selector key will give easy searchability in GUI application for selector dropdown\n    selectorsObject[selector.key] = selector;\n  });\n\n}\n\n\n"
  },
  {
    "path": "package/index.ts",
    "content": "import {useState, useEffect} from 'react';\nimport {\n  useRecoilTransactionObserver_UNSTABLE,\n  useRecoilSnapshot,\n  useGotoRecoilSnapshot,\n} from 'recoil';\nimport formatFiberNodes from './formatFiberNodes';\n\n// isRestored state disables snapshots from being recorded\nlet isRestoredState = false;\n\nexport default function RecoilizeDebugger(props: any) {\n  // We should ask for Array of atoms and selectors.\n  // Captures all atoms that were defined to get the initial state\n  let nodes = null;\n\n  if (typeof props.nodes === 'object' && !Array.isArray(props.nodes)) {\n    nodes = Object.values(props.nodes);\n  } else if (Array.isArray(props.nodes)) {\n    nodes = props.nodes;\n  }\n\n  const {root} = props;\n\n  const snapshot: any = useRecoilSnapshot();\n\n  // Local state of all previous snapshots to use for time traveling when requested by dev tools.\n  const [snapshots, setSnapshots] = useState([snapshot]);\n  // const [isRestoredState, setRestoredState] = useState(false);\n  const gotoSnapshot = useGotoRecoilSnapshot();\n\n  const filteredSnapshot: any = {};\n  const currentTree = snapshot._store.getState().currentTree;\n\n  // Traverse all atoms and selector state nodes and get value\n  nodes.forEach((node: any) => {\n    const type = node.__proto__.constructor.name;\n    const contents = snapshot.getLoadable(node).contents;\n    const nodeDeps = currentTree.nodeDeps.get(node.key);\n    const nodeToNodeSubscriptions = currentTree.nodeToNodeSubscriptions.get(\n      node.key,\n    );\n\n    // Construct node data structure for dev tool to consume\n    filteredSnapshot[node.key] = {\n      type,\n      contents,\n      nodeDeps: nodeDeps ? Array.from(nodeDeps) : [],\n      nodeToNodeSubscriptions: nodeToNodeSubscriptions\n        ? Array.from(nodeToNodeSubscriptions)\n        : [],\n    };\n  });\n\n  // React lifecycle hook on re-render\n  useEffect(() => {\n    if (!isRestoredState) {\n      setTimeout(() => {\n        const devToolData = createDevToolDataObject(filteredSnapshot);\n\n        // Post message to content script on every re-render of the developers application only if content script has started\n        sendWindowMessage('recordSnapshot', devToolData);\n      }, 0);\n    } else {\n      isRestoredState = false;\n    }\n\n    // Window listener for messages from dev tool UI & background.js\n    window.addEventListener('message', onMessageReceived);\n\n    // Clears the window event listener.\n    return () => window.removeEventListener('message', onMessageReceived);\n  });\n\n  // Listener callback for messages sent to window\n  const onMessageReceived = (msg: any) => {\n    // Add other actions from dev tool here\n    switch (msg.data.action) {\n      // Checks to see if content script has started before sending initial snapshot\n      case 'contentScriptStarted':\n        const initialFilteredSnapshot = formatAtomSelectorRelationship(\n          filteredSnapshot,\n        );\n        const devToolData = createDevToolDataObject(initialFilteredSnapshot);\n        sendWindowMessage('moduleInitialized', devToolData);\n        break;\n      // Listens for a request from dev tool to time travel to previous state of the app.\n      case 'snapshotTimeTravel':\n        timeTravelToSnapshot(msg);\n        break;\n      default:\n        break;\n    }\n  };\n\n  // Sends window an action and payload message.\n  const sendWindowMessage = (action: any, payload: any) => {\n    window.postMessage(\n      {\n        action,\n        payload,\n      },\n      '*',\n    );\n  };\n\n  const createDevToolDataObject = (filteredSnapshot: any) => {\n    return {\n      filteredSnapshot: filteredSnapshot,\n      componentAtomTree: formatFiberNodes(\n        root._reactRootContainer._internalRoot.current,\n      ),\n    };\n  };\n\n  const formatAtomSelectorRelationship = (filteredSnapshot: any) => {\n    const windowAny: any = window;\n\n    if (\n      windowAny.$recoilDebugStates &&\n      Array.isArray(windowAny.$recoilDebugStates) &&\n      windowAny.$recoilDebugStates.length\n    ) {\n      let snapObj =\n        windowAny.$recoilDebugStates[windowAny.$recoilDebugStates.length - 1];\n      if (snapObj.hasOwnProperty('nodeDeps')) {\n        for (let [key, value] of snapObj.nodeDeps) {\n          filteredSnapshot[key].nodeDeps = Array.from(value);\n        }\n      }\n      if (snapObj.hasOwnProperty('nodeToNodeSubscriptions')) {\n        for (let [key, value] of snapObj.nodeToNodeSubscriptions) {\n          filteredSnapshot[key].nodeToNodeSubscriptions = Array.from(value);\n        }\n      }\n    }\n    return filteredSnapshot;\n  };\n\n  // FOR TIME TRAVEL: time travels to a given snapshot, re renders application.\n  const timeTravelToSnapshot = async (msg: any) => {\n    isRestoredState = true;\n    await gotoSnapshot(snapshots[msg.data.payload.snapshotIndex]);\n  };\n\n  // FOR TIME TRAVEL: Recoil hook to fire a callback on every snapshot change\n  useRecoilTransactionObserver_UNSTABLE(({snapshot}) => {\n    if (!isRestoredState) {\n      setSnapshots([...snapshots, snapshot]);\n    }\n  });\n\n  return null;\n}\n"
  },
  {
    "path": "package/package.json",
    "content": "{\n  \"name\": \"recoilize\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Recoil Dev Tool\",\n  \"main\": \"index.js\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/open-source-labs/Recoilize\"\n  },\n  \"peerDependencies\": {\n    \"react\": \"^16.13.1\",\n    \"react-dom\": \"^16.13.1\",\n    \"recoil\": \"^0.1.2\"\n  },\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"author\": \"Bren Yamaguchi, Saejin Kang, Jonathan Escamilla, Sean Smith, Justin Choo, Anthony Lin, Spenser Schwartz, Steven Nguyen, Henry Taing, Seungho Baek, Taven Shumaker, Aaron Yang, Jesus Vargas, Davide Molino, Janis Hernandez, Jaime Baik, Anthony Magallanes, Edward Shei, Leonard Lew, Joey Ma, Harvey Nguyen, Victor Wang\",\n  \"license\": \"MIT\",\n  \"devDependencies\": {\n    \"typescript\": \"^3.9.6\"\n  }\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"recoilize\",\n  \"jest\": {\n    \"setupFiles\": [\n      \"jest-webextension-mock\"\n    ]\n  },\n  \"version\": \"3.0.0\",\n  \"description\": \"A Chrome extension that helps debug Recoil applications by memorizing the state of components with every render.\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"build\": \"webpack --mode production\",\n    \"dev\": \"webpack --mode development --watch\",\n    \"test\": \"jest --verbose\",\n    \"lint\": \"eslint '**/*.tsx' '**/*.ts' '**/*.js' --ignore-path .gitignore\"\n  },\n  \"author\": \"Bren Yamaguchi, Saejin Kang, Jonathan Escamilla, Sean Smith, Justin Choo, Anthony Lin, Spenser Schwartz, Steven Nguyen, Henry Taing, Seungho Baek, Taven Shumaker, Aaron Yang, Jesus Vargas, Davide Molino, Janis Hernandez, Jaime Baik, Anthony Magallanes, Edward Shei, Leonard Lew, Joey Ma, Harvey Nguyen, Victor Wang\",\n  \"license\": \"MIT\",\n  \"devDependencies\": {\n    \"@babel/core\": \"^7.10.3\",\n    \"@babel/polyfill\": \"^7.10.4\",\n    \"@babel/preset-env\": \"^7.10.3\",\n    \"@babel/preset-react\": \"^7.10.1\",\n    \"@babel/preset-typescript\": \"^7.10.4\",\n    \"@testing-library/jest-dom\": \"^5.11.0\",\n    \"@testing-library/react\": \"^10.4.6\",\n    \"@types/react\": \"^16.9.41\",\n    \"@types/react-dom\": \"^16.9.8\",\n    \"babel-eslint\": \"^10.1.0\",\n    \"babel-loader\": \"^8.1.0\",\n    \"css-loader\": \"^3.6.0\",\n    \"enzyme\": \"^3.11.0\",\n    \"enzyme-adapter-react-16\": \"^1.15.2\",\n    \"eslint\": \"^7.4.0\",\n    \"eslint-config-fbjs\": \"^3.1.1\",\n    \"eslint-config-prettier\": \"^6.11.0\",\n    \"eslint-plugin-babel\": \"^5.3.1\",\n    \"eslint-plugin-flowtype\": \"^5.2.0\",\n    \"eslint-plugin-jsx-a11y\": \"^6.3.1\",\n    \"eslint-plugin-prettier\": \"^3.1.4\",\n    \"eslint-plugin-react\": \"^7.20.3\",\n    \"jest\": \"^26.1.0\",\n    \"jest-webextension-mock\": \"^3.6.1\",\n    \"prettier\": \"^2.0.5\",\n    \"recoil\": \"^0.7.2\",\n    \"style-loader\": \"^2.0.0\",\n    \"ts-loader\": \"^7.0.5\",\n    \"typescript\": \"^3.9.6\",\n    \"webpack\": \"^4.43.0\",\n    \"webpack-chrome-extension-reloader\": \"^1.3.0\",\n    \"webpack-cli\": \"^3.3.12\"\n  },\n  \"dependencies\": {\n    \"@angular/cli\": \"^10.0.5\",\n    \"@angular/core\": \"^10.0.8\",\n    \"@popperjs/core\": \"^2.4.4\",\n    \"@reduxjs/toolkit\": \"^1.5.0\",\n    \"@testing-library/react-hooks\": \"^3.4.1\",\n    \"@types/chrome\": \"0.0.117\",\n    \"@types/react-html-parser\": \"^2.0.1\",\n    \"@types/react-json-tree\": \"^0.6.11\",\n    \"@types/react-redux\": \"^7.1.16\",\n    \"@vx/group\": \"0.0.196\",\n    \"@vx/hierarchy\": \"0.0.196\",\n    \"@vx/responsive\": \"0.0.196\",\n    \"babel-eslint\": \"^10.1.0\",\n    \"codemirror\": \"^5.65.2\",\n    \"d3\": \"^5.16.0\",\n    \"d3-hierarchy\": \"1.1.9\",\n    \"d3-interpolate\": \"1.4.0\",\n    \"d3-scale\": \"3.2.1\",\n    \"d3-scale-chromatic\": \"1.5.0\",\n    \"d3-shape\": \"1.3.7\",\n    \"jest-webextension-mock\": \"^3.6.1\",\n    \"jsondiffpatch\": \"^0.4.1\",\n    \"multiselect-react-dropdown\": \"^1.5.7\",\n    \"prop-types\": \"^15.7.2\",\n    \"rc-slider\": \"^9.7.5\",\n    \"rc-tooltip\": \"^5.1.1\",\n    \"react\": \"^16.13.1\",\n    \"react-codemirror2\": \"^7.2.1\",\n    \"react-dom\": \"^16.13.1\",\n    \"react-html-parser\": \"^2.0.2\",\n    \"react-json-tree\": \"^0.11.2\",\n    \"react-multi-select-component\": \"^3.0.1\",\n    \"react-redux\": \"^7.2.3\",\n    \"react-router-dom\": \"^5.2.0\",\n    \"react-spring\": \"^8.0.27\",\n    \"react-testing-library\": \"^8.0.1\",\n    \"redux-persist\": \"^6.0.0\",\n    \"rxjs\": \"^6.6.2\",\n    \"semantic-ui-react\": \"^1.1.1\",\n    \"style-loader\": \"^2.0.0\"\n  },\n  \"engines\": {\n    \"node\": \"^10.12.0 || >=12.0.0\"\n  }\n}\n"
  },
  {
    "path": "src/README.md",
    "content": "# Developer README\n\n## Brief\nThough Recoil.js is still in the experimental state, it has proved its ability and catched a tremendous amount of attentions from experience developers for the past three years. We created Recoilize with one great mission in mind - providing a helpful tool so the developers can make an easy transition to Recoil.js and making the debugging process more effective. Recoilize is an open source product that is maintained and iterated constantly, and we're always welcome developers who are interested in it. Getting on board and understanding the codebase are never easy, so here are some useful tips and information that will help you get started quickly.\n\n## File Structure\nThe scr folder contains Recoilize's source code - frontend and chrome extension.\n\n```\nsrc/\n├── app/                      # Frontend code\n│   ├── components/           # React components\n│   ├── Containers/           # More React components\n│   ├── state-management/     # Redux Toolkit Slices\n│   ├── utils/                # Helper Functions  \n│   └── index.tsx             # Starting point for root App component \n│\n├── types/                           \n│   └── index.d.ts            #\n│\n├── extension/                # Chrome Extension code\n│   ├── build/                # Destination for bundles and manifest.json (Chrome config file)\n│   │                         #\n│   ├── background.js         # Chrome Background Script\n│   └── contentScript.ts      # Chrome Content Script\n└──\n```\nHere is an in-depth view of the app's components:\n\n![FRONTEND DATA FLOW](../assets/Diagram.png)\n\n## Diagramming\nIf there's an update in the file structure, we suggest using [excalidraw](https://excalidraw.com/)\n\n## Future Features and Possible Improvements\n- Optimizing Time Travel Algorithm. The Time Travel Algorithm is working perfectly. However, we believe that it can be better by implementing a better algorithm (or different approaches). Please note that there's no right or wrong approaches, and everyone is welcome to try new ideas.\n- UI/UX improvement. The UI/UX aspect definitely has room for improvement. Please feel free to play around with the app to get some ideas. For example, look at the component graph, atom network, graphs (flame, ranked, comparison).\n- Testing is an important aspect, and we believe in TDD (Test Driven Development). However, due to time constraint and the limitation of resources, Recoilize is missing the testing part. Since testing has so much potential to improve, we strongly recommend developers who love testing get started as soon as you get onboard.\n- Documentations - Documentations often get neglected since developers usually focus on writing code to improve features. However, at Recoilize, we believe that documenting is one of the most important tasks. Why? Because it's helpful not only for you, your teammates but it can also be valuable for future developers who interested in Recoilize. So if you love Recoilize, take good care of it by writing good documentations.\n- Containerization - Have you ever wondered why the app that you made ran perfectly on your computer but it couldn't run on other's computers. That is a common problem that software engineers often encounter. We can solve this by containerize our app, and `Docker` is a good candidate for this task.\n"
  },
  {
    "path": "src/app/Containers/ButtonsContainer.tsx",
    "content": "import React from 'react';\n\nconst ButtonsContainer = () => {\n  const howToUseHandler = () => {\n    window.open('https://github.com/open-source-labs/Recoilize', '_blank');\n  };\n  return (\n    <div className=\"buttons_container\">\n      <button\n        id=\"docs_button\"\n        type=\"button\"\n        onClick={() => {\n          howToUseHandler();\n        }}>\n        How To Use\n      </button>\n    </div>\n  );\n};\n\nexport default ButtonsContainer;\n"
  },
  {
    "path": "src/app/Containers/MainContainer.tsx",
    "content": "import React from 'react';\nimport SnapshotsContainer from './SnapshotContainer';\nimport VisualContainer from './VisualContainer';\nimport TravelContainer from './TravelContainer';\n\nconst MainContainer: React.FC = () => {\n  return (\n    <div className=\"MainContainer\">\n      <SnapshotsContainer />\n      <VisualContainer />\n      <TravelContainer />\n    </div>\n  );\n};\n\nexport default MainContainer;\n"
  },
  {
    "path": "src/app/Containers/SnapshotContainer.tsx",
    "content": "import React, {useEffect, useState, useRef} from 'react';\nimport {selectFilterState} from '../state-management/slices/FilterSlice';\nimport {useAppSelector, useAppDispatch} from '../state-management/hooks';\nimport {setRenderIndex} from '../state-management/slices/SnapshotSlice';\n\nconst SnapshotsContainer: React.FC = () => {\n  const dispatch = useAppDispatch();\n\n  const snapshotHistory = useAppSelector(\n    state => state.snapshot.snapshotHistory,\n  );\n  const selected = useAppSelector(state => state.selected.selectedData);\n\n  const renderIndex = useAppSelector(state => state.snapshot.renderIndex);\n  const filterData = useAppSelector(selectFilterState);\n  const snapshotEndRef = useRef<HTMLDivElement>(null);\n\n  let snapshotHistoryLength = snapshotHistory.length;\n\n  // useEffect to scroll bottom whenever snapshot history changes\n  useEffect(() => {\n    scrollToBottom();\n  }, [snapshotHistoryLength]);\n\n  // using scrollInToView makes a smoother scroll\n  const scrollToBottom = (): void => {\n    snapshotEndRef.current.scrollIntoView({behavior: 'smooth'});\n  };\n\n  const snapshotDivs: JSX.Element[] = [];\n  // iterate the same length of our snapshotHistory\n  for (let i = 0; i < snapshotHistoryLength; i++) {\n    // filterFunc will return false if there is no change to state\n    const filterFunc = (): boolean => {\n      // don't use the counter for this, not reliable\n      if (i === 0) {\n        return true;\n      }\n      // checks if the filteredSnapshot object at index i has a key (atom or selector) found in the selected array. This would indicate that there was a change to that state/selector because filter is an array of objects containing differences between snapshots.\n      if (filterData[i]) {\n        for (let key in filterData[i].filteredSnapshot) {\n          for (let j = 0; j < selected.length; j++) {\n            if (key === selected[j].name) {\n              return true;\n            }\n          }\n        }\n      }\n      return false;\n    };\n    const x: boolean = filterFunc();\n\n    if (x === false) {\n      continue;\n    }\n\n    // renderTime is set equal to the actualDuration. If i is zero then we are obtaining actualDuration from the very first snapshot in snapshotHistory. This is to avoid having undefined filter elements since there will be no difference between snapshot at the first instance.\n    let renderTime: number =\n      snapshotHistory[i].componentAtomTree.treeBaseDuration;\n    // if (i === 0) {\n    //   renderTime = snapshotHistory[0].componentAtomTree.treeBaseDuration;\n    // }\n    // //Checks to see if the actualDuration within filter is an array. If it is an array then the 2nd value in the array is the new actualDuration.\n    // else if (Array.isArray(filterData[i].componentAtomTree.actualDuration)) {\n    //   renderTime = filterData[i].componentAtomTree.treeBaseDuration[1];\n    // } else {\n    //   renderTime = filterData[i].componentAtomTree.treeBaseDuration;\n    // }\n\n    // Push a div container to snapshotDivs array only if there was a change to state.\n    // The div container will contain renderTimes evaluated above.\n    snapshotDivs.push(\n      <div\n        id={`snapshot${i}`}\n        className=\"individualSnapshot\"\n        key={i}\n        style={\n          renderIndex === i\n            ? {color: '#E6E6E6', backgroundColor: '#212121'}\n            : {color: '#989898'}\n        }\n        onClick={() => {\n          dispatch(setRenderIndex(i));\n        }}>\n        {/* <li>{i}</li> */}\n        <li>{`${Math.round(renderTime * 100) / 100}ms`}</li>\n        <button\n          className=\"timeTravelButton\"\n          style={\n            renderIndex === i\n              ? {color: '#E6E6E6', backgroundColor: '#212121'}\n              : {}\n          }\n          onClick={() => {\n            timeTravelFunc(i);\n          }}>\n          Jump\n        </button>\n      </div>,\n    );\n  }\n\n  //indexDiff is used to ensure the index of filter matches the index of the snapshots array in the backend\n  let indexDiff: number = 0;\n  if (filterData[0] && filterData[0].indexDiff) {\n    indexDiff = filterData[0].indexDiff;\n  }\n\n  // functionality to postMessage the selected snapshot index to background.js\n  const timeTravelFunc = (index: number) => {\n    // variable to store/reference connection\n    const backgroundConnection = chrome.runtime.connect();\n    //const test = chrome.extension.getBackgroundPage();\n    // post the message with index in payload to the connection\n    backgroundConnection.postMessage({\n      action: 'snapshotTimeTravel',\n      tabId: chrome.devtools.inspectedWindow.tabId,\n      payload: {\n        snapshotIndex: index + indexDiff,\n      },\n    });\n  };\n\n  function prevClr() {\n    const snapshotListArr = document.querySelectorAll('.individualSnapshot');\n    for (let i = 0; i < snapshotListArr.length; i++) {\n      let index = parseInt(snapshotListArr[i].id.match(/\\d+/g)[0]);\n\n      if (index < renderIndex) {\n        snapshotListArr[i].parentNode.removeChild(snapshotListArr[i]);\n      } else break;\n    }\n  }\n\n  function fwrdClr() {\n    const snapshotListArr = document.querySelectorAll('.individualSnapshot');\n\n    for (let i = snapshotListArr.length - 1; i >= 0; i--) {\n      let index = parseInt(snapshotListArr[i].id.match(/\\d+/g)[0]);\n\n      if (index > renderIndex) {\n        snapshotListArr[i].parentNode.removeChild(snapshotListArr[i]);\n      } else break;\n    }\n  }\n\n  // create a function to store current data to local storage\n  const toLocalStorage = (data: any) => {\n    for (let i = 0; i < data.length; i++) {\n      console.log('trigger toLocalStorage');\n      const jsonData = JSON.stringify(data[i]);\n      localStorage.setItem(`${i}`, jsonData);\n    }\n  };\n\n  return (\n    <div className=\"SnapshotsContainer\">\n      <div id=\"clear-snapshots-title\">Clear Snapshots</div>\n      <div className=\"clear-buttons\">\n        <button onClick={prevClr} id=\"prevClr\">\n          Previous\n        </button>\n        <button onClick={fwrdClr} id=\"fwrdClr\">\n          Forward\n        </button>\n      </div>\n      <span\n        style={{\n          fontSize: '14px',\n          fontWeight: 'bold',\n          marginTop: '10px',\n          marginBottom: '10px',\n        }}>\n        Snapshots\n      </span>\n      <button\n        className=\"save-series-button\"\n        onClick={e => {\n          toLocalStorage(snapshotHistory);\n        }}>\n        Save Series\n      </button>\n      <div className=\"SnapshotsList\">\n        <div>{snapshotDivs}</div>\n        <div ref={snapshotEndRef} />\n      </div>\n    </div>\n  );\n};\n\nexport default SnapshotsContainer;\n"
  },
  {
    "path": "src/app/Containers/TravelContainer.tsx",
    "content": "import React from 'react';\nimport MainSlider from '../components/Slider/MainSlider';\nimport ButtonsContainer from './ButtonsContainer';\n\nconst TravelContainer: React.FC = () => {\n  return (\n    <div className=\"travel-container\">\n      <MainSlider />\n      <ButtonsContainer />\n    </div>\n  );\n};\n\nexport default TravelContainer;\n"
  },
  {
    "path": "src/app/Containers/VisualContainer.tsx",
    "content": "import React, {useState} from 'react';\nimport {RecoilRoot} from 'recoil';\nimport Diff from '../components/StateDiff/Diff';\nimport NavBar from '../components/NavBar/NavBar';\nimport Metrics from '../components/Metrics/MetricsContainer';\nimport Tree from '../components/StateTree/Tree';\nimport Network from '../components/AtomNetwork/AtomNetwork';\nimport AtomComponentVisualContainer from '../components/ComponentGraph/AtomComponentContainer';\nimport Settings from '../components/Settings/SettingsContainer';\nimport Testing from '../components/Testing/TestingContainer';\n\ntype navTypes = {\n  [tabName: string]: JSX.Element;\n};\n\n// Renders Navbar and conditionally renders Diff, Visualizer, and Tree\nconst VisualContainer: React.FC = () => {\n  // object containing all conditional renders based on navBar\n  const nav: navTypes = {\n    // compare the diff of filteredPrevSnap and filteredCurSnap\n    'State Diff': <Diff />,\n    // render JSON tree of snapshot\n    'State Tree': <Tree />,\n    // tree visualizer of components showing atom/selector relationships\n    'Component Graph': <AtomComponentVisualContainer />,\n\n    // atom and selector subscription relationship\n    'Atom Network': <Network />,\n\n    // quotes not needed where name = component variable\n    // individual snapshot visualizer\n    Metrics: <Metrics />,\n\n    // settings tab\n    Settings: <Settings />,\n    // add a testing tab\n    Testing: (\n      <RecoilRoot>\n        <Testing />\n      </RecoilRoot>\n    ),\n  };\n  // array of all nav obj keys\n  const tabsList: string[] = Object.keys(nav);\n  // useState hook to update which component to render in the VisualContainer\n  const [tab, setTab] = useState<string>('State Diff');\n  // conditionally render based on value of nav[tab]\n  return (\n    <div className=\"VisualContainer\">\n      <NavBar setTab={setTab} tabsList={tabsList} tab={tab} />\n      {nav[tab]}\n    </div>\n  );\n};\n\nexport default VisualContainer;\n"
  },
  {
    "path": "src/app/Containers/__tests__/MainContainer.unit.test.js",
    "content": "import React, {useState} from 'react';\nimport ReactDOM from 'react-dom';\nimport {getQueriesForElement, getByText} from '@testing-library/dom';\n\nimport {render, fireEvent} from '@testing-library/react';\n\nimport MainContainer from '../MainContainer';\n\nit('Main Container Renders', () => {\n  window.HTMLElement.prototype.scrollIntoView = jest.fn();\n  const {getByPlaceholderText, debug} = render(\n    <MainContainer snapshotHistory={[]} />,\n  );\n});\n"
  },
  {
    "path": "src/app/Containers/__tests__/SnapshotContainer.unit.test.js",
    "content": "import React, {useState} from 'react';\nimport ReactDOM from 'react-dom';\nimport {getQueriesForElement, getByText} from '@testing-library/dom';\n\nimport {render, fireEvent} from '@testing-library/react';\n\nimport SnapshotContainer from '../SnapshotContainer';\n\nit('Snapshot Container Renders', () => {\n  window.HTMLElement.prototype.scrollIntoView = jest.fn();\n  const {getByPlaceholderText, debug} = render(\n    <SnapshotContainer snapshotHistory={[]} />,\n  );\n});\n"
  },
  {
    "path": "src/app/Containers/__tests__/VisualContainer.unit.test.js",
    "content": "import React, {useState} from 'react';\nimport ReactDOM from 'react-dom';\nimport {getQueriesForElement, getByText} from '@testing-library/dom';\n\nimport {render, fireEvent} from '@testing-library/react';\n\nimport VisualContainer from '../VisualContainer';\n\nit('Visual Container Renders', () => {\n  window.HTMLElement.prototype.scrollIntoView = jest.fn();\n  const {getByPlaceholderText, debug} = render(\n    <VisualContainer snapshotHistory={[]} />,\n  );\n});\n"
  },
  {
    "path": "src/app/components/App.tsx",
    "content": "import React, {useEffect} from 'react';\nimport MainContainer from '../Containers/MainContainer';\nimport {selectedTypes} from '../../types';\n// importing the diff to find difference\nimport {diff} from 'jsondiffpatch';\nimport {useAppSelector, useAppDispatch} from '../state-management/hooks';\nimport {\n  setSnapshotHistory,\n  setRenderIndex,\n  setCleanComponentAtomTree,\n} from '../state-management/slices/SnapshotSlice';\nimport {\n  addSelected,\n  setSelected,\n} from '../state-management/slices/SelectedSlice';\nimport {\n  updateFilter,\n  selectFilterState,\n} from '../state-management/slices/FilterSlice';\nimport {setAtomsAndSelectors} from '../state-management/slices/AtomsAndSelectorsSlice';\n\nconst LOGO_URL = './assets/Recoilize-v2.png';\nconst App: React.FC = () => {\n  const dispatch = useAppDispatch();\n\n  // useState hook to update the snapshotHistory array\n  // array of snapshots\n  const snapshotHistory = useAppSelector(\n    state => state.snapshot.snapshotHistory,\n  );\n  const renderIndex = useAppSelector(state => state.snapshot.renderIndex);\n  const selected = useAppSelector(state => state.selected.selectedData);\n  // selected will be an array with objects containing filteredSnapshot key names (the atoms and selectors)\n  // ex: [{name: 'Atom1'}, {name: 'Atom2'}, {name: 'Selector1'}, ...]\n  // const [selected, setSelected] = useState<selectedTypes[]>([]);\n\n  // todo: Create algo that will clean up the big setSnapshothistory object, now and before\n  // ! Setting up the selected\n  const filterData = useAppSelector(selectFilterState);\n\n  // Whenever snapshotHistory changes, useEffect will run, and selected will be updated\n  useEffect(() => {\n    // whenever snapshotHistory changes, update renderIndex\n    dispatch(setRenderIndex(snapshotHistory.length - 1));\n\n    let last;\n    if (snapshotHistory[renderIndex]) {\n      last = snapshotHistory[renderIndex].filteredSnapshot;\n    }\n    // we must compare with the original\n    for (let key in last) {\n      if (!snapshotHistory[0].filteredSnapshot[key]) {\n        // only push if the name doesn't already exist\n        const check = () => {\n          for (let i = 0; i < selected.length; i++) {\n            // break if it exists\n            if (selected[i].name === key) {\n              return true;\n            }\n          }\n          // does not exist\n          return false;\n        };\n        if (!check()) {\n         // console.log('after Check');\n          dispatch(addSelected({name: key}));\n        }\n      }\n    }\n  }, [snapshotHistory]); // Only re-run the effect if snapshot history changes -- react hooks\n\n  //Update cleanComponentAtomTree as Render Index changes\n\n  useEffect(() => {\n    if (snapshotHistory.length === 0) return;\n    dispatch(\n      setCleanComponentAtomTree(snapshotHistory[renderIndex].componentAtomTree),\n    );\n  }, [renderIndex]);\n\n  // useEffect for snapshotHistory\n  useEffect(() => {\n    // SETUP connection to bg script\n    const backgroundConnection = chrome.runtime.connect();\n    // INITIALIZE connection to bg script\n    backgroundConnection.postMessage({\n      action: 'devToolInitialized',\n      tabId: chrome.devtools.inspectedWindow.tabId,\n    });\n    // console.log(\n    //   'here is the background connection post message IN APP',\n    //   backgroundConnection,\n    // );\n    // LISTEN for messages FROM bg script\n    backgroundConnection.onMessage.addListener(msg => {\n      if (msg.action === 'recordSnapshot') {\n        // ! sets the initial selected\n        //console.log('should have our atoms and selectors: ', msg.payload);\n        if (!msg.payload[1]) {\n          // ensures we only set initially\n          const arr: selectedTypes[] = [];\n          for (let key in msg.payload[0].filteredSnapshot) {\n            arr.push({name: key});\n          }\n          // setSelected(arr);\n          //console.log('arr in App.tsx send to setSelected', arr)\n          dispatch(setSelected(arr));\n        }\n        // console.log(\n        //   'this is snapshotHistory',\n        //   msg.payload[msg.payload.length - 1],\n        // );\n        dispatch(setSnapshotHistory(msg.payload[msg.payload.length - 1]));\n\n        // update state with the atoms and selectors!!!\n        dispatch(setAtomsAndSelectors(msg.payload[0].atomsAndSelectors));\n        \n        //console.log('Payload: IS IT HERE??, ', msg.payload);\n\n        // ! Setting the FILTER Array\n        if (!msg.payload[1] && filterData.length === 0) {\n          // todo: currently the filter does not work if recoilize is not open, we must change msg.payload to incorporate delta function in the backend\n          dispatch(updateFilter(msg.payload));\n        } else {\n          // push the difference between the objects\n          const delta = diff(\n            msg.payload[msg.payload.length - 2],\n            msg.payload[msg.payload.length - 1],\n          );\n          // only push if the snapshot length is chill\n          if (filterData.length < msg.payload.length) {\n            dispatch(updateFilter([delta]));\n          }\n        }\n      }\n    });\n  }, []);\n\n  // Render main container if we have detected a recoil app with the recoilize module passing data\n  const renderMainContainer: JSX.Element = <MainContainer />;\n\n  // Render module not found message if snapHistory is null, this means we have not detected a recoil app with recoilize module installed properly\n  const renderModuleNotFoundContainer: JSX.Element = (\n    <div className=\"notFoundContainer\">\n      <img className=\"logo\" src={LOGO_URL} />\n      <p>\n        Supported only with Recoil apps with the Recoilize NPM module.\n        <br />\n        Please follow the installation instructions at&nbsp;\n        <a\n          target=\"_blank\"\n          href=\"https://github.com/open-source-labs/Recoilize\"\n          rel=\"noreferrer\">\n          Recoilize\n        </a>\n      </p>\n    </div>\n  );\n  return (\n    <div className=\"App\" key=\"App\">\n      {snapshotHistory.length\n        ? renderMainContainer\n        : renderModuleNotFoundContainer}\n    </div>\n  );\n};\nexport default App;\n"
  },
  {
    "path": "src/app/components/AtomNetwork/AtomNetwork.tsx",
    "content": "import React from 'react';\nimport AtomNetworkLegend from './AtomNetworkLegend';\nimport AtomNetworkVisual from './AtomNetworkVisual';\n\n//Create the container passing in the JSX props for each individual component\nconst AtomNetwork: React.FC = () => {\n  return (\n    <div className=\"networkContainer\">\n      <AtomNetworkVisual />\n      <AtomNetworkLegend />\n    </div>\n  );\n};\n\nexport default AtomNetwork;\n"
  },
  {
    "path": "src/app/components/AtomNetwork/AtomNetworkLegend.tsx",
    "content": "import React, {useState} from 'react';\nimport {useAppSelector, useAppDispatch} from '../../state-management/hooks';\nimport { setSearchValue } from '../../state-management/slices/AtomNetworkSlice';\n\nconst AtomNetworkLegend: React.FC = () => {\n  const dispatch = useAppDispatch();\n\n  //Retrieve snapshotHistory State from Redux Store\n  const snapshotHistory = useAppSelector(\n    state => state.snapshot.snapshotHistory,\n  );\n  const renderIndex = useAppSelector(state => state.snapshot.renderIndex);\n  const filteredCurSnap =\n  snapshotHistory[renderIndex].filteredSnapshot;\n\n   // an array of atoms and selector sub\n   const atomAndSelectorArr = Object.entries(filteredCurSnap);\n  \n    // array of atom names (what the drop down showAtomMenu is going to display)\n    const [atomList] = useState(atomAndSelectorArr.filter(([isAtom, obj]: [string, any])=> !obj.nodeDeps.length ? isAtom : null));\n\n    // array of selectors (what the drop down showSelectorMenu is going to display)\n    const [selectorList] = useState(atomAndSelectorArr.filter(([isSelector, obj]:[string, any]) => obj.nodeDeps.length ? isSelector : null));\n\n    //state hook for showing list of atoms\n    const [showAtomMenu, setShowAtomMenu] = useState(false);\n    \n    //state hook for showing list of selectors\n    const [showSelectorMenu, setShowSelectorMenu] = useState(false);\n    const [atomButtonClicked, setAtomButtonClicked] = useState<boolean>(false);\n    const [selectorButtonClicked, setSelectorButtonClicked] = useState<boolean>(false);\n\n    // function to handle change in search bar. Sets searchValue state\n     const handleChange = (e: any) => {\n        dispatch(setSearchValue(e.target.value));\n     };\n\n    // handles clicking on Selector and Atom buttom to bring down\n    // list of atoms or selects\n    function openDropdown(e: React.MouseEvent) {\n        // if user clicks on atom list button\n        const target = e.target as Element;\n        if(target.id === 'AtomP') {\n        // check if selector list was previously open, if it is, close it\n        if(showSelectorMenu) setShowSelectorMenu(false);\n        // open atom list\n        setShowAtomMenu(!showAtomMenu);\n        // empty search box\n        dispatch(setSearchValue(''));\n        setAtomButtonClicked(true);\n        setSelectorButtonClicked(false);\n        }\n        // if user clicks on selector list button\n        else if(target.id === 'SelectorP') {\n        // check if atom list was previously open, if it is, close it\n        if(showAtomMenu) setShowAtomMenu(false);\n        // show Selector list\n        setShowSelectorMenu(!showSelectorMenu);\n        // empty search box\n        dispatch(setSearchValue(''));\n        setSelectorButtonClicked(true);\n        setAtomButtonClicked(false);\n        }\n    }\n\n    return (\n\n        <div className=\"LegendContainer\">\n            <div className= \"RecoilSearch\"> \n                <input\n                id=\"networkSearch\"\n                type=\"text\"\n                placeholder=\"search for state\"\n                //check the input value to render corresponding and related nodes\n                onChange={handleChange}\n                />\n            </div>\n            <div className=\"AtomNetworkLegendWithSearch\">\n              \n                <div className=\"AtomLegend\"> </div>\n                    {/* //change the visibility of the div depending the value of the state */}\n                    <button\n                        onClick={openDropdown}\n                        id=\"AtomP\"\n                        className={\n                        atomButtonClicked ? \"AtomP atomSelected\" : \"AtomP atomLegendDefault\"\n                        }>\n                        ATOM\n                    </button>\n            \n                <div className=\"SelectorLegend\"></div> \n                    <button\n                        onClick={openDropdown}\n                        id=\"SelectorP\"\n                        className={\n                        selectorButtonClicked ? \"SelctorP selectorSelected\" : \"SelectorP selectorLegendDefault\"\n                        }>\n                        SELECTOR\n                    </button>\n              \n            {/* conditional rendering of dropdowns depending on the value of the state */}\n            {showAtomMenu &&\n                <div className=\"AtomDropdown\">{atomList.map(([atom, atomObj], i)=> {\n                return (\n                    <div className= \"dropDownButtonDiv\">\n                <button key={i} id={`atom-drop${i}`} className='atom-class atomDropDown' onClick={(event: React.MouseEvent) => {\n                    if (\n                        !(event.target as HTMLInputElement).classList.contains(\n                          'atomSelected',\n                        ) &&\n                        (event.target as HTMLInputElement).classList.contains(\n                          'atomNotSelected',\n                        )\n                      ) {\n                        (event.target as HTMLInputElement).classList.replace(\n                          'atomNotSelected',\n                          'atomSelected',\n                        );\n                      } else if (\n                        !(event.target as HTMLInputElement).classList.contains(\n                          'atomSelected',\n                        ) &&\n                        !(event.target as HTMLInputElement).classList.contains(\n                          'atomNotSelected',\n                        )\n                      ) {\n                        (event.target as HTMLInputElement).classList.add(\n                          'atomSelected',\n                        );\n                      }\n  \n                      document.querySelectorAll('.atom-class').forEach(item => {\n                        if (\n                          item.id !== `atom-drop${i}` &&\n                          item.classList.contains('atomSelected')\n                        ) {\n                          item.classList.replace(\n                            'atomSelected',\n                            'atomNotSelected',\n                          );\n                        } else if (\n                          item.id !== `atom-drop${i}` &&\n                          !item.classList.contains('atomNotSelected')\n                        ) {\n                          item.classList.add('atomNotSelected');\n                        }\n                      });\n\n                    //set the search value to the name of the paragraph element to render only corresponding and related nodes\n                    dispatch(setSearchValue((event.target as Element).innerHTML));\n                }}>{atom}</button></div>)\n            })}</div>}\n            {showSelectorMenu &&\n                <div className=\"SelectorDropdown\">{selectorList.map(([selector, selectorObj], i) => {\n                return (\n                <div className=\"dropDownButtonDiv\">\n                <button key={i} id={`selector-drop${i}`} className='selector-class selectorDropDown' onClick={(event: React.MouseEvent) => {\n                    if (\n                        !(event.target as HTMLInputElement).classList.contains(\n                          'selectorSelected',\n                        ) &&\n                        (event.target as HTMLInputElement).classList.contains(\n                          'selectorNotSelected',\n                        )\n                      ) {\n                        (event.target as HTMLInputElement).classList.replace(\n                          'selectorNotSelected',\n                          'selectorSelected',\n                        );\n                      } else if (\n                        !(event.target as HTMLInputElement).classList.contains(\n                          'selectorSelected',\n                        ) &&\n                        !(event.target as HTMLInputElement).classList.contains(\n                          'selectorNotSelected',\n                        )\n                      ) {\n                        (event.target as HTMLInputElement).classList.add(\n                          'selectorSelected',\n                        );\n                      }\n  \n                      document\n                        .querySelectorAll('.selector-class')\n                        .forEach(item => {\n                          if (\n                            item.id !== `selector-drop${i}` &&\n                            item.classList.contains('selectorSelected')\n                          ) {\n                            item.classList.replace(\n                              'selectorSelected',\n                              'selectorNotSelected',\n                            );\n                          } else if (\n                            item.id !== `selector-drop${i}` &&\n                            !item.classList.contains('selectorNotSelected')\n                          ) {\n                            item.classList.add('selectorNotSelected');\n                          }\n                        });\n                    //set the search value to the name of the paragraph element to render only corresponding and related nodes\n                    dispatch(setSearchValue((event.target as Element).innerHTML));\n                }}>{selector}</button></div>)\n            })}</div>}\n            </div>\n        </div>\n    )\n}\n\nexport default AtomNetworkLegend; "
  },
  {
    "path": "src/app/components/AtomNetwork/AtomNetworkVisual.tsx",
    "content": "import React, {useState, useEffect} from 'react';\nimport * as d3 from 'd3';\nimport makeRelationshipLinks from '../../utils/makeRelationshipLinks';\nimport {useAppSelector} from '../../state-management/hooks';\nimport {filteredSnapshot} from '../../../types';\n\nconst AtomNetworkVisual: React.FC = () => {\n  //Retrieve snapshotHistory State from Redux Store\n  const snapshotHistory = useAppSelector(\n    state => state.snapshot.snapshotHistory,\n  );\n  const renderIndex = useAppSelector(state => state.snapshot.renderIndex);\n  const filteredCurSnap = snapshotHistory[renderIndex].filteredSnapshot;\n  //Retrieve atomNetworkSearch State from Redux Store\n  const searchValue = useAppSelector(state => state.atomNetwork.searchValue);\n\n  useEffect(() => {\n    // new filtered snap object to be constructed with search value\n    const newFilteredCurSnap: any = {};\n\n    // filters filteredCurSnap object with atoms and selectors that includes are search value\n    const filter = (filteredCurSnap: filteredSnapshot) => {\n      for (let key in filteredCurSnap) {\n        if (key.toLowerCase().includes(searchValue.toLowerCase())) {\n          newFilteredCurSnap[key] = filteredCurSnap[key];\n          grabNodeToNodeSubscriptions(newFilteredCurSnap[key]);\n          grabNodeDeps(newFilteredCurSnap[key]);\n        }\n      }\n    };\n\n    // helper functions to recursively include searched atoms/selectors' subscriptions\n    const grabNodeToNodeSubscriptions = (node: any) => {\n      let nodeSubscriptionLength = node.nodeToNodeSubscriptions.length;\n      if (nodeSubscriptionLength > 0) {\n        for (let i = 0; i < nodeSubscriptionLength; i += 1) {\n          let currSN = node.nodeToNodeSubscriptions[i];\n          newFilteredCurSnap[currSN] = filteredCurSnap[currSN];\n          grabNodeToNodeSubscriptions(filteredCurSnap[currSN]);\n        }\n      }\n    };\n    const grabNodeDeps = (node: any) => {\n      let nodeDepsLength = node.nodeDeps.length;\n      if (nodeDepsLength > 0) {\n        for (let i = 0; i < nodeDepsLength; i += 1) {\n          let currDepNode = node.nodeDeps[i];\n          newFilteredCurSnap[currDepNode] = filteredCurSnap[currDepNode];\n          grabNodeDeps(filteredCurSnap[currDepNode]);\n        }\n      }\n    };\n\n    // invoke filter to populate newFilteredCurSnap\n    filter(filteredCurSnap);\n    document.getElementById('networkCanvas').innerHTML = '';\n\n    let edgepaths: any;\n    let edgelabels: any;\n\n    const networkContainer = document.querySelector('.networkContainer');\n    // sets starting position of the atomNetwork graph.\n    // const width = networkContainer.clientWidth;\n    // const height = networkContainer.clientHeight;\n    const width = 300;\n    const height = 300;\n\n    // snap will be newFilteredCurSnap if searchValue exists, if not original\n    let snap: any = searchValue ? newFilteredCurSnap : filteredCurSnap;\n\n    // TRANSFORM DATA INTO D3 SUPPORTED FORMAT FOR NETWORK GRAPH\n    const networkData: any = makeRelationshipLinks(snap);\n\n    //Create Disjoint Force-Directed Graph\n\n    const chart = (data: any) => {\n      const links = data.links.map((d: any) => Object.create(d));\n      const nodes = data.nodes.map((d: any) => {\n        return Object.create(d);\n      });\n\n      const simulation = d3\n        .forceSimulation(nodes)\n        .force(\n          'link',\n          d3.forceLink(links).id((d: any) => d.id),\n        )\n        .force('charge', d3.forceManyBody())\n        .force('x', d3.forceX())\n        .force('y', d3.forceY());\n\n      const svg = d3\n        // .create('svg')\n        .select('#networkCanvas')\n        .attr('viewBox', [-width / 2, -height / 2, width, height]);\n\n      const link = svg\n        .append('g')\n        .attr('stroke', '#999')\n        .attr('stroke-opacity', 0.6)\n        .selectAll('line')\n        .data(links)\n        .join('line')\n        .attr('stroke-width', (d: any) => Math.sqrt(d.value));\n\n      const node = svg\n        .append('g')\n        .attr('stroke', '#fff')\n        .attr('stroke-width', 1.5)\n        .selectAll('circle')\n        .data(nodes)\n        .join('circle')\n        .attr('r', 5)\n        .style('fill', function (d: any, i: any) {\n          return d.name === 'Atom' ? '#9580FF' : '#FF80BF';\n        });\n\n      node\n        .append('title')\n        .attr('dx', 12)\n        // .attr('text-anchor', 'middle')\n        .attr('dy', '.35em')\n        .text((d: any) => d.label)\n        .attr('stroke', 'white')\n        .attr('stroke-width', 3);\n\n      node\n        .append('text')\n        .attr('dx', 12)\n        .attr('dy', '.35em')\n        .text(function (d: any) {\n          return d.name;\n        });\n\n      simulation.on('tick', () => {\n        link\n          .attr('x1', (d: any) => d.source.x)\n          .attr('y1', (d: any) => d.source.y)\n          .attr('x2', (d: any) => d.target.x)\n          .attr('y2', (d: any) => d.target.y);\n\n        node.attr('cx', (d: any) => d.x).attr('cy', (d: any) => d.y);\n      });\n\n      //Zoom functions\n      function zoomActions() {\n        svg.attr('transform', d3.event.transform);\n      }\n\n      var zoomHandler = d3.zoom().on('zoom', zoomActions);\n      zoomHandler(svg);\n\n      // allows the nodes to be draggable\n      const dragDrop = d3\n        .drag()\n        .on('start', (node: {fx: any; x: any; fy: any; y: any}) => {\n          node.fx = node.x;\n          node.fy = node.y;\n        })\n        .on('drag', (node: {fx: any; fy: any}) => {\n          simulation.alphaTarget(1).restart();\n          node.fx = d3.event.x;\n          node.fy = d3.event.y;\n        })\n        .on('end', (node: {fx: any; fy: any}) => {\n          if (!d3.event.active) {\n            simulation.alphaTarget(0);\n          }\n          node.fx = null;\n          node.fy = null;\n        });\n\n      node.call(dragDrop);\n\n      simulation.alpha(1).restart();\n\n      return svg.node();\n    };\n\n    chart(networkData);\n  }); //end of useEffect\n\n  return (\n    <div className=\"Network\">\n      <svg data-testid=\"networkCanvas\" id=\"networkCanvas\"></svg>\n    </div>\n  );\n};\n\nexport default AtomNetworkVisual;\n"
  },
  {
    "path": "src/app/components/AtomNetwork/__tests__/Network.unit.test.js",
    "content": "import React from 'react';\nimport Network from '../AtomNetwork';\nimport {render, cleanup} from '@testing-library/react';\nimport '@testing-library/jest-dom/extend-expect';\nimport '@babel/polyfill';\nimport {filteredCurSnapMock} from '../../../../../mock/snapshot.js';\n\nafterEach(cleanup);\n\nit('renders & matches snapshot', () => {\n  const {asFragment} = render(<Network />);\n  expect(asFragment()).toMatchSnapshot();\n});\n\nit('loads and displays Network component', () => {\n  const {getByTestId} = render(<Network />);\n  expect(getByTestId('networkCanvas')).toBeTruthy();\n});\n\nit('if empty object props is passed into Network', () => {\n  const {asFragment} = render(<Network filteredCurSnap={{}} />);\n  expect(asFragment()).toMatchSnapshot();\n});\n\nit('if mock data object prop is passed into Network', () => {\n  const {asFragment} = render(\n    <Network filteredCurSnap={filteredCurSnapMock} />,\n  );\n  expect(asFragment()).toMatchSnapshot();\n});\n"
  },
  {
    "path": "src/app/components/AtomNetwork/__tests__/__snapshots__/Network.unit.test.js.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`if empty object props is passed into Network 1`] = `\n<DocumentFragment>\n  <div\n    class=\"networkContainer\"\n  >\n    <div\n      class=\"Network\"\n    >\n      <svg\n        data-testid=\"networkCanvas\"\n        id=\"networkCanvas\"\n      >\n        <g\n          transform=\"translate(0, 0), scale(1)\"\n        >\n          <defs>\n            <marker\n              id=\"arrowhead\"\n              markerHeight=\"7\"\n              markerWidth=\"7\"\n              orient=\"auto\"\n              refX=\"13\"\n              refY=\"0\"\n              viewBox=\"-0 -5 10 10\"\n              xoverflow=\"visible\"\n            >\n              <path\n                d=\"M 0,-5 L 10 ,0 L 0,5\"\n                fill=\"#474747\"\n                style=\"stroke: none;\"\n              />\n            </marker>\n          </defs>\n        </g>\n      </svg>\n    </div>\n    <input\n      id=\"networkSearch\"\n      placeholder=\"search for atoms...\"\n      type=\"text\"\n      value=\"\"\n    />\n    <div\n      class=\"AtomNetworkLegend\"\n    >\n      <div\n        class=\"AtomLegend\"\n      />\n      <p>\n        ATOM\n      </p>\n      <div\n        class=\"SelectorLegend\"\n      />\n      <p>\n        SELECTOR\n      </p>\n    </div>\n  </div>\n</DocumentFragment>\n`;\n\nexports[`if mock data object prop is passed into Network 1`] = `\n<DocumentFragment>\n  <div\n    class=\"networkContainer\"\n  >\n    <div\n      class=\"Network\"\n    >\n      <svg\n        data-testid=\"networkCanvas\"\n        id=\"networkCanvas\"\n      >\n        <g\n          transform=\"translate(0, 0), scale(1)\"\n        >\n          <defs>\n            <marker\n              id=\"arrowhead\"\n              markerHeight=\"7\"\n              markerWidth=\"7\"\n              orient=\"auto\"\n              refX=\"13\"\n              refY=\"0\"\n              viewBox=\"-0 -5 10 10\"\n              xoverflow=\"visible\"\n            >\n              <path\n                d=\"M 0,-5 L 10 ,0 L 0,5\"\n                fill=\"#474747\"\n                style=\"stroke: none;\"\n              />\n            </marker>\n          </defs>\n          <line\n            class=\"link\"\n            marker-end=\"url(#arrowhead)\"\n          >\n            <title />\n          </line>\n          <line\n            class=\"link\"\n            marker-end=\"url(#arrowhead)\"\n          >\n            <title />\n          </line>\n          <line\n            class=\"link\"\n            marker-end=\"url(#arrowhead)\"\n          >\n            <title />\n          </line>\n          <path\n            class=\"edgepath\"\n            fill-opacity=\"1.0\"\n            id=\"edgepath0\"\n            stroke-opacity=\"1.0\"\n            style=\"pointer-events: none; fill: #474747; stroke: #474747; stroke-width: 1px;\"\n          />\n          <path\n            class=\"edgepath\"\n            fill-opacity=\"1.0\"\n            id=\"edgepath1\"\n            stroke-opacity=\"1.0\"\n            style=\"pointer-events: none; fill: #474747; stroke: #474747; stroke-width: 1px;\"\n          />\n          <path\n            class=\"edgepath\"\n            fill-opacity=\"1.0\"\n            id=\"edgepath2\"\n            stroke-opacity=\"1.0\"\n            style=\"pointer-events: none; fill: #474747; stroke: #474747; stroke-width: 1px;\"\n          />\n          <text\n            class=\"edgelabel\"\n            font-size=\"10\"\n            id=\"edgelabel0\"\n            style=\"pointer-events: none;\"\n          >\n            <textpath\n              href=\"#edgepath0\"\n              startOffset=\"50%\"\n              style=\"text-anchor: middle; pointer-events: none;\"\n            />\n          </text>\n          <text\n            class=\"edgelabel\"\n            font-size=\"10\"\n            id=\"edgelabel1\"\n            style=\"pointer-events: none;\"\n          >\n            <textpath\n              href=\"#edgepath1\"\n              startOffset=\"50%\"\n              style=\"text-anchor: middle; pointer-events: none;\"\n            />\n          </text>\n          <text\n            class=\"edgelabel\"\n            font-size=\"10\"\n            id=\"edgelabel2\"\n            style=\"pointer-events: none;\"\n          >\n            <textpath\n              href=\"#edgepath2\"\n              startOffset=\"50%\"\n              style=\"text-anchor: middle; pointer-events: none;\"\n            />\n          </text>\n          <g\n            class=\"node\"\n          >\n            <circle\n              r=\"5\"\n              style=\"fill: #9580FF;\"\n            />\n            <title>\n              0\n            </title>\n            <text\n              dy=\"-3\"\n              fill=\"#646464\"\n              stroke=\"none\"\n            >\n              dummyAtom1\n            </text>\n          </g>\n          <g\n            class=\"node\"\n          >\n            <circle\n              r=\"5\"\n              style=\"fill: #9580FF;\"\n            />\n            <title>\n              1\n            </title>\n            <text\n              dy=\"-3\"\n              fill=\"#646464\"\n              stroke=\"none\"\n            >\n              listState\n            </text>\n          </g>\n          <g\n            class=\"node\"\n          >\n            <circle\n              r=\"5\"\n              style=\"fill: #9580FF;\"\n            />\n            <title>\n              2\n            </title>\n            <text\n              dy=\"-3\"\n              fill=\"#646464\"\n              stroke=\"none\"\n            >\n              listState2\n            </text>\n          </g>\n          <g\n            class=\"node\"\n          >\n            <circle\n              r=\"5\"\n              style=\"fill: #FF80BF;\"\n            />\n            <title>\n              3\n            </title>\n            <text\n              dy=\"-3\"\n              fill=\"#646464\"\n              stroke=\"none\"\n            >\n              selectorTest\n            </text>\n          </g>\n          <g\n            class=\"node\"\n          >\n            <circle\n              r=\"5\"\n              style=\"fill: #FF80BF;\"\n            />\n            <title>\n              4\n            </title>\n            <text\n              dy=\"-3\"\n              fill=\"#646464\"\n              stroke=\"none\"\n            >\n              stateLengths\n            </text>\n          </g>\n        </g>\n      </svg>\n    </div>\n    <input\n      id=\"networkSearch\"\n      placeholder=\"search for atoms...\"\n      type=\"text\"\n      value=\"\"\n    />\n    <div\n      class=\"AtomNetworkLegend\"\n    >\n      <div\n        class=\"AtomLegend\"\n      />\n      <p>\n        ATOM\n      </p>\n      <div\n        class=\"SelectorLegend\"\n      />\n      <p>\n        SELECTOR\n      </p>\n    </div>\n  </div>\n</DocumentFragment>\n`;\n\nexports[`renders & matches snapshot 1`] = `\n<DocumentFragment>\n  <div\n    class=\"networkContainer\"\n  >\n    <div\n      class=\"Network\"\n    >\n      <svg\n        data-testid=\"networkCanvas\"\n        id=\"networkCanvas\"\n      >\n        <g\n          transform=\"translate(0, 0), scale(1)\"\n        >\n          <defs>\n            <marker\n              id=\"arrowhead\"\n              markerHeight=\"7\"\n              markerWidth=\"7\"\n              orient=\"auto\"\n              refX=\"13\"\n              refY=\"0\"\n              viewBox=\"-0 -5 10 10\"\n              xoverflow=\"visible\"\n            >\n              <path\n                d=\"M 0,-5 L 10 ,0 L 0,5\"\n                fill=\"#474747\"\n                style=\"stroke: none;\"\n              />\n            </marker>\n          </defs>\n        </g>\n      </svg>\n    </div>\n    <input\n      id=\"networkSearch\"\n      placeholder=\"search for atoms...\"\n      type=\"text\"\n      value=\"\"\n    />\n    <div\n      class=\"AtomNetworkLegend\"\n    >\n      <div\n        class=\"AtomLegend\"\n      />\n      <p>\n        ATOM\n      </p>\n      <div\n        class=\"SelectorLegend\"\n      />\n      <p>\n        SELECTOR\n      </p>\n    </div>\n  </div>\n</DocumentFragment>\n`;\n"
  },
  {
    "path": "src/app/components/ComponentGraph/AtomComponentContainer.tsx",
    "content": "import React, {useState} from 'react';\r\nimport AtomComponentVisual from './AtomComponentVisual';\r\nimport {filteredSnapshot, atom, selector} from '../../../types';\r\n\r\nimport {useAppSelector} from '../../state-management/hooks';\r\n\r\nconst AtomComponentVisualContainer: React.FC = () => {\r\n  //Retrieve State from store\r\n  const snapshotHistory = useAppSelector(\r\n    state => state.snapshot.snapshotHistory,\r\n  );\r\n  const renderIndex = useAppSelector(state => state.snapshot.renderIndex);\r\n  const cleanedComponentAtomTree = useAppSelector(\r\n    state => state.snapshot.cleanComponentAtomTree,\r\n  );\r\n  const filteredCurSnap: filteredSnapshot =\r\n    snapshotHistory[renderIndex].filteredSnapshot;\r\n  const componentAtomTree = snapshotHistory[renderIndex].componentAtomTree;\r\n\r\n  // this will be the atom or selector from the AtomSelectorLegend that the user clicked on.  an array with the ele at index 0 as the name of the atom/selector, and ele at index 1 will be 'atom' or 'selector'\r\n  // Why was selectedRecoilValue formatted as an array? why not an object?\r\n  const [selectedRecoilValue, setSelectedRecoilValue] = useState<string[]>([]);\r\n  const [str, setStr] = useState<string[]>([]);\r\n\r\n  // each property in the atoms or selectors object will be a property whose key is the atom or selector name,\r\n  // and whose value is the value of that atom or selector\r\n  const atoms: atom = {};\r\n  const selectors: selector = {};\r\n  if (filteredCurSnap) {\r\n    for (let [recoilValueName, object] of Object.entries(filteredCurSnap)) {\r\n      if (!object.nodeDeps.length) {\r\n        atoms[recoilValueName] = object.contents;\r\n      } else {\r\n        selectors[recoilValueName] = object.contents;\r\n      }\r\n    }\r\n  }\r\n\r\n  return (\r\n    <div className=\"Component\">\r\n      <AtomComponentVisual\r\n        componentAtomTree={componentAtomTree}\r\n        cleanedComponentAtomTree={cleanedComponentAtomTree}\r\n        selectedRecoilValue={selectedRecoilValue}\r\n        setSelectedRecoilValue={setSelectedRecoilValue}\r\n        atoms={atoms}\r\n        selectors={selectors}\r\n        setStr={setStr}\r\n      />\r\n    </div>\r\n  );\r\n};\r\n\r\nexport default AtomComponentVisualContainer;\r\n"
  },
  {
    "path": "src/app/components/ComponentGraph/AtomComponentVisual.tsx",
    "content": "import React, {useState, useEffect} from 'react';\nimport * as d3 from 'd3';\nimport {componentAtomTree, atom, selector} from '../../../types';\nimport {useAppSelector, useAppDispatch} from '../../state-management/hooks';\nimport {\n  updateZoomState,\n  selectZoomState,\n  setDefaultZoom,\n} from '../../state-management/slices/ZoomSlice';\n\ninterface AtomComponentVisualProps {\n  componentAtomTree: componentAtomTree;\n  cleanedComponentAtomTree: componentAtomTree;\n  selectedRecoilValue: string[];\n  atoms: atom;\n  selectors: selector;\n  setStr: React.Dispatch<React.SetStateAction<string[]>>;\n  setSelectedRecoilValue: React.Dispatch<React.SetStateAction<string[]>>;\n}\n\nconst AtomComponentVisual: React.FC<AtomComponentVisualProps> = ({\n    componentAtomTree,\n    cleanedComponentAtomTree,\n    selectedRecoilValue,\n    atoms,\n    selectors,\n    setStr,\n    setSelectedRecoilValue,\n  }) => {\n\n  const zoomSelector = useAppSelector(selectZoomState);\n  const {x, y, k} = zoomSelector; // initial scaling\n  const dispatch = useAppDispatch();\n\n  // set the heights and width of the tree to be passed into treeMap function\n  let width: number = 0;\n  let height: number = 0;\n\n  // useState hook to update the toggle of displaying entire tree or cleaned tree\n  const [rawToggle, setRawToggle] = useState<boolean>(false);\n\n  // useState hook to update whether a suspense component will be shown on the component graph\n  const [hasSuspense, setHasSuspense] = useState<boolean>(false);\n\n  //declare hooks to render lists of atoms or selectors\n  const [atomList, setAtomList] = useState(Object.keys(atoms)); // returns an array of atom's properties\n  const [selectorList, setSelectorList] = useState(Object.keys(selectors));\n\n  // need to create a hook for toggling\n  const [showAtomMenu, setShowAtomMenu] = useState<boolean>(false);\n  const [showSelectorMenu, setShowSelectorMenu] = useState<boolean>(false);\n\n  // hook for selected button styles on the legend\n  const [atomButtonClicked, setAtomButtonClicked] = useState<boolean>(false);\n  const [selectorButtonClicked, setSelectorButtonClicked] = useState<boolean>(\n    false,\n  );\n  const [bothButtonClicked, setBothButtonClicked] = useState<boolean>(false);\n  const [isDropDownItem, setIsDropDownItem] = useState<boolean>(false);\n\n  // hooks for sibling level node spacing adjustment\n  const [siblingSpacingFactor, setSiblingSpacingFactor] = useState<number>(1);\n  const [siblingSlider, setSiblingSlider] = useState<number>(10);\n\n  // hooks for parent-child node spacing adjustment\n  const [parentSpacingFactor, setParentSpacingFactor] = useState<number>(1);\n  const [parentChildSlider, setParentChildSlider] = useState<number>(10);\n\n  // hook for component graph orientation\n  const [vertOrient, setVertOrient] = useState<boolean>(false);\n\n  useEffect(() => {\n    height = document.querySelector('.Component').clientHeight;\n    width = document.querySelector('.Component').clientWidth;\n\n    document.getElementById('canvas').innerHTML = '';\n\n    // reset hasSuspense to false. This will get updated to true if the red borders are rendered on the component graph.\n    setHasSuspense(false);\n\n    // creating the main svg container for d3 elements\n    const svgContainer = d3.select('#canvas');\n\n    // creating a pseudo-class for reusability\n    const g = svgContainer\n      .append('g')\n      .attr('transform', `translate(${x}, ${y}), scale(${k})`)\n      .attr('id', 'componentGraph');\n\n    let i = 0;\n    let duration: number = 750;\n    let root: any;\n    let path: string;\n\n    // creating the tree map\n    const treeMap = d3.tree().nodeSize(\n      [\n        height * siblingSpacingFactor,\n        width * parentSpacingFactor\n      ]\n    );\n\n    if (!rawToggle) { // expanded tree\n      root = d3.hierarchy(\n        cleanedComponentAtomTree,\n        function (d: componentAtomTree) {\n          return d.children;\n        },\n      );\n    } else { // collapsed tree\n      root = d3.hierarchy(componentAtomTree, function (d: componentAtomTree) {\n        return d.children;\n      });\n    }\n\n    // Node distance from each other\n    root.x0 = 10;\n    root.y0 = width / 2;\n\n    update(root);\n\n    // d3 zoom functionality\n    let zoom = d3.zoom().on('zoom', zoomed);\n\n    // https://github.com/d3/d3-zoom/blob/v3.0.0/README.md#zoom_transform\n    svgContainer.call(\n      zoom.transform,\n      // Changes the initial view, (left, top)\n      d3.zoomIdentity.translate(x, y).scale(k),\n    );\n\n    // allows the canvas to be zoom-able\n    svgContainer.call(\n      d3\n        .zoom()\n        .scaleExtent([0.05, 0.9]) // [max zoomed out view, max zoomed in view]\n        .on('zoom', zoomed),\n    );\n\n    // helper function that allows for zooming\n    function zoomed() {\n      g.attr('transform', d3.event.transform).on(\n        'mouseup',\n        dispatch(\n          updateZoomState(d3.zoomTransform(d3.select('#canvas').node())),\n        ),\n      );\n    }\n\n    // Update function\n    /***\n     *  Function: Update()\n     *  Parameters: source\n     *  Output:\n     *\n     */\n    function update(source: any) {\n\n      treeMap(root);\n      let nodes = root.descendants(),\n        links = root.descendants().slice(1);\n\n      let node = g\n        .selectAll('g.node')\n      .attr('stroke-width', 5)\n      .data(nodes, function (d: any): number {\n        return d.id || (d.id = ++i);\n      });\n\n/* getting group element with id = \"componentGraph\"\n  ex 1: <path class=\"link\" stroke=\"#646464\" stroke-width=\"5\"\n            d=\"M 582 0\n              C 291 0,\n                291 0,\n                0 0\">\n          </path>\n  ex 2: <g class=\"node\" transform=\"translate(0, 0)\" opacity=\"1\">\n            <circle class=\"node\" r=\"100\" fill=\"#9580ff\" cursor=\"pointer\" style=\"stroke: none; stroke-width: 15;\">\n            </circle>\n            <text dy=\".31em\" y=\"138\" text-anchor=\"middle\" style=\"font-size: 7.5rem; fill: white;\">PlaygroundRender\n            </text>\n          </g>\n  https://devdocs.io/d3~5/d3-selection#selectAll */\n\n      /* this tells node where to be placed and go to\n       * adding a mouseOver event handler to each node\n       * display the data in the node on hover\n       * add mouseOut event handler that removes the popup text\n       */\n      //add div that will hold info regarding atoms and/or selectors for each node\n      const tooltip = d3\n        .select('.tooltipContainer')\n        .append('div')\n        .attr('class', 'hoverInfo')\n        .style('opacity', 0);\n\n      let nodeEnter = node\n        .enter()\n        .append('g')\n        .attr('class', 'node')\n        .attr('transform', function (): string {\n          return `translate(${source.y0}, ${source.x0})`;\n        // getting <g class=\"node\"> && setting its \"transform\" attr to \"translate(x, y) --> css manipulation\"\n        })\n        .on('click', click)\n        .on('mouseover', function (d: any, i: number): void {\n          // atsel is an array of all the atoms and selectors\n          const atsel: any = [];\n\n          if (d.data.recoilNodes) {\n            for (let x = 0; x < d.data.recoilNodes.length; x++) {\n              // pushing all the atoms and selectors for the node into 'atsel'\n              atsel.push(d.data.recoilNodes[x]);\n            }\n            // change the opacity of the node when the mouse is over\n            d3.select(this).transition().duration('50').attr('opacity', '.85');\n\n            // created a str for hover div to have corrensponding info\n            // let newStr = formatAtomSelectorText(atsel).join('<br>');\n            // newStr = newStr.replace(/,/g, '<br>');\n            // newStr = newStr.replace(/{/g, '<br>{');\n\n            const nodeData = formatAtomSelectorText(atsel)[0];\n\n            const genHTML = (obj: any): string => {\n              let str = '';\n              let htmlStr = '';\n              for (let key in obj) {\n                const curr = obj[key];\n\n                if (key === 'type') str += `${curr}: `;\n                if (key === 'name') str += curr;\n\n                if (key === 'info') {\n                  htmlStr += `<h3>${str}</h3>`;\n                  htmlStr += `<h5>Atomic Values</h5>`;\n                  if (typeof curr === 'string')\n                    htmlStr += `<p>title: ${curr}</p>`;\n                  else\n                    for (let prop in curr) {\n                      const title = prop;\n                      const data = curr[prop];\n                      htmlStr += `<p>${title}: ${data}</p>`;\n                    }\n                }\n              }\n              return `<div>${htmlStr}</div>`;\n            };\n\n            // tooltip appear near your mouse when hover over a node\n            tooltip\n              .style('opacity', 1)\n              .html(genHTML(nodeData))\n              .style('left', d3.event.pageX + 15 + 'px') // mouse position\n              .style('top', d3.event.pageY - 20 + 'px');\n          }\n        })\n        .on('mouseout', function (d: any, i: number): void {\n          d3.select(this).transition().duration('50').attr('opacity', '1');\n          tooltip.style('opacity', 0);\n        });\n\n      // determines shape/color/size of node // adding styling attributes\n      nodeEnter\n        .append('circle')\n        .attr('class', 'node')\n        .attr('r', determineSize)\n        .attr('fill', colorComponents)\n        .style('stroke', borderColor)\n        .style('stroke-width', 15);\n      // TO DO: Add attribute for border if it is a suspense component // what is a suspense component and what color to add?\n\n      // for each node that got created, append a text element that displays the name of the node\n      nodeEnter\n        .append('text')\n        .attr('dy', '.31em')\n        .attr('y', (d: any): number => (d.data.recoilNodes ? 138 : -75))\n        .attr('text-anchor', function (d: any): string {\n          return d.children || d._children ? 'middle' : 'middle';\n        })\n        .text((d: any): string => d.data.name)\n        .style('font-size', `7.5rem`)\n        .style('fill', 'white');\n\n      let nodeUpdate = nodeEnter.merge(node);\n\n      // transition that makes it slide down to next spot\n      nodeUpdate\n        // .transition()\n        // .duration(duration)\n        .attr('transform', function (d: any): string {\n          return vertOrient ? `translate(${d.x}, ${d.y})` : `translate(${d.y}, ${d.x})`;\n        });\n\n      // allows user to see hand pop out when clicking is available and maintains color/size\n      nodeUpdate\n        .select('circle.node')\n        .attr('r', determineSize)\n        .attr('fill', colorComponents)\n        .attr('cursor', 'pointer')\n        .style('stroke', borderColor)\n        .style('stroke-width', 15);\n\n      let nodeExit = node\n        .exit()\n        .transition()\n        .duration(duration)\n        .attr('transform', function (d: any): string {\n          return `translate(${source.y}, ${source.x})`;\n        })\n        .remove();\n\n      let link = g\n        .attr('fill', 'none')\n        .attr('stroke-width', 5)\n        .selectAll('path.link')\n        .data(links, function (d: any): number {\n          return d.id;\n        });\n\n      let linkEnter = link\n        .enter()\n        .insert('path', 'g')\n        .attr('class', 'link')\n        .attr('stroke', '#646464')\n        .attr('stroke-width', 5)\n        .attr('d', function (d: any): string {\n          let o = {x: source.x0, y: source.y0};\n          return diagonal(o, o);\n        });\n\n      let linkUpdate = linkEnter.merge(link);\n\n      linkUpdate\n        .transition()\n        .duration(duration)\n        .attr('stroke', '#646464')\n        .attr('stroke-width', 5)\n        .attr('d', function (d: any): string {\n          return diagonal(d, d.parent);\n        });\n\n      let linkExit = link\n        .exit()\n        .transition()\n        .duration(duration)\n        .attr('stroke', '#646464')\n        .attr('stroke-width', 5)\n        .attr('d', function (d: any): string {\n          let o = {y: source.y, x: source.x};\n          return diagonal(o, o);\n        })\n        .remove();\n\n      nodes.forEach(function (d: any): void {\n        d.x0 = d.x;\n        d.y0 = d.y;\n      });\n\n      function diagonal(s: any, d: any): string {\n        if (vertOrient) {\n          path = `M ${s.x} ${s.y}\n                C ${s.x} ${(s.y + d.y) / 2},\n                  ${d.x} ${(s.y + d.y) / 2},\n                  ${d.x} ${d.y}`;\n        } else {\n          path = `M ${s.y} ${s.x}\n                C ${(s.y + d.y) / 2} ${s.x},\n                  ${(s.y + d.y) / 2} ${d.x},\n                  ${d.y} ${d.x}`;\n        }\n        return path;\n      }\n\n      function click(d: any): void {\n        if (d.children) {\n          d._children = d.children;\n          d.children = null;\n        } else {\n          d.children = d._children;\n          d._children = null;\n        }\n\n        update(d);\n        const atsel = [];\n        if (d.data.recoilNodes) {\n          for (let x = 0; x < d.data.recoilNodes.length; x++) {\n            atsel.push(d.data.recoilNodes[x]);\n          }\n          setStr(formatAtomSelectorText(atsel));\n        }\n      }\n\n      // allows the canvas to be draggable\n      node.call(d3.drag());\n      // https://github.com/d3/d3-selection/blob/v3.0.0/README.md#selection_call\n\n      function formatMouseoverXValue(recoilValue: string): number {\n        if (atoms.hasOwnProperty(recoilValue)) {\n          return -30;\n        }\n        return -150;\n      }\n\n      function formatAtomSelectorText(atomOrSelector: string[]): string[] {\n        const recoilData: any = [];\n\n        for (let i = 0; i < atomOrSelector.length; i++) {\n          const data: any = {};\n          const curr = atomOrSelector[i];\n\n          data.type = atoms.hasOwnProperty(curr) ? 'atom' : 'selector';\n          data.name = curr;\n\n          if (data.type === 'atom') {\n            data.info = atoms[curr];\n          } else {\n            data.info = selectors[curr];\n          }\n\n          recoilData.push(data);\n        }\n\n        return recoilData;\n      }\n\n      function determineSize(d: any): number {\n        if (d.data.recoilNodes && d.data.recoilNodes.length) {\n          if (d.data.recoilNodes.includes(selectedRecoilValue[0])) {\n            // Size when the atom/selector is clicked on from legend\n            return 150;\n          }\n          // Size of atoms and selectors\n          return 100;\n        }\n        // Size of regular nodes\n        return 50;\n      }\n\n      function borderColor(d: any): string {\n        if (d.data.wasSuspended) setHasSuspense(true);\n        return d.data.wasSuspended ? '#FF0000' : 'none';\n      }\n\n      function colorComponents(d: any): string {\n        // if component node contains recoil atoms or selectors, make it orange red or yellow, otherwise keep node gray\n        if (d.data.recoilNodes && d.data.recoilNodes.length) {\n          if (d.data.recoilNodes.includes(selectedRecoilValue[0])) {\n            // Color of atom or selector when clicked on in legend\n            return 'yellow';\n          }\n\n          let hasAtom = false;\n          let hasSelector = false;\n          for (let i = 0; i < d.data.recoilNodes.length; i++) {\n            if (atoms.hasOwnProperty(d.data.recoilNodes[i])) {\n              hasAtom = true;\n            }\n            if (selectors.hasOwnProperty(d.data.recoilNodes[i])) {\n              hasSelector = true;\n            }\n          }\n          if (hasAtom && hasSelector) {\n            return 'springgreen';\n          }\n          if (hasAtom) {\n            return '#9580ff';\n          } else {\n            return '#ff80bf';\n          }\n        }\n        return 'gray';\n      }\n    }\n  }, [componentAtomTree, rawToggle, siblingSpacingFactor, parentSpacingFactor, vertOrient, selectedRecoilValue]);\n\n  // setting the component's user interface state by checking if the dropdown menu is open or not\n  function openDropdown(e: React.MouseEvent) {\n    const target = e.target as Element;\n    // the button element with id 'AtomP'\n    if (target.id === 'AtomP') {\n      setAtomButtonClicked(true);\n      setSelectorButtonClicked(false);\n      setShowAtomMenu(!showAtomMenu);\n      setShowSelectorMenu(false);\n    } else {\n      setAtomButtonClicked(false);\n      setSelectorButtonClicked(true);\n      setShowSelectorMenu(!showSelectorMenu);\n      setShowAtomMenu(false);\n    }\n  }\n\n  // resetting the component's user interface state when toggling between atoms & selectors\n  const resetNodes = () => {\n    setIsDropDownItem(false);\n    setSelectedRecoilValue([]);\n    setShowSelectorMenu(false);\n    setShowAtomMenu(false);\n    setAtomButtonClicked(false);\n    setSelectorButtonClicked(false);\n  };\n\n  return (\n    <div className=\"AtomComponentVisual\">\n      <svg id=\"canvas\"></svg>\n      <div id='spacingSliders'>\n        <div className='sliderContainer'>\n          <label\n            className='sliderLabel'\n            >{vertOrient ? 'H' : 'V'}\n          </label>\n          <input\n            className='siblingSlider'\n            type='range'\n            min={1}\n            max={20}\n            value={siblingSlider}\n            onChange={(e) => {\n              const val = parseInt(e.target.value);\n              setSiblingSlider(val);\n              setSiblingSpacingFactor(val / 10);\n            }}\n          />\n        </div>\n        <div className='sliderContainer'>\n          <label\n            className='sliderLabel'\n            >{vertOrient ? 'V' : 'H'}\n          </label>\n          <input\n            className='parentChildSlider'\n            type='range'\n            min={1}\n            max={50}\n            value={parentChildSlider}\n            onChange={(e) => {\n              const val = parseInt(e.target.value);\n              setParentChildSlider(val);\n              setParentSpacingFactor(val / 10);\n            }}\n          />\n        </div>\n      </div>\n      <div className='graphBtnContainer'>\n        <button\n          id='fixedButton'\n          className='graphBtn'\n          style={{\n            color: rawToggle ? '#E6E6E6' : '#989898',\n          }}\n          onClick={() => {\n            setRawToggle(!rawToggle);\n            dispatch(setDefaultZoom());\n          }}>\n          <span>\n            {rawToggle ? 'Expand' : 'Collapse'}\n          </span>\n        </button>\n        <button\n          id='orientationBtn'\n          className='graphBtn'\n          style={{\n            color: vertOrient ? '#E6E6E6' : '#989898',\n          }}\n          onClick={() => {\n            setVertOrient(!vertOrient);\n          }}>\n          <span>\n            {vertOrient ? 'Horizontal' : 'Vertical'}\n          </span>\n        </button>\n      </div>\n      <div className=\"AtomNetworkLegend\">\n        <div className=\"AtomLegend\" />\n        <button\n          onClick={isDropDownItem ? resetNodes : openDropdown}\n          id=\"AtomP\"\n          className={\n            atomButtonClicked ? 'AtomP atomSelected' : 'AtomP atomLegendDefault'\n          }>\n          ATOM\n        </button>\n        {showAtomMenu && (\n          <div id=\"atomDrop\" className=\"AtomDropDown\">\n            {atomList.map((atom, i) => (\n              <div className=\"dropDownButtonDiv\">\n                <button\n                  id={`atom-drop${i}`}\n                  className=\"atom-class atomDropDown\"\n                  key={i}\n                  onClick={event => {\n                    if (\n                      !(event.target as HTMLInputElement).classList.contains(\n                        'atomSelected',\n                      ) &&\n                      (event.target as HTMLInputElement).classList.contains(\n                        'atomNotSelected',\n                      )\n                    ) {\n                      (event.target as HTMLInputElement).classList.replace(\n                        'atomNotSelected',\n                        'atomSelected',\n                      );\n                    } else if (\n                      !(event.target as HTMLInputElement).classList.contains(\n                        'atomSelected',\n                      ) &&\n                      !(event.target as HTMLInputElement).classList.contains(\n                        'atomNotSelected',\n                      )\n                    ) {\n                      (event.target as HTMLInputElement).classList.add(\n                        'atomSelected',\n                      );\n                    }\n\n                    document.querySelectorAll('.atom-class').forEach(item => {\n                      if (\n                        item.id !== `atom-drop${i}` &&\n                        item.classList.contains('atomSelected')\n                      ) {\n                        item.classList.replace(\n                          'atomSelected',\n                          'atomNotSelected',\n                        );\n                      } else if (\n                        item.id !== `atom-drop${i}` &&\n                        !item.classList.contains('atomNotSelected')\n                      ) {\n                        item.classList.add('atomNotSelected');\n                      }\n                    });\n\n                    setSelectedRecoilValue([atom, 'atom']);\n                    setIsDropDownItem(true);\n                  }}>\n                  {atom}\n                </button>\n              </div>\n            ))}\n          </div>\n        )}\n        <div className=\"SelectorLegend\"></div>\n        <button\n          onClick={isDropDownItem ? resetNodes : openDropdown}\n          id=\"SelectorP\"\n          className={\n            selectorButtonClicked\n              ? 'SelectorP selectorSelected'\n              : 'SelectorP selectorLegendDefault'\n          }>\n          SELECTOR\n        </button>\n        {showSelectorMenu && (\n          <div id=\"selectorDrop\" className=\"SelectorDropDown\">\n            {selectorList.map((selector, i) => (\n              <div className=\"dropDownButtonDiv\">\n                <button\n                  id={`selector-drop${i}`}\n                  className=\"selector-class selectorDropDown\"\n                  key={i}\n                  onClick={event => {\n                    if (\n                      !(event.target as HTMLInputElement).classList.contains(\n                        'selectorSelected',\n                      ) &&\n                      (event.target as HTMLInputElement).classList.contains(\n                        'selectorNotSelected',\n                      )\n                    ) {\n                      (event.target as HTMLInputElement).classList.replace(\n                        'selectorNotSelected',\n                        'selectorSelected',\n                      );\n                    } else if (\n                      !(event.target as HTMLInputElement).classList.contains(\n                        'selectorSelected',\n                      ) &&\n                      !(event.target as HTMLInputElement).classList.contains(\n                        'selectorNotSelected',\n                      )\n                    ) {\n                      (event.target as HTMLInputElement).classList.add(\n                        'selectorSelected',\n                      );\n                    }\n\n                    document\n                      .querySelectorAll('.selector-class')\n                      .forEach(item => {\n                        if (\n                          item.id !== `selector-drop${i}` &&\n                          item.classList.contains('selectorSelected')\n                        ) {\n                          item.classList.replace(\n                            'selectorSelected',\n                            'selectorNotSelected',\n                          );\n                        } else if (\n                          item.id !== `selector-drop${i}` &&\n                          !item.classList.contains('selectorNotSelected')\n                        ) {\n                          item.classList.add('selectorNotSelected');\n                        }\n                      });\n                    setSelectedRecoilValue([selector, 'selector']);\n                    setIsDropDownItem(true);\n                  }}>\n                  {selector}\n                </button>\n              </div>\n            ))}\n          </div>\n        )}\n        <div className=\"bothLegend\"></div>\n        <button className=\"bothLegendDefault\">BOTH</button>\n        <div className={hasSuspense ? 'suspenseLegend' : ''}></div>\n        <p>{hasSuspense ? 'SUSPENSE' : ''}</p>\n        <div className=\"tooltipContainer\"></div>\n      </div>\n    </div>\n  );\n};\n\nexport default AtomComponentVisual;\n"
  },
  {
    "path": "src/app/components/ComponentGraph/__tests__/AtomComponentContainer.unit.test.js",
    "content": "import React from 'react';\nimport AtomComponentVisualContainer from '../AtomComponentContainer';\nimport {cleanup, render} from '@testing-library/react';\n\nafterEach(cleanup);\n\nxit('testing to see if the component is properly rendered', () => {\n  // This test gets 'TypeError: Cannot read property 'baseVal' of undefined\n  // because Jest doesn't fully support testing SVGs yet\n  const atomTree = {children: []};\n  const {component, debug} = render(\n    <AtomComponentVisualContainer componentAtomTree={atomTree} />,\n  );\n  debug();\n});\n"
  },
  {
    "path": "src/app/components/ComponentGraph/__tests__/AtomComponentVisual.unit.test.js",
    "content": "import React from 'react';\nimport AtomComponentVisualContainer from '../AtomComponentContainer';\nimport AtomComponentVisual from '../AtomComponentVisual';\nimport {render, cleanup} from '@testing-library/react';\nimport '@testing-library/jest-dom/extend-expect';\nimport '@babel/polyfill';\nimport {\n  componentAtomTreeMock,\n  filteredCurSnapMock,\n} from '../../../../../mock/snapshot';\n\nafterEach(cleanup);\n\nxit('testing to see if the component is properly rendered', () => {\n  // Now that we have the componentClassDiv appended, we get the same svg error as AtomComponentContainer test\n  const componentClassDiv = document.createElement('div');\n  componentClassDiv.className = 'Component';\n  document.body.appendChild(componentClassDiv);\n  // This test fails because it cannot find the element with class 'Component'\n  const atomTree = {children: []};\n  const {component, debug} = render(\n    // possibly test 'setStr', 'selectedRecoilValue', 'componentAtomTree' props\n    <AtomComponentVisual\n      selectors={[]}\n      atoms={[]}\n      componentAtomTree={atomTree}\n    />,\n    componentClassDiv,\n  );\n  debug();\n});\n\nxit('renders & matches snapshot - no props', () => {\n  const {asFragment} = render(<AtomComponentVisualContainer />);\n  expect(asFragment()).toMatchSnapshot();\n});\n\nxit('renders & matches snapshot - componetAtomTree props', () => {\n  const {asFragment} = render(\n    <AtomComponentVisualContainer\n      filteredSnapshot={filteredCurSnapMock}\n      componentAtomTree={componentAtomTreeMock}\n    />,\n  );\n  expect(asFragment()).toMatchSnapshot();\n});\n"
  },
  {
    "path": "src/app/components/ComponentGraph/__tests__/AtomSelectorLegend.unit.test.js",
    "content": "import React from 'react';\nimport AtomSelectorLegend from '../AtomSelectorLegend';\nimport {cleanup, render} from '@testing-library/react';\nafterEach(cleanup);\n\nit('testing to see if the component is properly rendered', () => {\n  const {component, debug} = render(\n    // possibly test 'selectedRecoilValue', 'setSelectedRecoilValue', 'setStr' props\n    <AtomSelectorLegend selectors={[]} atoms={[]} str={[]} />,\n  );\n});\n"
  },
  {
    "path": "src/app/components/Metrics/ComparisonGraph.tsx",
    "content": "import React, {useEffect, useRef} from 'react';\nimport * as d3 from 'd3';\nimport {dataDurationArr} from '../../../types';\nimport {useAppSelector} from '../../state-management/hooks';\n\ninterface ComparisonGraphProps {\n  data: dataDurationArr; // an array of object{name:, actualDuration}\n  width?: number;\n  height?: number;\n}\n\nconst ComparisonGraph: React.FC<ComparisonGraphProps> = ({\n  data,\n  width,\n  height,\n}: ComparisonGraphProps) => {\n  const snapshotHistory = useAppSelector(\n    (state: {snapshot: {snapshotHistory: any}}) =>\n      state.snapshot.snapshotHistory,\n  );\n  console.log('comparison snapshot ', snapshotHistory);\n  // declare an array that holds 2 objects: past and current\n  const displayData = [\n    {name: 'past', duration: 0},\n    {name: 'current', duration: 0},\n  ];\n\n  // retrieve and get total duration for past serie from the local storage\n  const values: any[] = [];\n  const keys = Object.keys(localStorage);\n  let i = keys.length;\n  while (i--) {\n    const series = localStorage.getItem(keys[i]);\n    values.push(JSON.parse(series));\n  }\n  for (const element of values) {\n    displayData[0].duration += element.componentAtomTree.treeBaseDuration;\n  }\n\n  let total = 0;\n  for (const element of snapshotHistory) {\n    total += element.componentAtomTree.treeBaseDuration;\n  }\n  displayData[1].duration = total;\n  // delete series in local storage\n  const deleteSeries = () => {\n    for (const i of keys) {\n      localStorage.removeItem(i);\n    }\n  };\n\n  // svg\n  const svgRef = useRef();\n  useEffect(() => {\n    document.getElementById('canvas').innerHTML = '';\n    // set the dimensions and margins of the graph\n    let left = 80;\n    if (length > 13) {\n      left = 100;\n    }\n    if (length > 17) {\n      left = 120;\n    }\n\n    const margin = {top: 20, right: 20, bottom: 30, left};\n    // set range for y scale\n    const y = d3.scaleBand().range([height, 0]).padding(0.2);\n    // set range for x scale\n    const x = d3.scaleLinear().range([0, width * 0.8]);\n    // set range for durations\n    const z = d3.scaleBand().range([height, 0]).padding(0.2);\n    // determines the color based on actualDuration\n    function colorPicker(data: any) {\n      if (data <= 1) return '#51a8f0';\n      else if (data <= 2) return '#3a7bb0';\n      else return '#2d608a';\n    }\n    // append the svg object to the body of the page\n    // append a 'group' element to 'svg'\n    // moves the 'group' element to the top left margin\n    const svg = d3\n      .select(svgRef.current)\n      .classed('svg-container', true)\n      .append('svg')\n      .attr('class', 'chart')\n      .attr('viewBox', '-100 0 900 600')\n      .attr('preserveAspectRatio', 'xMinYMin meet')\n      .classed('svg-content-responsive', true)\n      .call(\n        d3.zoom().on('zoom', function () {\n          svg.attr('transform', d3.event.transform);\n        }),\n      )\n      .append('g')\n      .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');\n    // Scale the range of the data in the domain\n    x.domain([\n      0,\n      d3.max(displayData, (d: any) => {\n        return d.duration;\n      }),\n    ]);\n    // Scale the range of the data across the y-axis\n    y.domain(\n      displayData.map((d: any, i) => {\n        return d.name + '-' + i;\n      }),\n    );\n    // Scale actualDuration with the y-axis\n    z.domain(\n      displayData.map((d: any, i) => {\n        return d.duration.toFixed(2) + 'ms' + '-' + i;\n      }),\n    );\n    // append the rectangles for the bar chart\n    svg\n      .selectAll('.bar')\n      .data(displayData)\n      .enter()\n      .append('rect')\n      .attr('class', 'bar')\n      .on('mouseover', function () {\n        d3.select(this).attr('opacity', '0.85');\n        const backgroundConnection = chrome.runtime.connect();\n        const barName = this.data;\n        const payload = {\n          action: 'mouseover',\n          tabId: chrome.devtools.inspectedWindow.tabId,\n          payload: barName,\n        };\n        backgroundConnection.postMessage(payload);\n      })\n      // .transition()\n      // .duration(1300)\n      // .delay((d: any,i: any) => i * 100)\n      .attr('width', function (d: any) {\n        return x(d.duration);\n      })\n      .attr('fill', function (d: any) {\n        return colorPicker(d.duration);\n      })\n      .attr('y', function (d: any, i: any) {\n        return y(d.name + '-' + i);\n      })\n      .attr('height', y.bandwidth());\n    // add x axis\n    svg\n      .append('g')\n      .attr('transform', 'translate(0,' + height + ')')\n      .call(d3.axisBottom(x));\n    // add y axis to able to have duplicate strings\n    const yAxis = d3.axisLeft(y).tickFormat(function (d: any) {\n      return d.split('-')[0];\n    });\n    yAxis(svg.append('g'));\n    // add z axis to display all duration times\n    const zAxis = d3.axisRight(z).tickFormat(function (d: any) {\n      return d.split('-')[0];\n    });\n    zAxis(svg.append('g'));\n    // svg.append('g')\n    //   .call(d3.axisRight(z));\n  }, [data]);\n\n  return (\n    <div data-testid=\"canvas\" id=\"stateGraphContainer\">\n      <button\n        id=\"docs_button\"\n        onClick={() => {\n          deleteSeries();\n        }}>\n        Delete Series\n      </button>\n      <svg id=\"canvas\" ref={svgRef}></svg>\n    </div>\n  );\n};\n\nexport default ComparisonGraph;\n"
  },
  {
    "path": "src/app/components/Metrics/FlameGraph.js",
    "content": "import React, {useState, useRef} from 'react';\nimport {scaleLinear} from 'd3-scale';\nimport {interpolate} from 'd3-interpolate';\nimport {format as d3format} from 'd3-format';\nimport {hierarchy} from 'd3-hierarchy';\nimport {Group} from '@vx/group';\nimport {Partition} from '@vx/hierarchy';\nimport {useSpring, animated} from 'react-spring';\n\n//determining the number of decimal places displayed\nconst format = d3format('.2f');\n\nconst FlameGraph = ({cleanedComponentAtomTree, width, height}) => {\n  //Building a heirarchy for d3 to graph\n  const root = hierarchy(cleanedComponentAtomTree)\n    //determining tree based duration by summing actual duration of children\n    .sum(d => {\n      // targets cleanedComponentAtomTree.actualDuration\n      return d.actualDuration;\n    })\n    //sorting children by their tree based duration for graph\n    .sort((a, b) => b.value - a.value);\n  // this is where we get value for App in this object\n  //traversing tree to determine number of nodes\n  let totalNodes = 0;\n  root.each(() => {\n    totalNodes = totalNodes + 1;\n    return;\n  });\n\n  //calculating average actualDuration of nodes in entire tree\n  const averageDiration = root.value / totalNodes;\n\n  //setting margins to fit graphed componets together and fit to container\n  const margin = {top: 20, left: 0, right: 20, bottom: 30};\n\n  //scaleLinear outputs a funciton\n  //this function is used to determine a graph components color based on actualDuration\n  const color = scaleLinear()\n    .domain([\n      averageDiration / 2,\n      averageDiration * 3,\n      averageDiration * 6,\n      averageDiration * 8,\n    ])\n    .range(['#ffffff', '#e9c7ff', '#ee9f30', '#ff0000']);\n\n  //initiate graphArea as state variable area, and create setArea funciton\n  const [area, setArea] = useState({\n    xDomain: [0, width],\n    xRange: [0, width],\n    yDomain: [0, height],\n    yRange: [0, height],\n  });\n\n  //define horizontal scaling of graph\n  const xScale = useRef(scaleLinear().domain(area.xDomain).range(area.xRange));\n  //define vertical scaling of graph\n  const yScale = useRef(scaleLinear().domain(area.yDomain).range(area.yRange));\n\n  //set interpolates to allow individual graph components to resize when entire graph resizes\n  const xd = interpolate(xScale.current.domain(), area.xDomain);\n  const yd = interpolate(yScale.current.domain(), area.yDomain);\n  const yr = interpolate(yScale.current.range(), area.yRange);\n\n  //set parameters for zooming animations\n  const {t} = useSpring({\n    native: true,\n    reset: true,\n    from: {t: 0},\n    to: {t: 1},\n    config: {\n      mass: 5,\n      tension: 500,\n      friction: 100,\n      precision: 0.00001,\n    },\n    onFrame: Param => {\n      xScale.current.domain(xd(Param.t));\n      yScale.current.domain(yd(Param.t)).range(yr(Param.t));\n    },\n  });\n\n  //return an svg to render the FlameGraph\n  return (\n    <svg width={width} height={height}>\n      <Partition\n        top={margin.top}\n        left={margin.left}\n        right={margin.right}\n        bottom={margin.bottom}\n        root={root}\n        size={[height, width]}\n        padding={1}\n        round={true}>\n        {data => (\n          <Group>\n            {data.descendants().map((node, i) => (\n              <animated.g\n                transform={t.interpolate(\n                  () =>\n                    `translate(${xScale.current(node.y0)}, ${yScale.current(\n                      node.x0,\n                    )})`,\n                )}\n                key={`node-${i}`}\n                onClick={() => {\n                  if (\n                    node.y0 === area.xDomain[0] &&\n                    node.x0 === area.yDomain[0] &&\n                    node.parent\n                  ) {\n                    // If the clicked graph component is already the selected componet, select parent\n                    setArea({\n                      ...area,\n                      xDomain: [node.parent.y0, width],\n                      yDomain: [node.parent.x0, node.parent.x1],\n                      yRange: [0, height],\n                    });\n                    // Otherwise select clicked\n                  } else {\n                    setArea({\n                      ...area,\n                      xDomain: [node.y0, width],\n                      yDomain: [node.x0, node.x1],\n                      yRange: [0, height],\n                    });\n                  }\n                }}>\n                <animated.rect\n                  id={`rect-${i}`}\n                  width={t.interpolate(\n                    () => xScale.current(node.y1) - xScale.current(node.y0),\n                  )}\n                  height={t.interpolate(\n                    () => yScale.current(node.x1) - yScale.current(node.x0),\n                  )}\n                  fill={color(node.data.actualDuration)}\n                  fillOpacity={1}\n                />\n\n                <clipPath id={`clip-${i}`}>\n                  <use xlinkHref={`#rect-${i}`} />\n                </clipPath>\n\n                <text\n                  x={4}\n                  y={13}\n                  clipPath={`url(#clip-${i})`}\n                  style={{\n                    font: '10px sans-serif',\n                    fontWeight: 'bold',\n                  }}>\n                  {node.data.name}\n                  <tspan\n                    style={{\n                      fontSize: 9,\n                      fillOpacity: 0.8,\n                    }}>\n                    {' '}\n                    {format(node.data.actualDuration)}\n                  </tspan>\n                </text>\n              </animated.g>\n            ))}\n          </Group>\n        )}\n      </Partition>\n    </svg>\n  );\n};\n\nexport default FlameGraph;\n"
  },
  {
    "path": "src/app/components/Metrics/MetricsContainer.tsx",
    "content": "import React, {useState} from 'react';\nimport {ParentSize} from '@vx/responsive';\nimport {dataDurationArr} from '../../../types';\nimport FlameGraph from './FlameGraph.js';\nimport RankedGraph from './RankedGraph';\nimport ComparisonGraph from './ComparisonGraph';\nimport {useAppSelector} from '../../state-management/hooks';\n\nconst Metrics: React.FC = () => {\n  const cleanedComponentAtomTree = useAppSelector(\n    state => state.snapshot.cleanComponentAtomTree,\n  );\n  //create state for the graph type toggle\n  const [graphType, setGraphType] = useState<string>('flame');\n\n  //funciton that toggles the graphType state\n  const toggleGraphFunc = (check: string): void => {\n    if (check === 'flame') setGraphType('flame');\n    if (check === 'ranked') setGraphType('ranked');\n    if (check === 'comparison') setGraphType('comparison');\n  };\n\n  // create an empty array to store objects for property name and actualDuration for rankedGraph\n  const dataDurationArr: dataDurationArr = [];\n  let length = 0;\n  // function to traverse through the fiber tree\n  const namesAndDurations = (node: any) => {\n    if (node === undefined) return;\n    if (node.name && node.actualDuration) {\n      const obj: any = {};\n      if (node.name.length > length) {\n        length = node.name.length;\n      }\n      obj['name'] = node.name;\n      obj['actualDuration'] = node.actualDuration;\n      dataDurationArr.push(obj);\n    }\n    node.children.forEach((child: any) => namesAndDurations(child));\n  };\n  namesAndDurations(cleanedComponentAtomTree);\n\n  //function that renders either graphComponent based on graphType state variable\n  const determineRender: any = () => {\n    if (graphType === 'flame') {\n      return (\n        //ParentSize component allows us to scale the FlameGraph to fit its container.\n        <ParentSize>\n          {size =>\n            size.ref && (\n              <FlameGraph\n                cleanedComponentAtomTree={cleanedComponentAtomTree}\n                width={size.width}\n                height={600}\n              />\n            )\n          }\n        </ParentSize>\n      );\n    } else if (graphType === 'ranked') {\n      return (\n        <ParentSize>\n          {size =>\n            size.ref && (\n              <RankedGraph\n                data={dataDurationArr}\n                width={size.width}\n                height={size.height}\n              />\n            )\n          }\n        </ParentSize>\n      );\n    } else if (graphType === 'comparison') {\n      return (\n        <ParentSize>\n          {size =>\n            size.ref && (\n              <ComparisonGraph\n                data={dataDurationArr}\n                width={size.width}\n                height={size.height}\n              />\n            )\n          }\n        </ParentSize>\n      );\n    }\n  };\n\n  //render the toggle buttons and the appropriate graph based on GraphType state variable\n  return (\n    <div id=\"metricsWrapper\">\n      <div data-testid=\"canvas\" className=\"graphContainer\">\n        <button\n          className=\"graphButton\"\n          autoFocus={true}\n          onClick={() => {\n            toggleGraphFunc('flame');\n          }}>\n          Flame Graph\n        </button>\n        <button\n          className=\"graphButton\"\n          onClick={() => {\n            toggleGraphFunc('ranked');\n          }}>\n          Ranked Graph\n        </button>\n        <button\n          className=\"graphButton\"\n          onClick={() => {\n            toggleGraphFunc('comparison');\n          }}>\n          Comparison Graph\n        </button>\n      </div>\n      {determineRender()}\n    </div>\n  );\n};\n\nexport default Metrics;\n"
  },
  {
    "path": "src/app/components/Metrics/RankedGraph.tsx",
    "content": "import React, {useEffect, useRef} from 'react';\nimport * as d3 from 'd3';\nimport {dataDurationArr} from '../../../types';\n\ninterface RankedGraphProps {\n  data: dataDurationArr; // an array of object{name:, actualDuration}\n  width?: number;\n  height?: number;\n}\n\nconst RankedGraph: React.FC<RankedGraphProps> = ({\n  data,\n  width,\n  height,\n}: RankedGraphProps) => {\n  const svgRef = useRef();\n  useEffect(() => {\n    document.getElementById('canvas').innerHTML = '';\n    // set the dimensions and margins of the graph\n    let left = 80;\n    if (length > 13) {\n      left = 100;\n    }\n    if (length > 17) {\n      left = 120;\n    }\n\n    const margin = {top: 20, right: 20, bottom: 30, left};\n    // set range for y scale\n    const y = d3.scaleBand().range([height, 0]).padding(0.2);\n    // set range for x scale\n    const x = d3.scaleLinear().range([0, width * 0.8]);\n    // set range for durations\n    const z = d3.scaleBand().range([height, 0]).padding(0.2);\n    // determines the color based on actualDuration\n    function colorPicker(data: any) {\n      if (data <= 1) return '#51a8f0';\n      else if (data <= 2) return '#3a7bb0';\n      else return '#2d608a';\n    }\n    // append the svg object to the body of the page\n    // append a 'group' element to 'svg'\n    // moves the 'group' element to the top left margin\n    const svg = d3\n      .select(svgRef.current)\n      .classed('svg-container', true)\n      .append('svg')\n      .attr('class', 'chart')\n      .attr('viewBox', '-100 0 900 600')\n      .attr('preserveAspectRatio', 'xMinYMin meet')\n      .classed('svg-content-responsive', true)\n      .call(\n        d3.zoom().on('zoom', function () {\n          svg.attr('transform', d3.event.transform);\n        }),\n      )\n      .append('g')\n      .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');\n    // Scale the range of the data in the domain\n    x.domain([\n      0,\n      d3.max(data, (d: any) => {\n        return d.actualDuration;\n      }),\n    ]);\n    // Scale the range of the data across the y-axis\n    y.domain(\n      data.map((d: any, i) => {\n        return d.name + '-' + i;\n      }),\n    );\n    // Scale actualDuration with the y-axis\n    z.domain(\n      data.map((d: any, i) => {\n        return d.actualDuration.toFixed(2) + 'ms' + '-' + i;\n      }),\n    );\n    // append the rectangles for the bar chart\n    svg\n      .selectAll('.bar')\n      .data(data)\n      .enter()\n      .append('rect')\n      .attr('class', 'bar')\n      .on('mouseover', function () {\n        d3.select(this).attr('opacity', '0.85');\n        const backgroundConnection = chrome.runtime.connect();\n        const barName = this.data;\n        const payload = {\n          action: 'mouseover',\n          tabId: chrome.devtools.inspectedWindow.tabId,\n          payload: barName,\n        };\n        backgroundConnection.postMessage(payload);\n      })\n      // .transition()\n      // .duration(1300)\n      // .delay((d: any,i: any) => i * 100)\n      .attr('width', function (d: any) {\n        return x(d.actualDuration);\n      })\n      .attr('fill', function (d: any) {\n        return colorPicker(d.actualDuration);\n      })\n      .attr('y', function (d: any, i: any) {\n        return y(d.name + '-' + i);\n      })\n      .attr('height', y.bandwidth());\n    // add x axis\n    svg\n      .append('g')\n      .attr('transform', 'translate(0,' + height + ')')\n      .call(d3.axisBottom(x));\n    // add y axis to able to have duplicate strings\n    const yAxis = d3.axisLeft(y).tickFormat(function (d: any) {\n      return d.split('-')[0];\n    });\n    yAxis(svg.append('g'));\n    // add z axis to display all duration times\n    const zAxis = d3.axisRight(z).tickFormat(function (d: any) {\n      return d.split('-')[0];\n    });\n    zAxis(svg.append('g'));\n    // svg.append('g')\n    //   .call(d3.axisRight(z));\n  }, [data]);\n\n  return (\n    <div data-testid=\"canvas\" id=\"stateGraphContainer\">\n      <svg id=\"canvas\" ref={svgRef}></svg>\n    </div>\n  );\n};\n\nexport default RankedGraph;\n"
  },
  {
    "path": "src/app/components/Metrics/__tests__/IcicleVerticle.unit.test.js",
    "content": "\r\n"
  },
  {
    "path": "src/app/components/Metrics/__tests__/Metrics.unit.test.js",
    "content": "import React from 'react';\nimport { render, cleanup, findByTestId } from '@testing-library/react';\nimport { componentAtomTree } from '../../../../types';\nimport { shallow } from 'enzyme';\nimport Metrics from '../Metrics';\n// import prop object being passed down to metric graphs to be tested\ndescribe('Metrics graph testing', () => {\n    afterEach(cleanup);\n    //create a shallow copy of Metrics component passing in expected prop\n    const wrapper = shallow(<Metrics includedProp={cleanedComponentAtomTree}/>);\n    describe('shape of props being passed down should match cleanedComponentAtomTree shape', () => {\n        expect(wrapper.props().includedProp).toMatchObject(componentAtomTree);\n    });\n    describe('component is being rendered correctly', () => {\n        const { getByTestId } = render(<Metrics />);\n        //if the component rendered then it has to have return an element with id 'canvas'\n        //getBy returns an error when not finding an element, here is is looking for an id of canvas\n        expect(getByTestId('canvas')).toBeTruthy();\n    });\n});\n"
  },
  {
    "path": "src/app/components/Metrics/__tests__/Visualizer.unit.test.js",
    "content": "import React from 'react';\nimport RankedGraph from '../RankedGraph';\nimport {\n  filteredCurSnapMock,\n  componentAtomTreeMock,\n} from '../../../../../mock/snapshot.js';\nimport {render, cleanup} from '@testing-library/react';\n\nafterEach(cleanup);\n\ndescribe('Ranked graph displays correct information', () => {\n  it('should display correct atom tree', () => {\n    const {asFragment} = render(\n      <RankedGraph cleanedComponentAtomTree={componentAtomTreeMock} />,\n    );\n    expect(asFragment()).toMatchSnapshot();\n  });\n  //a component should display the render time of a component\n  xit('render time should be of type number', () => {\n    //the type of data being rendered should be a number\n    expect().toBe();\n  });\n  xit('should be of type string', () => {\n    //that dom element should render a string\n    expect().toBe();\n  });\n  //a component should display the name of the component\n  xit('should display correct name', () => {\n    //that dom element should display the correct name\n    expect().toBe();\n  });\n});\n\ndescribe('components rendering correctly', () => {\n  it('renders without crashing', () => {\n    const {getByTestId} = render(<RankedGraph />);\n    expect(getByTestId('canvas')).toBeTruthy();\n  });\n\n  it('should match snapshot when props are passed into RankedGraph', () => {\n    const {asFragment} = render(\n      <RankedGraph filteredCurSnap={filteredCurSnapMock} />,\n    );\n    expect(asFragment()).toMatchSnapshot();\n  });\n\n  it('should match snapshot when no props are passed in', () => {\n    const {asFragment} = render(<RankedGraph />);\n    expect(asFragment()).toMatchSnapshot();\n  });\n\n  xit('should render Recoil Root as text', () => {\n    const {getByTestId} = render(<RankedGraph />);\n    expect(getByTestId('canvas')).toHaveTextContent('Recoil Root');\n  });\n});\n"
  },
  {
    "path": "src/app/components/NavBar/NavBar.tsx",
    "content": "import React from 'react';\n\ninterface NavBarProps {\n  // setState functionality to update tab\n  setTab: React.Dispatch<React.SetStateAction<string>>;\n  // array of object keys of conditional render object\n  tabsList: string[];\n  // string of the current tab selected/rendered/displayed\n  tab: string;\n}\n\nconst NavBar: React.FC<NavBarProps> = ({setTab, tabsList, tab}) => {\n  // array of buttons with setTab functionality\n  const renderedTabButtons = tabsList.reduce<JSX.Element[]>((acc, tabName) => {\n    acc.push(\n      <button\n        className=\"navBarButtons\"\n        key={tabName}\n        style={\n          tab === tabName\n            ? {color: '#E6E6E6', backgroundColor: '#212121'}\n            : {color: '#989898'}\n        }\n        onClick={() => {\n          setTab(tabName);\n        }}>\n        {tabName}\n      </button>,\n    );\n    return acc;\n  }, []);\n  // render the array of NavBar buttons generated above\n  return <div className=\"NavBar\">{renderedTabButtons}</div>;\n};\n\nexport default NavBar;\n"
  },
  {
    "path": "src/app/components/NavBar/__test__/Navbar.unit.test.js",
    "content": "import React, {useState} from 'react';\nimport ReactDOM from 'react-dom';\nimport {getQueriesForElement, getByText} from '@testing-library/dom';\n\nimport {render, fireEvent} from '@testing-library/react';\n\nimport Navbar from '../Navbar';\n\nit('renders to the dom', () => {\n  const {debug} = render(<Navbar tabsList={[]} />);\n});\n\nit('renders to the dom with the tab name', () => {\n  const {getByText} = render(\n    <Navbar tabsList={['testtab1', 'testtab2', 'testtab3']} />,\n  );\n\n  getByText('testtab1');\n});\n"
  },
  {
    "path": "src/app/components/Settings/AtomSettings.tsx",
    "content": "import React, {useEffect} from 'react';\nconst {Multiselect} = require('multiselect-react-dropdown');\nimport {selectedTypes} from '../../../types';\nimport {useAppSelector, useAppDispatch} from '../../state-management/hooks';\nimport {setSelected} from '../../state-management/slices/SelectedSlice';\n\nconst AtomSettings: React.FC = () => {\n  const dispatch = useAppDispatch();\n\n  //Retrieve snapshotHistory State from Redux Store\n  const snapshotHistory = useAppSelector(\n    state => state.snapshot.snapshotHistory,\n  );\n  const renderIndex = useAppSelector(state => state.snapshot.renderIndex);\n  const selected = useAppSelector(state => state.selected.selectedData);\n\n  // https://github.com/srigar/multiselect-react-dropdown\n  // Make filterArray into array of objects, we want to get the most recent so that we have all possible options\n  const options: selectedTypes[] = [];\n  // filling the options with the most recent\n  for (let key in snapshotHistory[renderIndex].filteredSnapshot) {\n    const obj: selectedTypes = {name: key};\n    options.push(obj);\n  }\n\n  // ! setting up the selected options\n  const selected2: selectedTypes[] = [];\n  for (let i = 0; i < selected.length; i++) {\n    selected2.push({name: selected[i].name});\n  }\n\n  // Todo: Create a conditional that will update the selected options onchange of the array -- updates if they are not equal, will add in NEW ADDITIONS\n  // onSelect & onRemove functions for when selecting & removing atoms/selectors from the filter\n  const onSelect = (selectedList: selectedTypes[]): void => {\n    dispatch(setSelected(selectedList)); // propdrilled, so edited up top\n  };\n\n  const onRemove = (selectedList: selectedTypes[]): void => {\n    dispatch(setSelected(selectedList));\n  };\n  return (\n    <div>\n      <h2>Atom and Selector Filter</h2>\n      <Multiselect\n        options={options}\n        selectedValues={selected2}\n        onSelect={onSelect}\n        onRemove={onRemove}\n        displayValue=\"name\"\n      />\n    </div>\n  );\n};\nexport default AtomSettings;\n"
  },
  {
    "path": "src/app/components/Settings/SettingsContainer.tsx",
    "content": "import React from 'react';\n\n// Importing various settings components\nimport ThrottleSettings from './ThrottleSettings';\nimport AtomSettings from './AtomSettings';\n\n// renders the difference between the most recent state change and the previous\nconst Settings: React.FC = () => {\n  return (\n    <div className=\"Settings\">\n      <AtomSettings />\n      <ThrottleSettings />\n    </div>\n  );\n};\n\nexport default Settings;\n"
  },
  {
    "path": "src/app/components/Settings/ThrottleSettings.tsx",
    "content": "import React, {useState, useEffect} from 'react';\nimport {useAppSelector, useAppDispatch} from '../../state-management/hooks';\nimport {\n  newThrottle,\n  resetThrottle,\n} from '../../state-management/slices/ThrottleSlice';\n\nconst ThrottleSettings: React.FC = () => {\n  const dispatch = useAppDispatch();\n  const throttle = useAppSelector(state => state.throttle.throttleValue);\n  const [throttleNum, setThrottleNum] = useState<string>(throttle);\n\n  useEffect(() => {\n    setThrottleNum(throttle);\n  }, [throttle]);\n\n  useEffect(() => {\n    setThrottleNum(throttle);\n  }, [throttle]);\n  // onClick function for reset button. 70ms is the default throttle\n  const onClick = (): void => {\n    dispatch(resetThrottle());\n  };\n\n  // Creating function to tie to get to the backend\n  const throttleFunc = (e: React.FormEvent<HTMLFormElement>): void => {\n    e.preventDefault();\n\n    const backgroundConnection = chrome.runtime.connect();\n    // post the message with index in payload to the connection\n    backgroundConnection.postMessage({\n      action: 'throttleEdit',\n      tabId: chrome.devtools.inspectedWindow.tabId,\n      payload: {value: throttleNum}, // edit this value to some other number\n    });\n\n    dispatch(newThrottle(throttleNum));\n  };\n\n  return (\n    <div>\n      <h2>Enter Throttle</h2>\n      <form onSubmit={throttleFunc}>\n        <input\n          type=\"text\"\n          style={{marginBottom: '10px'}}\n          onChange={e => setThrottleNum(e.target.value)}\n          value={throttleNum}\n          placeholder=\"enter in milliseconds\"\n        />{' '}\n        <span style={{fontSize: '14px'}}>milliseconds</span>\n        <div>\n          <button type=\"submit\">Enter</button>\n          <button onClick={onClick} type=\"button\">\n            Reset\n          </button>\n        </div>\n      </form>\n      <span>\n        Current throttle is{' '}\n        {throttle === '70' ? `${throttle} (default)` : throttle} ms\n      </span>\n    </div>\n  );\n};\n\nexport default ThrottleSettings;\n"
  },
  {
    "path": "src/app/components/Settings/__tests__/AtomSettings.unit.test.js",
    "content": "import React, {useState} from 'react';\nimport ReactDOM from 'react-dom';\nimport {getQueriesForElement, getByText} from '@testing-library/dom';\n\nimport {render, fireEvent} from '@testing-library/react';\nconst {Multiselect} = require('multiselect-react-dropdown');\n\nimport AtomSettings from '../AtomSettings';\nconst mockprops = {\n  snapshotHistory: [{}],\n  selected: [{name: 'testname1'}, {name: 'testname2'}, {name: 'testname3'}],\n  setSelected: jest.fn(),\n};\ndescribe('atom settings properly rendering', () => {\n  it('Component Renders', () => {\n    const {getByPlaceholderText, debug, getByText} = render(\n      <AtomSettings {...mockprops} />,\n    );\n    getByText('testname1');\n  });\n  // Check if render without crashing\n  it('renders Atom Settings without crashing', () => {\n    const root = document.createElement('div');\n    ReactDOM.render(<AtomSettings {...mockprops} />, root);\n  });\n  it('renders Multiselect component without crashing', () => {\n    const root = document.createElement('div');\n    ReactDOM.render(<Multiselect />, root);\n  });\n})\n"
  },
  {
    "path": "src/app/components/Settings/__tests__/StateSettings.unit.test.js",
    "content": "import React from 'react';\nimport ReactDOM from 'react-dom';\nimport StateSettings from '../StateSettings';\n\nimport {render, cleanup, getByTestId, fireEvent} from '@testing-library/react';\nimport '@testing-library/jest-dom/extend-expect';\n\nafterEach(cleanup);\n\nconst mockProps = {\n  checked: false,\n  setChecked: jest.fn(),\n};\n\nit('renders without crashing', () => {\n  const div = document.createElement('div');\n  ReactDOM.render(<StateSettings />, div);\n});\n\nit('renders persist state text correctly', () => {\n  const {getByTestId} = render(<StateSettings />);\n  expect(getByTestId('stateSettings')).toHaveTextContent('Persist State');\n});\n\ndescribe('input checkbox', () => {\n  beforeAll(cleanup);\n  afterAll(cleanup);\n\n  it('is rendered correctly', () => {\n    const {getByTestId} = render(<StateSettings />);\n    expect(getByTestId('stateSettingsToggle')).toHaveAttribute(\n      'type',\n      'checkbox',\n    );\n  });\n\n  it('is unchecked initially', () => {\n    const {getByTestId} = render(<StateSettings checked={mockProps.checked} />);\n    const toggle = getByTestId('stateSettingsToggle');\n    expect(toggle).toHaveProperty('checked', false);\n  });\n\n  it('is checked after user clicks', () => {\n    const {getByTestId} = render(<StateSettings {...mockProps} />);\n    const toggle = getByTestId('stateSettingsToggle');\n    chrome.runtime.connect = function () {\n      return {postMessage: jest.fn()};\n    };\n    chrome.devtools = {inspectedWindow: {}};\n    fireEvent.change(toggle, {target: {checked: true}});\n    expect(toggle).toBeChecked();\n  });\n});\n\nit('should match snapshot when no props are passed in', () => {\n  const {asFragment} = render(<StateSettings />);\n  expect(asFragment()).toMatchSnapshot();\n});\n\nit('should match snapshot when props are passed in', () => {\n  const {asFragment} = render(<StateSettings {...mockProps} />);\n  expect(asFragment()).toMatchSnapshot();\n});\n"
  },
  {
    "path": "src/app/components/Settings/__tests__/ThrottleSettings.unit.test.js",
    "content": "import React, {useState} from 'react';\nimport ReactDOM from 'react-dom';\nimport {getQueriesForElement, getByText} from '@testing-library/dom';\n\nimport {render, fireEvent} from '@testing-library/react';\n\nimport ThrottleSettings from '../ThrottleSettings';\n\n// Check the render\ndescribe('Throttle Component renders correctly', () => {\n  beforeEach(() => {\n    const {} = render(<ThrottleSettings throttleDisplay={1000} />);\n  });\n  it('renders without crashing', () => {\n    const root = document.createElement('div');\n    ReactDOM.render(<ThrottleSettings />, root);\n  });\n  it('renders to the dom with the correct content', () => {\n    const root = document.createElement('div');\n    ReactDOM.render(<ThrottleSettings throttleDisplay={1000} />, root);\n    const {getByText} = getQueriesForElement(root);\n    expect(getByText('Enter Throttle')).not.toBeNull();\n    expect(getByText('Enter')).not.toBeNull();\n    expect(getByText('Reset')).not.toBeNull();\n    expect(getByText('Current throttle is 1000 ms')).not.toBeNull();\n  });\n});\n\ndescribe('Throttle Snapshots Testing', () => {\n  it('renders & matches snapshots', () => {\n    const {asFragment} = render(<ThrottleSettings />);\n    expect(asFragment()).toMatchSnapshot();\n  });\n});\n\ndescribe('Check button and user input functionalities', () => {\n  it('Check the user input typing', () => {\n    const {getByPlaceholderText} = render(\n      <ThrottleSettings throttleDisplay={1000} />,\n    );\n    const input = getByPlaceholderText('enter in milliseconds');\n\n    fireEvent.change(input, {target: {value: 500}}); // this successfully changes the value\n    expect(input.value).toEqual('500');\n  });\n\n  it('Check the enter and reset button', () => {\n    // creating mock functions for chrome\n    chrome.runtime.connect = function () {\n      return {postMessage: function () {}};\n    };\n    let setThrottleDisplay = jest.fn();\n    chrome.devtools = {inspectedWindow: {}};\n\n    // Rendering the component\n    const {getByText} = render(\n      <ThrottleSettings\n        throttleDisplay={1000}\n        setThrottleDisplay={setThrottleDisplay}\n      />,\n    );\n    // Testing the buttons\n    const submitButton = getByText('Enter'); // check these buttons -- how to test\n    const resetButton = getByText('Reset');\n    fireEvent.click(submitButton);\n    fireEvent.click(resetButton);\n  });\n});\n"
  },
  {
    "path": "src/app/components/Settings/__tests__/__snapshots__/StateSettings.unit.test.js.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`should match snapshot when no props are passed in 1`] = `\n<DocumentFragment>\n  <div\n    data-testid=\"stateSettings\"\n  >\n    <h2>\n      Persist State\n    </h2>\n    <div\n      class=\"persistContainer\"\n    >\n      <label\n        class=\"switch\"\n        for=\"checkbox\"\n      >\n        <input\n          data-testid=\"stateSettingsToggle\"\n          id=\"checkbox\"\n          type=\"checkbox\"\n        />\n        <div\n          class=\"slider round\"\n        />\n         \n      </label>\n      <span\n        class=\"persistText\"\n      >\n        Slide to Toggle Persist Mode\n      </span>\n    </div>\n  </div>\n</DocumentFragment>\n`;\n\nexports[`should match snapshot when props are passed in 1`] = `\n<DocumentFragment>\n  <div\n    data-testid=\"stateSettings\"\n  >\n    <h2>\n      Persist State\n    </h2>\n    <div\n      class=\"persistContainer\"\n    >\n      <label\n        class=\"switch\"\n        for=\"checkbox\"\n      >\n        <input\n          data-testid=\"stateSettingsToggle\"\n          id=\"checkbox\"\n          type=\"checkbox\"\n        />\n        <div\n          class=\"slider round\"\n        />\n         \n      </label>\n      <span\n        class=\"persistText\"\n      >\n        Slide to Toggle Persist Mode\n      </span>\n    </div>\n  </div>\n</DocumentFragment>\n`;\n"
  },
  {
    "path": "src/app/components/Settings/__tests__/__snapshots__/ThrottleSettings.unit.test.js.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`Throttle Snapshots Testing renders & matches snapshots 1`] = `\n<DocumentFragment>\n  <div>\n    <h2>\n      Enter Throttle\n    </h2>\n    <form>\n      <input\n        placeholder=\"enter in milliseconds\"\n        style=\"margin-bottom: 10px;\"\n        type=\"text\"\n        value=\"\"\n      />\n       \n      <span\n        style=\"font-size: 14px;\"\n      >\n        milliseconds\n      </span>\n      <div>\n        <button\n          type=\"submit\"\n        >\n          Enter\n        </button>\n        <button\n          type=\"submit\"\n        >\n          Reset\n        </button>\n      </div>\n    </form>\n    <span>\n      Current throttle is  ms\n    </span>\n  </div>\n</DocumentFragment>\n`;\n"
  },
  {
    "path": "src/app/components/Slider/MainSlider.tsx",
    "content": "import React from 'react';\nimport Slider from 'rc-slider';\nimport Tooltip from 'rc-tooltip';\nimport {useAppSelector, useAppDispatch} from '../../state-management/hooks';\nimport {setRenderIndex} from '../../state-management/slices/SnapshotSlice';\nimport {selectFilterState} from '../../state-management/slices/FilterSlice';\n\nconst {Handle} = Slider;\n\ninterface handleProps {\n  className: string;\n  prefixCls?: string;\n  vertical?: boolean;\n  offset: number;\n  value: number;\n  dragging?: boolean;\n  disabled?: boolean;\n  min?: number;\n  max?: number;\n  reverse?: boolean;\n  index: number;\n  tabIndex?: number;\n}\n\nconst handle = (props: handleProps) => {\n  const {value, dragging, index, ...restProps} = props;\n\n  return (\n    <Tooltip\n      prefixCls=\"rc-slider-tooltip\"\n      overlay={''}\n      visible={dragging}\n      placement=\"top\"\n      key={index}>\n      <Handle value={value} {...restProps} />\n    </Tooltip>\n  );\n};\n\n// interface MainSliderProps {\n//   snapshotsLength: number;\n// }\n\nfunction MainSlider() {\n  const dispatch = useAppDispatch();\n\n  const renderIndex = useAppSelector(state => state.snapshot.renderIndex);\n  const snapshotHistory = useAppSelector(\n    state => state.snapshot.snapshotHistory,\n  );\n  const filterData = useAppSelector(selectFilterState);\n  console.log('this is renderIndex ', renderIndex);\n  console.log('this is snapshotHistory length ', snapshotHistory.length);\n\n  //indexDiff is used to ensure the index of filter matches the index of the snapshots array in the backend\n  let indexDiff: number = 0;\n  if (filterData[0] && filterData[0].indexDiff) {\n    indexDiff = filterData[0].indexDiff;\n  }\n\n  const timeTravelFunc = (index: number) => {\n    // variable to store/reference connection\n    const backgroundConnection = chrome.runtime.connect();\n    //const test = chrome.extension.getBackgroundPage();\n    // post the message with index in payload to the connection\n    backgroundConnection.postMessage({\n      action: 'snapshotTimeTravel',\n      tabId: chrome.devtools.inspectedWindow.tabId,\n      payload: {\n        snapshotIndex: index + indexDiff,\n      },\n    });\n  };\n\n  const forwardButton = () => {\n    console.log('forward');\n    if (renderIndex < snapshotHistory.length - 1) {\n      dispatch(setRenderIndex(renderIndex + 1));\n      timeTravelFunc(renderIndex + 1);\n    }\n  };\n\n  const backwardButton = () => {\n    if (renderIndex > 0) {\n      dispatch(setRenderIndex(renderIndex - 1));\n      timeTravelFunc(renderIndex - 1);\n    }\n  };\n\n  const playButton = () => {\n    let currentIndex = 0;\n    dispatch(setRenderIndex(currentIndex));\n    timeTravelFunc(currentIndex);\n    const intervalId = setInterval(() => {\n      if (currentIndex < snapshotHistory.length - 1) {\n        dispatch(setRenderIndex(currentIndex + 1));\n        timeTravelFunc(currentIndex + 1);\n        currentIndex += 1;\n      } else {\n        clearInterval(intervalId);\n      }\n    }, 1000);\n  };\n\n  return (\n    <div className=\"main-slider\">\n      <button\n        id=\"slider-start-button\"\n        type=\"button\"\n        onClick={() => {\n          playButton();\n        }}>\n        Play\n      </button>\n      <Slider\n        min={0}\n        max={snapshotHistory.length - 1}\n        value={renderIndex}\n        onChange={(index: number) => {\n          dispatch(setRenderIndex(index));\n          timeTravelFunc(index);\n        }}\n        handle={handle}\n      />\n      <button\n        className=\"backfor-button\"\n        type=\"button\"\n        onClick={() => {\n          backwardButton();\n        }}>\n        {'<'}\n      </button>\n      <button\n        className=\"backfor-button\"\n        type=\"button\"\n        onClick={() => {\n          forwardButton();\n        }}>\n        {'>'}\n      </button>\n    </div>\n  );\n}\n\nexport default MainSlider;\n"
  },
  {
    "path": "src/app/components/SnapshotList/__tests__/SnapshotList.unit.test.js",
    "content": "import React, {useState} from 'react';\nimport {configure, shallow} from 'enzyme';\nimport Adapter from 'enzyme-adapter-react-16';\n\nimport SnapshotsList from '../SnapshotList';\n\n// Newer enzyme versions require this configuration to adapt to a particular version of React\nconfigure({adapter: new Adapter()});\n\ndescribe('Testing for rendering of SnapshotList under several expected conditions', () => {\n  let wrapper;\n  let childObj;\n  let props;\n  beforeEach(() => {\n    childObj = {\n      actualDuration: 14,\n      children: [],\n      name: 'Replace',\n      recoildNodes: [],\n      tag: 0,\n      treeBaseDuration: 6,\n      wasSuspended: false,\n    };\n\n    props = {\n      renderIndex: 0,\n      snapshotHistoryLength: 1,\n      setRenderIndex: jest.fn(),\n      timeTravelFunc: jest.fn(),\n      selected: [{name: 'id'}],\n      filter: [],\n      snapshotHistory: [\n        {\n          componentAtomTree: {\n            actualDuration: 12,\n            children: [\n              {\n                ...childObj,\n                name: 'RecoilRoot',\n                childdren: [\n                  {\n                    ...childObj,\n                    tag: 10,\n                    children: [{...childObj}],\n                  },\n                ],\n              },\n            ],\n            name: 'HR',\n            recoilNodes: [],\n            tag: 3,\n            treeBaseDuration: 6,\n            wasSuspended: false,\n          },\n          filteredSnapshot: {\n            id: {\n              contents: 1,\n              nodeDeps: [],\n              nodeToNodeSubscriptions: [],\n              type: 'RecoilState',\n            },\n          },\n        },\n      ],\n    };\n  });\n\n  it('Renders with no componentAtomTree children', () => {\n    props.snapshotHistory[0].componentAtomTree.children = [];\n    wrapper = shallow(<SnapshotsList {...props} />);\n    expect(wrapper.type()).toEqual('div');\n    expect(wrapper.find('div').first().hasClass('SnapshotsList')).toEqual(true);\n  });\n\n  it('Renders one div with class individualSnapshot when filter is empty', () => {\n    wrapper = shallow(<SnapshotsList {...props} />);\n    let individual = 0;\n    for (let key of wrapper.find('div')) {\n      if (key.props) {\n        if (key.props.className) {\n          if (key.props.className === 'individualSnapshot') individual += 1;\n        }\n      }\n    }\n    expect(individual).toBe(1);\n  });\n\n  // Test to see if invalid props break the function. Functional component should render empty div when props are invalid.\n  describe('Snapshots List Error Handling', () => {\n    it('Snapshots List renders empty divs when renderIndex is invalid', () => {\n      props.renderIndex = -1;\n      wrapper = shallow(<SnapshotsList {...props} />);\n      expect(wrapper.type()).toEqual('div');\n      expect(wrapper.find('div').first().hasClass('SnapshotsList')).toEqual(\n        true,\n      );\n    });\n\n    it('Handles the snapshotHistoryLength prop being greater than the filter length', () => {\n      props.snapshotHistoryLength = 8;\n      wrapper = shallow(<SnapshotsList {...props} />);\n      expect(wrapper.type()).toEqual('div');\n      expect(wrapper.find('div').first().hasClass('SnapshotsList')).toEqual(\n        true,\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "src/app/components/StateDiff/Diff.tsx",
    "content": "import React, {useState} from 'react';\nimport {diff, formatters} from 'jsondiffpatch';\nimport ReactHtmlParser from 'react-html-parser';\nimport {useAppSelector} from '../../state-management/hooks';\n\n// renders the difference between the most recent state change and the previous\nconst Diff: React.FC = () => {\n  // retrieve snapshotHistory State from Redux Store\n  const snapshotHistory = useAppSelector(\n    state => state.snapshot.snapshotHistory,\n  );\n  const renderIndex = useAppSelector(state => state.snapshot.renderIndex);\n\n  const currentState = useAppSelector(state => state);\n  console.log('newState in Diff: ', currentState);\n\n  const filteredPrevSnap =\n    renderIndex > 0\n      ? snapshotHistory[renderIndex - 1].filteredSnapshot\n      : undefined;\n\n  const filteredCurSnap = snapshotHistory[renderIndex]\n    ? snapshotHistory[renderIndex].filteredSnapshot\n    : snapshotHistory[0].filteredSnapshot;\n\n  // useState hook to update the toggle of showUnchanged or hideUnchanged\n  const [rawToggle, setRawToggle] = useState(false);\n  // diffing between filteredPrevSnap && filteredCurSnap\n  const delta = diff(filteredPrevSnap, filteredCurSnap);\n  // string of html with comparisons\n  const html = formatters.html.format(delta, filteredPrevSnap);\n  // conditionally render changes or not based on rawToggle bool\n  formatters.html.showUnchanged(rawToggle);\n\n  return (\n    <div className=\"Diff\">\n      <div className=\"toggleDiv\">\n        <button\n          id=\"raw\"\n          className=\"rawToggle\"\n          style={{color: rawToggle ? '#E6E6E6' : '#989898'}}\n          onClick={() => {\n            setRawToggle(!rawToggle);\n          }}>\n          Raw\n        </button>\n      </div>\n      {ReactHtmlParser(html)}\n    </div>\n  );\n};\n\nexport default Diff;\n"
  },
  {
    "path": "src/app/components/StateDiff/__tests__/StateDiff.unit.test.js",
    "content": "import React from 'react';\nimport ReactDOM from 'react-dom';\nimport Diff from '../Diff';\n\nimport {\n  render,\n  cleanup,\n  getByText,\n  fireEvent,\n  debug,\n} from '@testing-library/react';\nimport '@testing-library/jest-dom/extend-expect';\nimport {\n  filteredPrevSnapMock,\n  filteredCurSnapMock,\n} from '../../../../../mock/snapshot.js';\n\nafterEach(cleanup);\n\nconst mockProps = {\n  filteredPrevSnap: filteredPrevSnapMock,\n  filteredCurSnap: filteredCurSnapMock,\n};\ndescribe('Diff Component', () => {\n  it('renders without crashing', () => {\n    const div = document.createElement('div');\n    ReactDOM.render(<Diff />, div);\n  });\n});\n\ndescribe('Raw Toggle Button', () => {\n  beforeAll(cleanup);\n  afterAll(cleanup);\n\n  it('raw button should be color #989898 at initial render', () => {\n    const {getByText} = render(<Diff />);\n    expect(getByText('Raw')).toHaveStyle('color: #989898');\n  });\n\n  it('raw button should change to color #E6E6E6 after clicked', () => {\n    const {getByText} = render(<Diff />);\n    fireEvent.click(getByText('Raw'));\n    expect(getByText('Raw')).toHaveStyle('color: #E6E6E6');\n  });\n});\n\nit('should match snapshot when no props are passed in', () => {\n  const {asFragment} = render(<Diff />);\n  expect(asFragment()).toMatchSnapshot();\n});\n\nit('should match snapshot when props are passed in', () => {\n  const {asFragment} = render(<Diff {...mockProps} />);\n  expect(asFragment()).toMatchSnapshot();\n});\n"
  },
  {
    "path": "src/app/components/StateDiff/__tests__/__snapshots__/StateDiff.unit.test.js.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`should match snapshot when no props are passed in 1`] = `\n<DocumentFragment>\n  <div\n    class=\"Diff\"\n  >\n    <div\n      class=\"toggleDiv\"\n    >\n      <button\n        class=\"rawToggle\"\n        style=\"color: rgb(152, 152, 152);\"\n      >\n        Raw\n      </button>\n    </div>\n  </div>\n</DocumentFragment>\n`;\n\nexports[`should match snapshot when props are passed in 1`] = `\n<DocumentFragment>\n  <div\n    class=\"Diff\"\n  >\n    <div\n      class=\"toggleDiv\"\n    >\n      <button\n        class=\"rawToggle\"\n        style=\"color: rgb(152, 152, 152);\"\n      >\n        Raw\n      </button>\n    </div>\n    <div\n      class=\"jsondiffpatch-delta jsondiffpatch-node jsondiffpatch-child-node-type-object\"\n    >\n      <ul\n        class=\"jsondiffpatch-node jsondiffpatch-node-type-object\"\n      >\n        <li\n          class=\"jsondiffpatch-unchanged\"\n          data-key=\"dummyAtom1\"\n        >\n          <div\n            class=\"jsondiffpatch-property-name\"\n          >\n            dummyAtom1\n          </div>\n          <div\n            class=\"jsondiffpatch-value\"\n          >\n            <pre>\n              {\n  \"contents\": {\n    \"hello\": [],\n    \"hi\": []\n  },\n  \"nodeDeps\": [],\n  \"nodeToNodeSubscriptions\": [],\n  \"type\": \"RecoilState\"\n}\n            </pre>\n          </div>\n        </li>\n        <li\n          class=\"jsondiffpatch-unchanged\"\n          data-key=\"listState\"\n        >\n          <div\n            class=\"jsondiffpatch-property-name\"\n          >\n            listState\n          </div>\n          <div\n            class=\"jsondiffpatch-value\"\n          >\n            <pre>\n              {\n  \"contents\": [\n    {\n      \"text\": \"list item\"\n    },\n    {\n      \"text\": \"list item\"\n    },\n    {\n      \"text\": \"list item\"\n    }\n  ],\n  \"nodeDeps\": [],\n  \"nodeToNodeSubscriptions\": [\n    \"selectorTest\",\n    \"stateLengths\"\n  ],\n  \"type\": \"RecoilState\"\n}\n            </pre>\n          </div>\n        </li>\n        <li\n          class=\"jsondiffpatch-node jsondiffpatch-child-node-type-object\"\n          data-key=\"listState2\"\n        >\n          <div\n            class=\"jsondiffpatch-property-name\"\n          >\n            listState2\n          </div>\n          <ul\n            class=\"jsondiffpatch-node jsondiffpatch-node-type-object\"\n          >\n            <li\n              class=\"jsondiffpatch-node jsondiffpatch-child-node-type-array\"\n              data-key=\"contents\"\n            >\n              <div\n                class=\"jsondiffpatch-property-name\"\n              >\n                contents\n              </div>\n              <ul\n                class=\"jsondiffpatch-node jsondiffpatch-node-type-array\"\n              >\n                <li\n                  class=\"jsondiffpatch-unchanged\"\n                  data-key=\"0\"\n                >\n                  <div\n                    class=\"jsondiffpatch-property-name\"\n                  >\n                    0\n                  </div>\n                  <div\n                    class=\"jsondiffpatch-value\"\n                  >\n                    <pre>\n                      {\n  \"text\": \"list item\"\n}\n                    </pre>\n                  </div>\n                </li>\n                <li\n                  class=\"jsondiffpatch-unchanged\"\n                  data-key=\"1\"\n                >\n                  <div\n                    class=\"jsondiffpatch-property-name\"\n                  >\n                    1\n                  </div>\n                  <div\n                    class=\"jsondiffpatch-value\"\n                  >\n                    <pre>\n                      {\n  \"text\": \"list item\"\n}\n                    </pre>\n                  </div>\n                </li>\n                <li\n                  class=\"jsondiffpatch-unchanged\"\n                  data-key=\"2\"\n                >\n                  <div\n                    class=\"jsondiffpatch-property-name\"\n                  >\n                    2\n                  </div>\n                  <div\n                    class=\"jsondiffpatch-value\"\n                  >\n                    <pre>\n                      {\n  \"text\": \"list item\"\n}\n                    </pre>\n                  </div>\n                </li>\n                <li\n                  class=\"jsondiffpatch-deleted\"\n                  data-key=\"3\"\n                >\n                  <div\n                    class=\"jsondiffpatch-property-name\"\n                  >\n                    3\n                  </div>\n                  <div\n                    class=\"jsondiffpatch-value\"\n                  >\n                    <pre>\n                      {\n  \"text\": \"list item\"\n}\n                    </pre>\n                  </div>\n                </li>\n              </ul>\n            </li>\n            <li\n              class=\"jsondiffpatch-unchanged\"\n              data-key=\"nodeDeps\"\n            >\n              <div\n                class=\"jsondiffpatch-property-name\"\n              >\n                nodeDeps\n              </div>\n              <div\n                class=\"jsondiffpatch-value\"\n              >\n                <pre>\n                  []\n                </pre>\n              </div>\n            </li>\n            <li\n              class=\"jsondiffpatch-unchanged\"\n              data-key=\"nodeToNodeSubscriptions\"\n            >\n              <div\n                class=\"jsondiffpatch-property-name\"\n              >\n                nodeToNodeSubscriptions\n              </div>\n              <div\n                class=\"jsondiffpatch-value\"\n              >\n                <pre>\n                  [\n  \"stateLengths\"\n]\n                </pre>\n              </div>\n            </li>\n            <li\n              class=\"jsondiffpatch-unchanged\"\n              data-key=\"type\"\n            >\n              <div\n                class=\"jsondiffpatch-property-name\"\n              >\n                type\n              </div>\n              <div\n                class=\"jsondiffpatch-value\"\n              >\n                <pre>\n                  \"RecoilState\"\n                </pre>\n              </div>\n            </li>\n          </ul>\n        </li>\n        <li\n          class=\"jsondiffpatch-unchanged\"\n          data-key=\"selectorTest\"\n        >\n          <div\n            class=\"jsondiffpatch-property-name\"\n          >\n            selectorTest\n          </div>\n          <div\n            class=\"jsondiffpatch-value\"\n          >\n            <pre>\n              {\n  \"contents\": \"test\",\n  \"nodeDeps\": [\n    \"listState\"\n  ],\n  \"nodeToNodeSubscriptions\": [],\n  \"type\": \"RecoilValueReadOnly\"\n}\n            </pre>\n          </div>\n        </li>\n        <li\n          class=\"jsondiffpatch-unchanged\"\n          data-key=\"stateLengths\"\n        >\n          <div\n            class=\"jsondiffpatch-property-name\"\n          >\n            stateLengths\n          </div>\n          <div\n            class=\"jsondiffpatch-value\"\n          >\n            <pre>\n              {\n  \"contents\": 6,\n  \"nodeDeps\": [\n    \"listState\",\n    \"listState2\"\n  ],\n  \"nodeToNodeSubscriptions\": [],\n  \"type\": \"RecoilValueReadOnly\"\n}\n            </pre>\n          </div>\n        </li>\n      </ul>\n    </div>\n  </div>\n</DocumentFragment>\n`;\n"
  },
  {
    "path": "src/app/components/StateTree/Tree.tsx",
    "content": "import React from 'react';\nimport JSONTree from 'react-json-tree';\nimport {useAppSelector} from '../../state-management/hooks';\n\nconst Tree: React.FC = () => {\n  // render json tree while passing in newSnap as data to JSONTree\n  //Retrieve snapshotHistory State from Redux Store\n  const snapshotHistory = useAppSelector(\n    state => state.snapshot.snapshotHistory,\n  );\n  const renderIndex = useAppSelector(state => state.snapshot.renderIndex);\n  const filteredCurSnap = snapshotHistory[renderIndex].filteredSnapshot;\n  return (\n    <div className=\"Tree\">\n      {filteredCurSnap && (\n        <JSONTree\n          data={filteredCurSnap}\n          theme={{tree: () => ({className: 'json-tree'})}}\n          shouldExpandNode={() => true}\n          labelRenderer={raw =>\n            typeof raw[0] !== 'number' ? (\n              <span style={{fontSize: '14px'}}>{raw[0]}</span>\n            ) : null\n          }\n        />\n      )}\n    </div>\n  );\n};\n\nexport default Tree;\n"
  },
  {
    "path": "src/app/components/StateTree/__tests__/Tree.unit.test.js",
    "content": "import React, {useState} from 'react';\nimport ReactDOM from 'react-dom';\nimport {getQueriesForElement, getByText} from '@testing-library/dom';\n\nimport {render, fireEvent} from '@testing-library/react';\n\nimport Tree from '../Tree';\n\nit('Current Tree Renders', () => {\n  const {getByPlaceholderText, debug} = render(<Tree filterCurSnap={[]} />);\n});\n"
  },
  {
    "path": "src/app/components/Testing/CodeResults.js",
    "content": "/* eslint-disable prettier/prettier */\nimport React from 'react';\n\nconst CodeResults = props => {\nconst { evaluatedCode } = props;\n  return (\n    <div className='console-output'>\n      {evaluatedCode}\n    </div>\n  )\n}\n\nexport default CodeResults;"
  },
  {
    "path": "src/app/components/Testing/Editor.js",
    "content": "/* eslint-disable prettier/prettier */\nimport React from 'react';\nimport {useState} from 'react';\n//import our css stylings from codemirror\nimport 'codemirror/lib/codemirror.css';\nimport 'codemirror/theme/dracula.css';\nimport 'codemirror/mode/javascript/javascript';\nimport 'codemirror/mode/css/css';\nimport { Controlled as ControlledEditor } from 'react-codemirror2';\nimport CodeResults from './CodeResults'\nimport { useSetRecoilState, useRecoilValue } from 'recoil';\nimport { selector, atom } from 'recoil';\nimport { dummySelector, dummyAtom } from './dummySelector';\nconst Editor = props => {\nconst { value, onChange, loadButton, selectorsFnAsStrings, madeAtoms, toBeValue, currentAtom, currentAtomValue, currentSelector, setCurrentSelector, madeSelectors, parameters } = props;\n// expect two more prop drilling variables: expect (atom's current value) amd user inputted to be (atom's expected value)\nconst [hasRendered, setHasRendered] = useState(false);\nlet mySelector;\nlet comparisonValue;\n// create the selectors and atoms for use in this editor associated with the drop down selection\nif (madeSelectors[currentSelector]){\n  mySelector = useSetRecoilState(madeSelectors[currentSelector]);\n\n  if (!hasRendered){\n    \n    mySelector()\n    setHasRendered(true);\n  }\n  comparisonValue = useRecoilValue(madeAtoms[currentAtom]);\n\n} else {\n  mySelector = useSetRecoilState(dummySelector);\n  comparisonValue = useRecoilValue(dummyAtom);\n}\n\n\nconst [ evaluatedCode, setEvaluatedCode ] = useState('Run code here...');\n\nfunction handleRunCodeClick () {\n  try {\n    if (toBeValue !== comparisonValue) {\n      setEvaluatedCode(`❌ Expected ${currentAtom} to be ${toBeValue} and Received ${comparisonValue}`);\n    }\n    else {\n      setEvaluatedCode(`✅ Expected ${currentAtom} to be ${toBeValue} and Received ${comparisonValue}`);\n    }\n  }\n  catch(err) {\n    setEvaluatedCode(err.message);\n  }\n}\n\nfunction handleChange (editor, data, value) {\n  onChange(value);\n}\n\n  return (\n      <div className='editor-container'>\n        <div>\n          <ControlledEditor\n          className='code-mirror-wrapper'\n          onBeforeChange={handleChange}\n          value={value}\n          options={{\n            lineWrapping: true,\n            mode: 'javascript',\n            lineNumbers: true,\n            lint: true,\n            //indentUnit: 2,\n            autoCloseBrackets: true,\n            theme: 'dracula',\n            // smartIndent: tru,\n          }}\n          />\n        </div>\n\n        <div>\n        <button className='run-code' onClick={() => handleRunCodeClick(value)}>Run Code</button>\n        </div>\n\n        <CodeResults\n        evaluatedCode={evaluatedCode}\n        />\n\n      </div>\n  )\n}\n\nexport default Editor;"
  },
  {
    "path": "src/app/components/Testing/SelectorsButton.tsx",
    "content": "/* eslint-disable prettier/prettier */\nimport React, {useState, useEffect} from 'react';\nimport DisplayTests from './displayTests';\nimport {useAppSelector} from '../../state-management/hooks';\nimport { useSetRecoilState } from 'recoil';\n\nconst SelectorsButton: React.FC<any> = props => {\n\n  const {\n    selectorsFnAsStrings, selectors, atoms, onChange, currentSelector, setCurrentSelector, currentAtom, setCurrentAtom, currentAtomValue, setCurrentAtomValue, toBeValue, setToBeValue, parameters, setParameters, \n    loadedSelector, setLoadedSelector, madeSelectors\n  } = props;\n\n  // create a hook that stores the current value of the selected drop down\n  //const [currentSelector, setCurrentSelector] = useState('');\n  // label of the atom associated with the selcetor clicked from the drop down\n  // value of the atom associated with the selector clicked from the drop down\n  // value to be expected -> updated in displayTests\n  // stateful value to contain parameters initialized as an empty array\n  // grab the filtered snapshot so we know which atoms and selectors are dependent of each other\n  const snapshotHistory = useAppSelector(\n    state => state.snapshot.snapshotHistory,\n  );\n\n  const handleChange = (item) => {\n    const selectorKey = item.options[item.selectedIndex].value;\n    // update state with the chosen Selector\n    setCurrentSelector(selectorKey);\n    \n    const capturedFnString = selectorsFnAsStrings[selectorKey];\n    let { key, set, get } = capturedFnString;\n\n    const parser = (string) => {\n      if (!string) return;\n      \n      // the portion before the fat arrow (parameters)\n      const firstPortion = string.slice(0, string.indexOf(';'));\n      // the portion after the fat arrow (function definition)\n      const secondPortion = string.slice(string.indexOf(';') + 1, string.length);\n      \n      // determine if the passed in string has a get, set, or both get and set methods\n      let newFirstPortion = '';\n      if (firstPortion.includes('get') && firstPortion.includes('set')) {newFirstPortion += 'get, set'}\n      else if (firstPortion.includes('get')) newFirstPortion += 'get';\n      else if (firstPortion.includes('set')) newFirstPortion += 'set';\n      \n      //parameter portion will be assigned the value of the strings following _ref6 (let {get,set})\n      //if there is a comma found within the slice between the parameter parenthesis\n      let parameterPortion = '';\n      if (string.slice(string.indexOf('('), string.indexOf(')')).includes(',')) {\n        parameterPortion = string.slice(string.indexOf(' ') + 1, string.indexOf(')'));\n        // return the first portion ({ get and/or set }), the parameters, and the associated function definition\n        return `({ ${newFirstPortion} }, ${parameterPortion}) => { ${secondPortion}`;\n      }\n      \n      // return the first portion ({ get and/or set }) and the associated function definition\n      return `({ ${newFirstPortion} }) => { ${secondPortion}`;\n    }\n\n    //first portion of string is from 0 to ;\n    const displayedSelector = \n    `Chosen selector:\n  ${key}: {\n    get: ${parser(get)}, \n    set: ${parser(set)},\n  }`\n\n    onChange(displayedSelector);\n\n    // console.log('Selector Key: ', selectorKey);\n    // setCurrentSelector(selectorKey);\n\n    // find the current atom dependent on the selector clicked from the drop down\n    // currently referencing the last element in the snapshotHistory array\n    const dependentAtom = snapshotHistory[snapshotHistory.length - 1].filteredSnapshot[selectorKey].nodeDeps[0];\n    setCurrentAtom(dependentAtom);\n    // find the current atom value from the dependentAtom associated with the clicked on Selector\n    const dependentAtomValue = snapshotHistory[snapshotHistory.length - 1].filteredSnapshot[dependentAtom].contents;\n    setCurrentAtomValue(dependentAtomValue);\n    //setLoadedSelector(useSetRecoilState(madeSelectors.nextPlayerSetSelector));\n    //console.log('loaded Selector set: ', loadedSelector);\n  }\n  \n  //relabeled and used a value property to capture the value on an on change above - you can now find the keys. Function needs to be completed though\n  const HTMLselectorArray: JSX.Element[] = [];\n  selectors.forEach((selector, i) => {\n    HTMLselectorArray.push(<option key={i} value={selector}>{selector}</option>);\n  });\n  \n  return (\n    <div>\n      <div>\n          <label htmlFor='selectors'>Selectors: </label>\n          <select name='selectors' id='selectors' onChange={() => handleChange(document.querySelector('#selectors'))}>{HTMLselectorArray}</select>\n      </div>\n      <div>\n        <DisplayTests \n          currentSelector={currentSelector}\n          currentAtom={currentAtom} \n          currentAtomValue={currentAtomValue} \n          toBeValue={toBeValue}\n          setToBeValue={setToBeValue}\n          parameters={parameters}\n          setParameters={setParameters}\n        />\n      </div>\n    </div>\n  );\n};\n \nexport default SelectorsButton;\n"
  },
  {
    "path": "src/app/components/Testing/TestingContainer.tsx",
    "content": "/* eslint-disable prettier/prettier */\nimport React, { useState, useEffect } from 'react';\n\nimport Editor from './Editor';\nimport SelectorsButton from './SelectorsButton';\nimport {useAppSelector} from '../../state-management/hooks';\nimport {selectAtomsAndSelectorsState} from '../../state-management/slices/AtomsAndSelectorsSlice';\nimport './testing.css';\nimport {atom, selector} from 'recoil';\nimport { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';\n\n\nconst Testing = () => {\n  // retrieve snapshotHistory State from Redux Store\n  const snapshotHistory = useAppSelector(\n      state => state.snapshot.snapshotHistory,\n  );\n  \n  const [ currentAtom, setCurrentAtom ] = useState('');\n  const [ currentAtomValue, setCurrentAtomValue ] = useState('');\n  const [ toBeValue, setToBeValue ] = useState('');\n  const [ parameters, setParameters ] = useState('');\n\n  const [ loadButton, setLoadButton ] = useState(true);\n  const [ secondLoadButton, setSecondLoadButton ] = useState(true);\n  \n  const [ atoms, setAtoms ] = useState([]);\n  const [ selectors, setSelectors ] = useState([]);\n  const [ selectorsFnAsStrings, setSelectorsFnAsStrings ] = useState({});\n  const [ madeSelectors, setMadeSelectors ] = useState({});\n  const [ madeAtoms, setMadeAtoms ] = useState({});\n  const theObject = JSON.parse(JSON.stringify(useAppSelector(selectAtomsAndSelectorsState)));\n  \n\n\n  const handleLoadClick = () => {\n\n    setSelectorsFnAsStrings(theObject.atomsAndSelectors.$selectors);\n    setAtoms(theObject.atomsAndSelectors.atoms);\n    setSelectors(theObject.atomsAndSelectors.selectors);\n    \n    // create our atoms using recoil on the first render instance only\n    const createdAtoms = {};\n    snapshotHistory[snapshotHistory.length - 1].atomsAndSelectors.atoms.forEach(theAtom => {\n      createdAtoms[theAtom] = atom({\n        key: theAtom,\n        default: snapshotHistory[snapshotHistory.length - 1].filteredSnapshot[theAtom].contents,\n      });\n    });\n    setMadeAtoms(createdAtoms);\n    \n    \n    \n    setLoadButton(false);\n  }\n  \n  const handleSecondClick = () => {\n   let selectorsClone = JSON.parse(JSON.stringify(theObject.atomsAndSelectors.$selectors));\n   const createdSelectors = {};\n   theObject.atomsAndSelectors.selectors.forEach(selectorKey => {\n     if (selectorsClone[selectorKey].set) {\n       selectorsClone[selectorKey].set = selectorsClone[selectorKey].set.replaceAll('get(', 'get(madeAtoms.');\n       selectorsClone[selectorKey].set = selectorsClone[selectorKey].set.replaceAll('set(', 'set(madeAtoms.');\n       selectorsClone[selectorKey].set = eval('(' + selectorsClone[selectorKey].set + ')');\n     }\n     if (selectorsClone[selectorKey].get){\n       selectorsClone[selectorKey].get = selectorsClone[selectorKey].get.replaceAll('get(', 'get(madeAtoms.');\n       selectorsClone[selectorKey].get = eval('(' + selectorsClone[selectorKey].get + ')');\n     } else {\n       selectorsClone[selectorKey].get = ({ get }) => {return};\n     }\n     createdSelectors[selectorKey] = selector(selectorsClone[selectorKey]);\n   });\n   setMadeSelectors(createdSelectors);\n\n   setSecondLoadButton(false);\n }\n\n\n  // chosen selector piece of state that tells our container which piece of state has been chosen, and therefore will be drilled down (chosenSelector is just a string)\n  const [ currentSelector, setCurrentSelector ] = useState(''); \n  const [ loadedSelector, setLoadedSelector ] = useState(() => {return});\n  const [ javascript, setJavascript ] = useState('');\n  \n  \n\n  if (loadButton){\n    return (<div>\n      <button onClick={handleLoadClick}>Load Atoms</button>\n    </div>);\n  }\n  else if (secondLoadButton){\n    return (<div>\n      <button onClick={handleSecondClick}>Load Selectors</button>\n    </div>)\n  }\n  else {\n  return (\n      //invoking an onclick to test out the fact that our selector works and is using the selector that WE MADE from our object.\n    <div className='testing-container'>\n      <div>\n        {/* requires a parameter to be passed in, regardless of whether or not it's used. */}\n        <h1>Testing Window</h1>\n        <SelectorsButton\n        key='selectors button'\n        madeSelectors={madeSelectors}\n        currentAtom={currentAtom}\n        setCurrentAtom={setCurrentAtom}\n        currentAtomValue={currentAtomValue}\n        setCurrentAtomValue={setCurrentAtomValue}\n        toBeValue={toBeValue}\n        setToBeValue={setToBeValue}\n        parameters={parameters}\n        setParameters={setParameters}\n        atoms={atoms}\n        selectors={selectors}\n        currentSelector={currentSelector}\n        setCurrentSelector={setCurrentSelector}\n        onChange={setJavascript}\n        selectorsFnAsStrings={selectorsFnAsStrings}\n        loadedSelector={loadedSelector}\n        setLoadedSelector={setLoadedSelector}\n        />\n      </div>\n      <Editor\n          onChange={setJavascript}\n          selectorsFnAsStrings={selectorsFnAsStrings}\n          madeAtoms={madeAtoms}\n          value={javascript}\n          loadedSelector={loadedSelector}\n          toBeValue={toBeValue}             // the value that is expected after the selector is invoked (user input)\n          currentAtom={currentAtom}         // the current atom's key value -> will need to grab our atom's value with matching key from our recoil state to compare with toBeValue\n          currentAtomValue={currentAtomValue} // reassign our GUIs stateful atom as the value of currentAtomValue \n          currentSelector={currentSelector} // the currentSelector chosen from our drop down menu (just the key)\n          setCurrentSelector={setCurrentSelector}\n          madeSelectors={madeSelectors} // the object containing our actual selectors from our recoil state -> match the key from currentSelector from our made selector with useSetRecoilState\n          parameters={parameters}       // pass down the parameters into editor to use\n      />\n    </div>\n    )\n  }\n};\n\nexport default Testing;\n"
  },
  {
    "path": "src/app/components/Testing/displayTests.tsx",
    "content": "/* eslint-disable prettier/prettier */\nimport React, {useState, useEffect} from 'react';\nimport {useRecoilState, useRecoilValue, useSetRecoilState} from 'recoil';\n\nconst DisplayTests: React.FC<any> = (props) => {\n  const {currentSelector, currentAtom, currentAtomValue, toBeValue, setToBeValue, parameters, setParameters} = props;\n\n  // use displayedSelector to check if a new selector is chose\n  // if truthy, reassign parameters and toBeValue to empty strings\n  // reassign displayedSelector to the current value of\n\n  if (currentSelector.length){\n\n    // update the toBe value with wahtever to\n    function handleToBeChange(e) {\n      setToBeValue(e.target.value);\n    };\n\n    function handleParameterChange(e) {\n      setParameters(e.target.value);\n      console.log('E.TARGET.VAL: ', e.target.value);\n    }\n\n    return (\n      <div>\n        <p>Atom ({currentAtom}): <strong>{currentAtomValue}</strong></p>\n        <p>Selector: <strong>{currentSelector}</strong></p>\n        <form>\n          <label htmlFor='expected'>To Be: </label>\n          <input type='text' id='expected' name='expected' onChange={handleToBeChange}></input>\n          \n          <label htmlFor='parameters'>Parameters: </label>\n          <input type='text' id='parameters' name='parameters' onChange={handleParameterChange}></input>\n        </form>\n          \n        <p>expect({currentAtom}).toBe({toBeValue})</p>\n      </div>\n    );\n  }\n  else {\n    return (\n      <div>      \n      </div>\n    );\n  }\n};\n\nexport default DisplayTests;\n"
  },
  {
    "path": "src/app/components/Testing/dummySelector.js",
    "content": "import {atom, selector} from 'recoil';\n\nexport const dummySelector = selector({\n  key: 'dummySelector',\n  get: ({ get }) => {return},\n  set: ({ set }) => {return}\n});\n\nexport const dummyAtom = atom({\n  key: 'dummyAtom',\n  default: 'I am not a real atom'\n})"
  },
  {
    "path": "src/app/components/Testing/testing.css",
    "content": ".testing-container {\n  display: flex;\n  flex-direction: column;\n  margin: 5px;\n  height: 100%;\n  width: 100%;\n}\n\n.code-mirror-wrapper {\n  font-size: medium;\n  position: static;\n  margin-top: 10px;\n  margin-bottom: 10px;\n  height: 75%;\n  width: 95%;\n}\n\n.run-code {\n  margin-top: 10px;\n  margin-bottom: 10px;\n  padding: 5px;\n}\n\n.console-output {\n  font-size: medium;\n}\n\n"
  },
  {
    "path": "src/app/index.tsx",
    "content": "import React from 'react';\nimport ReactDOM from 'react-dom';\nimport App from './components/App';\nimport {store, persistor} from './state-management/index';\nimport {Provider} from 'react-redux';\nimport {PersistGate} from 'redux-persist/integration/react';\n\nReactDOM.render(\n  <Provider store={store}>\n    <PersistGate loading={null} persistor={persistor}>\n      <App />\n    </PersistGate>\n  </Provider>,\n  document.getElementById('root'),\n);\n"
  },
  {
    "path": "src/app/state-management/__tests__/slices.test.tsx",
    "content": "import {setSearchValue} from '../slices/AtomNetworkSlice';\nimport {updateFilter} from '../slices/FilterSlice';\nimport {setSelected, addSelected} from '../slices/SelectedSlice';\nimport {\n  setSnapshotHistory,\n  setRenderIndex,\n  setCleanComponentAtomTree,\n} from '../slices/SnapshotSlice';\nimport {newThrottle, resetThrottle} from '../slices/ThrottleSlice';\nimport {setDefaultZoom, updateZoomState} from '../slices/ZoomSlice';\nimport {store} from '../index';\nimport {snapshotHistoryMock} from '../../../../mock/state-snapshot';\n\ndescribe('ZoomSlice', () => {\n  it('update zoom state', () => {\n    const obj: any = {\n      x: 20,\n      y: 530,\n      k: 0.15,\n    };\n    store.dispatch(updateZoomState(obj));\n\n    const newZoomState = store.getState().zoom.zoomData;\n\n    expect(newZoomState.x).toEqual(20);\n    expect(newZoomState.y).toEqual(530);\n    expect(newZoomState.k).toEqual(0.15);\n  });\n\n  it('reset zoom state', () => {\n    store.dispatch(setDefaultZoom());\n    const defaultZoomState = store.getState().zoom.zoomData;\n    expect(defaultZoomState.x).toEqual(18);\n    expect(defaultZoomState.y).toEqual(527);\n    expect(defaultZoomState.k).toEqual(0.12);\n  });\n});\n\ndescribe('AtomNetworkSlice', () => {\n  it('update search value', () => {\n    store.dispatch(setSearchValue('square-8'));\n    const newSearchValue = store.getState().atomNetwork.searchValue;\n    expect(newSearchValue).toEqual('square-8');\n  });\n});\n\ndescribe('ThrottleSlice', () => {\n  it('update throttle', () => {\n    store.dispatch(newThrottle('100'));\n    const newThrottleValue = store.getState().throttle.throttleValue;\n    expect(newThrottleValue).toEqual('100');\n  });\n\n  it('reset throttle', () => {\n    store.dispatch(resetThrottle());\n    const defaultThrottleValue = store.getState().throttle.throttleValue;\n    expect(defaultThrottleValue).toEqual('70');\n  });\n});\n\ndescribe('FilterSlice', () => {\n  it('update filter', () => {\n    store.dispatch(updateFilter(['square-8']));\n    const filterData = store.getState().filter.filterData;\n    expect(filterData).toEqual(['square-8']);\n  });\n});\n\ndescribe('SnapshotSlice', () => {\n  it('set render index', () => {\n    store.dispatch(setRenderIndex(10));\n    const renderIndex = store.getState().snapshot.renderIndex;\n    expect(renderIndex).toEqual(10);\n  });\n\n  it('set cleaned component atom tree', () => {\n    store.dispatch(\n      setCleanComponentAtomTree(\n        snapshotHistoryMock.snapshotHistory[0].componentAtomTree,\n      ),\n    );\n    const cleanedComponentAtomTreeData = store.getState().snapshot\n      .cleanComponentAtomTree;\n\n    const result: any = {\n      children: [\n        {\n          actualDuration: 0.8450000004813774,\n          children: [],\n          name: 'PlaygroundStart',\n          recoilNodes: [],\n          tag: 0,\n          treeBaseDuration: 0.5550000005314359,\n          wasSuspended: false,\n        },\n      ],\n      name: 'PlaygroundRender',\n      recoilNodes: ['playStart'],\n      tag: 0,\n      actualDuration: 1.6750000004321919,\n    };\n\n    expect(cleanedComponentAtomTreeData).toEqual(result);\n  });\n\n  it('set snapshot', () => {\n    store.dispatch(setSnapshotHistory(snapshotHistoryMock));\n    const snapshotData = store.getState().snapshot.snapshotHistory;\n    expect(snapshotData).toEqual([snapshotHistoryMock]);\n  });\n});\n\ndescribe('SelectedSlice', () => {\n  beforeEach(() => {\n    store.dispatch(setSelected([]));\n  });\n\n  it('set selected item', () => {\n    store.dispatch(setSelected(['square-8']));\n    const selectedData = store.getState().selected.selectedData;\n    expect(selectedData).toEqual(['square-8']);\n  });\n\n  it('add selected item', () => {\n    store.dispatch(addSelected('square-8'));\n    const selectedData = store.getState().selected.selectedData;\n    expect(selectedData).toEqual(['square-8']);\n  });\n});\n"
  },
  {
    "path": "src/app/state-management/hooks.tsx",
    "content": "import {TypedUseSelectorHook, useDispatch, useSelector} from 'react-redux';\nimport type {RootState, AppDispatch} from './index';\n\nexport const useAppDispatch = () => useDispatch<AppDispatch>();\nexport const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;\n"
  },
  {
    "path": "src/app/state-management/index.tsx",
    "content": "import {configureStore, getDefaultMiddleware} from '@reduxjs/toolkit';\nimport throttleReducer from './slices/ThrottleSlice';\nimport zoomReducer from '../state-management/slices/ZoomSlice';\nimport snapshotReducer from '../state-management/slices/SnapshotSlice';\nimport atomNetworkReducer from '../state-management/slices/AtomNetworkSlice';\nimport filterReducer from '../state-management/slices/FilterSlice';\nimport selectedReducer from './slices/SelectedSlice';\nimport atomsAndSelectorsReducer from './slices/AtomsAndSelectorsSlice';\n\nimport {persistStore, persistReducer} from 'redux-persist';\nimport storage from 'redux-persist/lib/storage/session';\nimport {combineReducers} from 'redux';\n\nconst customizedPayloadAction = getDefaultMiddleware({\n  serializableCheck: false,\n});\n\nconst reducers = combineReducers({\n  zoom: zoomReducer,\n  throttle: throttleReducer,\n  snapshot: snapshotReducer,\n  atomNetwork: atomNetworkReducer,\n  filter: filterReducer,\n  selected: selectedReducer,\n  atomsAndSelectors: atomsAndSelectorsReducer,\n});\n\nconst persistConfig = {\n  key: 'root',\n  storage,\n};\n\nconst persistedReducer = persistReducer(persistConfig, reducers);\n\nexport const store = configureStore({\n  reducer: persistedReducer,\n  middleware: customizedPayloadAction,\n});\n\nexport let persistor = persistStore(store);\nexport type RootState = ReturnType<typeof store.getState>;\nexport type AppDispatch = typeof store.dispatch;\n"
  },
  {
    "path": "src/app/state-management/slices/AtomNetworkSlice.tsx",
    "content": "import {createSlice, PayloadAction} from '@reduxjs/toolkit';\n\nconst initialState: any = {\n  searchValue: '',\n};\n\nexport const atomNetworkSlice = createSlice({\n  name: 'atomNetwork',\n  initialState,\n  reducers: {\n    setSearchValue: (state, action: PayloadAction<string>) => {\n      state.searchValue = action.payload;\n    },\n  },\n});\n\nexport const {setSearchValue} = atomNetworkSlice.actions;\n\nexport default atomNetworkSlice.reducer;\n"
  },
  {
    "path": "src/app/state-management/slices/AtomsAndSelectorsSlice.tsx",
    "content": "import {createSlice, PayloadAction} from '@reduxjs/toolkit';\nimport {RootState} from '../index';\n\ninterface selectedState {\n  atomsAndSelectors: {\n    atoms: string[];\n    selectors: string[];\n    $selectors: any;\n  };\n}\n\nconst initialState: selectedState = {\n  atomsAndSelectors: {\n    atoms: [],\n    selectors: [],\n    $selectors: {},\n  },\n};\n\n// zoom: zoomReducer,\n// throttle: throttleReducer,\n// snapshot: snapshotReducer,\n// atomNetwork: atomNetworkReducer,\n// filter: filterReducer,\n// selected: selectedReducer,\n\nexport const atomsAndSelectorsSlice = createSlice({\n  name: 'atomsAndSelectors',\n  initialState,\n  reducers: {\n    setAtomsAndSelectors: (state, action: PayloadAction<any>) => {\n      state.atomsAndSelectors = action.payload;\n    },\n  },\n});\n\nconsole.log('atomsAndSelectorsSlice:', atomsAndSelectorsSlice);\n\nexport const {setAtomsAndSelectors} = atomsAndSelectorsSlice.actions;\n\nexport const selectAtomsAndSelectorsState = (state: RootState) => state.atomsAndSelectors;\n\nexport default atomsAndSelectorsSlice.reducer;\n"
  },
  {
    "path": "src/app/state-management/slices/FilterSlice.tsx",
    "content": "import {createSlice, PayloadAction} from '@reduxjs/toolkit';\nimport {RootState} from '../index';\n\ninterface FilterState {\n  filterData: any;\n}\n\nconst initialState: FilterState = {\n  filterData: [],\n};\n\nexport const filterSlice = createSlice({\n  name: 'filter',\n  initialState,\n  reducers: {\n    updateFilter: (state, action: PayloadAction<any>) => {\n      state.filterData = [...state.filterData, ...action.payload];\n    },\n  },\n});\n\nexport const {updateFilter} = filterSlice.actions;\nexport const selectFilterState = (state: RootState) => state.filter.filterData;\n\nconst filterReducer = filterSlice.reducer;\n\nexport default filterReducer;\n"
  },
  {
    "path": "src/app/state-management/slices/SelectedSlice.tsx",
    "content": "import {createSlice, PayloadAction} from '@reduxjs/toolkit';\nimport {selectedTypes} from '../../../types';\n\ninterface selectedState {\n  selectedData: selectedTypes[];\n}\n\nconst initialState: selectedState = {\n  selectedData: [],\n};\n\nexport const selectedSlice = createSlice({\n  name: 'selected',\n  initialState,\n  reducers: {\n    setSelected: (state, action: PayloadAction<any>) => {\n      state.selectedData = action.payload;\n    },\n    addSelected: (state, action: PayloadAction<any>) => {\n      state.selectedData.push(action.payload);\n    },\n  },\n});\n\nexport const {setSelected, addSelected} = selectedSlice.actions;\n// export const selectedState = (state: RootState) => state.selected.selectedData;\n\nconst selectedReducer = selectedSlice.reducer;\n\nexport default selectedReducer;\n"
  },
  {
    "path": "src/app/state-management/slices/SnapshotSlice.tsx",
    "content": "import {createSlice, PayloadAction} from '@reduxjs/toolkit';\nimport generateCleanComponentAtomTree from '../../utils/cleanComponentAtomTree';\n\nconst initialState: any = {\n  snapshotHistory: [],\n  renderIndex: 0,\n  cleanComponentAtomTree: {},\n};\n\nexport const snapshotSlice = createSlice({\n  name: 'snapshotHistory',\n  initialState,\n  reducers: {\n    setSnapshotHistory: (state, action: PayloadAction<any>) => {\n      state.snapshotHistory.push(action.payload);\n    },\n    setRenderIndex: (state, action: PayloadAction<any>) => {\n      state.renderIndex = action.payload;\n    },\n    setCleanComponentAtomTree: (state, action: PayloadAction<any>) => {\n      state.cleanComponentAtomTree = generateCleanComponentAtomTree(\n        action.payload,\n      );\n    },\n  },\n});\n\nconsole.log(\n  'what snapshotSlice looks like in SnapshotSlice.tsx: ',\n  snapshotSlice,\n);\n\nexport const {setSnapshotHistory, setRenderIndex, setCleanComponentAtomTree} =\n  snapshotSlice.actions;\n\nexport default snapshotSlice.reducer;\n"
  },
  {
    "path": "src/app/state-management/slices/ThrottleSlice.tsx",
    "content": "import {createSlice, PayloadAction} from '@reduxjs/toolkit';\n\nconst initialState: any = {\n  throttleValue: '70',\n};\n\nexport const throttleSlice = createSlice({\n  name: 'throttle',\n  initialState,\n  reducers: {\n    newThrottle: (state, action: PayloadAction<string>) => {\n      state.throttleValue = action.payload;\n    },\n    resetThrottle: state => {\n      state.throttleValue = '70';\n    },\n  },\n});\n\nexport const {newThrottle, resetThrottle} = throttleSlice.actions;\n\nexport default throttleSlice.reducer;\n"
  },
  {
    "path": "src/app/state-management/slices/ZoomSlice.tsx",
    "content": "import {createSlice, PayloadAction} from '@reduxjs/toolkit';\nimport {RootState} from '../index';\n\ninterface ZoomState {\n  zoomData: any;\n}\n\nconst initialState: ZoomState = { // starting x, y positions on SVG canvas\n  zoomData: { // x: 18, y: 527, k: 0.12 - updated x: 100, y: 527, k: 0.09\n    x: 220,\n    y: 850,\n    k: 0.09,\n  },\n};\n\nexport const zoomSlice = createSlice({\n  name: 'zoom',\n  initialState,\n  reducers: {\n    updateZoomState: (state, action: PayloadAction<ZoomState>) => {\n      state.zoomData = action.payload;\n    },\n    setDefaultZoom: state => {\n      state.zoomData = initialState.zoomData;\n    },\n  },\n});\n\nexport const {updateZoomState, setDefaultZoom} = zoomSlice.actions;\nexport const selectZoomState = (state: RootState) => state.zoom.zoomData;\n\nconst zoomReducer = zoomSlice.reducer;\n\nexport default zoomReducer;\n"
  },
  {
    "path": "src/app/utils/cleanComponentAtomTree.ts",
    "content": "import {componentAtomTree} from '../../types';\n\nconst generateCleanComponentAtomTree = (\n    inputObj: componentAtomTree,\n  ): componentAtomTree => {\n    const obj = {} as componentAtomTree;\n    let counter = 0;\n    const innerClean = (inputObj: any, outputObj: any, counter: number = 0) => {\n      if (\n        (inputObj.tag === 0 || inputObj.tag === 2) &&\n        inputObj.name !== 'RecoilRoot' &&\n        inputObj.name !== 'Batcher' &&\n        inputObj.name !== 'RecoilizeDebugger' &&\n        inputObj.name !== 'CssBaseline'\n      ) {\n        // if the obj is empty, we do this\n        if (Object.keys(obj).length === 0) {\n          outputObj.children = [];\n          outputObj.name = inputObj.name;\n          outputObj.recoilNodes = inputObj.recoilNodes;\n          outputObj.tag = inputObj.tag;\n          outputObj = outputObj.children;\n        }\n        // create another conditional\n        else {\n          const deepCopy: componentAtomTree = JSON.parse(\n            JSON.stringify(inputObj),\n          );\n          deepCopy.children = [];\n          outputObj.push(deepCopy);\n          if (outputObj.length > 1) {\n            outputObj = outputObj[outputObj.length - 1].children;\n          } else {\n            outputObj = outputObj[0].children;\n          }\n        }\n      }\n      // recursive call running through the whole component atom tree -- understand this better\n      for (let i = 0; i < inputObj.children.length; i++) {\n        innerClean(inputObj.children[i], outputObj, counter);\n      }\n      return outputObj;\n    };\n    innerClean(inputObj, obj, counter);\n  \n    //ensure that the root element's actual duration is included in outObj\n    if (inputObj.actualDuration) {\n      obj.actualDuration = inputObj.actualDuration;\n    }\n  \n    // returning the new object that we create\n    return obj;\n  };\n\n  export default generateCleanComponentAtomTree;"
  },
  {
    "path": "src/app/utils/makeRelationshipLinks.ts",
    "content": "type relationships = {\n  nodes: any[];\n  links: any[];\n};\n\ntype caches = {\n  [key: string]: any;\n};\n\nconst makeRelationshipLinks = (obj: any) => {\n  const relationships: relationships = {nodes: [], links: []};\n  if (!obj) return relationships;\n\n  // relationships.nodes = makeNodes(obj);\n\n  // to help with O(1) look up for nodeKeys & target => source combinations\n  const nodeCache: caches = {};\n  const sourceTargetCache: caches = {};\n\n  // loops and push nodes into nodes array\n  relationships.nodes = Object.keys(obj).map((nodeKey, index) => {\n    nodeCache[nodeKey] = index;\n\n    // make the node object and properties\n    const objToReturn = {\n      name: obj[nodeKey].type === 'RecoilState' ? 'Atom' : 'Selector',\n      label: nodeKey,\n      id: index,\n    };\n    if (objToReturn.name === 'Atom') {\n      if (obj[nodeKey].nodeDeps.length) {\n        objToReturn.name = 'Selector';\n      }\n    }\n    return objToReturn;\n  });\n\n  // loops node Data and push links into array\n  Object.keys(obj).forEach((nodeKey, _index) => {\n    // check all nodeToNode subscriptions (atoms to selectors)\n    obj[nodeKey].nodeToNodeSubscriptions.forEach(\n      (nodeToNodeSubscription: any) => {\n        const targetSource = `${nodeCache[nodeKey]}${nodeCache[nodeToNodeSubscription]}`;\n\n        if (\n          nodeCache[nodeToNodeSubscription] &&\n          !sourceTargetCache[targetSource]\n        ) {\n          sourceTargetCache[targetSource] = true;\n          relationships.links.push({\n            source: nodeCache[nodeKey],\n            target: nodeCache[nodeToNodeSubscription],\n          });\n        }\n      },\n    );\n\n    // check all nodeDeps subscriptions (selectors to atoms)\n    obj[nodeKey].nodeDeps.forEach((nodeDeps: any) => {\n      const targetSource = `${nodeCache[nodeDeps]}${nodeCache[nodeKey]}`;\n\n      if (nodeCache[nodeDeps] && !sourceTargetCache[targetSource]) {\n        sourceTargetCache[targetSource] = true;\n        relationships.links.push({\n          source: nodeCache[nodeDeps],\n          target: nodeCache[nodeKey],\n        });\n      }\n    });\n  });\n\n  return relationships;\n};\n\nexport default makeRelationshipLinks;\n"
  },
  {
    "path": "src/app/utils/makeTreeConversion.ts",
    "content": "type makeTreeObj = {\n  [name: string]: any;\n};\n\nconst makeTree = (obj: any) => {\n  // function that parses and refactors snapshotHistory into an object d3 can understand\n\n  if (!obj) return;\n\n  let result: any[] = [];\n  let keys = Object.keys(obj);\n  keys.forEach(key => {\n    let newObj: makeTreeObj = {};\n    newObj['name'] = key;\n    // obj[key] is a nested object so recurse\n    if (typeof obj[key] === 'object' && !Array.isArray(obj[key]) && obj[key]) {\n      newObj['children'] = makeTree(obj[key]);\n    } else if (Array.isArray(obj[key])) {\n      // obj[key] is an array\n      newObj['children'] = [];\n      obj[key].forEach((_el: any, i: number) => {\n        newObj.children.push({\n          name: `${key}[${i}]`,\n          value: obj[key][i],\n        });\n      });\n    } else {\n      // obj[key] is a primitive\n      newObj.children = [\n        {\n          name: JSON.stringify(obj[key]),\n        },\n      ];\n    }\n\n    result.push(newObj);\n  });\n  return result;\n};\n\nexport default makeTree;\n"
  },
  {
    "path": "src/extension/background.ts",
    "content": "// TO CONSOLE LOG IN THIS FILE, we need to go to chrome://extensions/ and click inspect on the actual devTOOL!\n\n// Message Interface\ninterface Msg {\n  action: string;\n  tabId?: string;\n  payload?: object;\n  test?: number;\n}\n\ninterface Connections {\n  [tabId: string]: any;\n}\n\n// once background-script start, start with cleared connections\nconst connections: Connections = {};\n\n// once background starts, start with cleared local storage\n// this only happens when we open chrome again, not on refresh\nchrome.storage.local.clear(function (): void {\n  chrome.storage.local.get(null, function (result): void {});\n});\n\n// LISTEN for initial connection from dev tool\n// runs when devtool is connected\nchrome.runtime.onConnect.addListener(port => {\n  const devToolsListener = (msg: Msg, port: object) => {\n    console.log(\n      'in the onConnect of background script IN BG SCRIPT ',\n      msg,\n      port,\n    );\n    const {tabId, action} = msg;\n\n    switch (action) {\n      case 'devToolInitialized':\n        connections[tabId] = port;\n        // read and send back to dev tool current local storage for corresponding tabId & port\n        console.log('local chrome storage: ', chrome.storage.local);\n        chrome.storage.local.get(null, function (result) {\n          console.log('chrome.storage.local.get result: ', result);\n          connections[tabId].postMessage({\n            action: 'recordSnapshot',\n            payload: result[tabId],\n          });\n          console.log('connections tabId: ', connections[tabId]);\n          console.log('connections: ', connections);\n          console.log('tabId: ', tabId);\n        });\n        break;\n\n      case 'snapshotTimeTravel':\n        if (tabId) {\n          // if msg tabId provided, send time travel snapshot history to content-script\n          chrome.tabs.sendMessage(Number(tabId), msg);\n        }\n        break;\n\n      case 'mouseover':\n        if (tabId) {\n          chrome.tabs.sendMessage(Number(tabId), msg);\n        }\n        break;\n\n      case 'persistState':\n        if (tabId) {\n          // if msg tabId provided, send persistState command to content-script\n          chrome.tabs.sendMessage(Number(tabId), msg);\n        }\n        break;\n      case 'throttleEdit':\n        if (tabId) {\n          console.log('doing a throttle edit');\n          chrome.tabs.sendMessage(Number(tabId), msg);\n        }\n        // window.postMessage({action: 'throttleChange'}, throttler);\n        break;\n\n      default:\n        break;\n    }\n  };\n\n  // BEGINS listening to messages from port\n  port.onMessage.addListener(devToolsListener);\n\n  // ENDS listening to messages from port\n  port.onDisconnect.addListener(port => {\n    // Removes listener\n    port.onMessage.removeListener(devToolsListener);\n\n    // Removes reference to devtool instance when the devtool is closed\n    for (const prop in connections) {\n      if (connections[prop] === port) {\n        delete connections[prop];\n        break;\n      }\n    }\n  });\n});\n\n// Listens to message from the Recoilize module\nchrome.runtime.onMessage.addListener((msg, sender) => {\n  // Error handling if there isn't a proper tabId\n  if (!sender.tab) return;\n\n  console.log(\"background.ts chromeruntime msg: \", msg);\n\n  // Grabs tab id from content script and converts it to a string\n  const tabId = `${sender.tab.id}`;\n\n  const {action} = msg;\n\n  switch (action) {\n    // Listens to new snapshots (state changes) from module, stores in local storage and sends to dev tool if port is opened\n    case 'recordSnapshot':\n      //console.log('chrome.runtime.onMessage with case: recordSnapshot');\n\n      // Next snapshot from the msg payload\n      const snapshot = msg.payload;\n      //console.log('snapshot: ', snapshot);\n\n      // Get current snapshot history from local storage\n      chrome.storage.local.get([tabId], function (result) {\n        // Grab the current snapshot history from local storage\n        const tabIdSnapshotHistory = result[tabId] ? [...result[tabId]] : [];\n\n        // Grab the last (most recent) snapshot from the history\n        const lastSnapshot =\n          tabIdSnapshotHistory.length > 0\n            ? tabIdSnapshotHistory[tabIdSnapshotHistory.length - 1]\n            : {};\n\n        // Merge the changed atoms from the new state with the old state\n        // the old state should have the list of ALL atoms, not just the ones that changed, so we want to preserve a list of all atoms.\n        tabIdSnapshotHistory.push(Object.assign({}, lastSnapshot, snapshot));\n\n        // Set local storage with updated snapshotHistory\n        chrome.storage.local.set({[tabId]: tabIdSnapshotHistory}, function () {\n          // ONLY if there is a port connection with the current tabId\n          if (connections[tabId]) {\n            // Send to dev tool\n\n            connections[tabId].postMessage({\n              action: 'recordSnapshot',\n              payload: tabIdSnapshotHistory,\n            });\n          }\n        });\n      });\n      break;\n\n    // const devToolData = createDevToolDataObject(\n    //   initialFilteredSnapshot,\n    //   indexDiff,\n    //   atomsAndSelectorsMsg,\n    //   selectorsObject,\n    // );\n\n    // If the module is loaded for first time or refreshed reset snapshot History and send initial payload\n    case 'moduleInitialized':\n      //console.log('module has been initialized IN BG SCRIPT', msg);\n      const tabIdSnapshotHistory = [msg.payload];\n      // set tabId within local storage to initial snapshot sent from module\n      chrome.storage.local.set({[tabId]: tabIdSnapshotHistory}, function () {\n        // if tabId is has opened dev tool port, send snapshotHistory to dev tool.\n        if (connections[tabId]) {\n          connections[tabId].postMessage({\n            action: 'recordSnapshot',\n            payload: tabIdSnapshotHistory,\n          });\n        }\n      });\n      break;\n    case 'persistSnapshots':\n      // getting the array of filtered snapshots that exists on locoal storage\n      chrome.storage.local.get(tabId, function (result) {\n        connections[tabId].postMessage({\n          action: 'recordSnapshot',\n          payload: result[tabId],\n        });\n      });\n\n    default:\n      break;\n  }\n});\n\n// when the tab is closed reset local storage for that specific tabId\nchrome.tabs.onRemoved.addListener(tabId => {\n  // we should only do this when tab closes not when port closes.\n  chrome.storage.local.remove([`${tabId}`], function () {\n    chrome.storage.local.get(null, function (result) {});\n  });\n});\n"
  },
  {
    "path": "src/extension/build/devtools.html",
    "content": "<!-- The startup page which will link to the startup Javascript. -->\n<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\" />\n    <title>⚛ Recoilize</title>\n  </head>\n  <body>\n    <script src=\"devtools.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "src/extension/build/devtools.js",
    "content": "// The initial script that loads the extension into the DevTools panel.\nchrome.devtools.panels.create(\n  'Recoilize', // title of devtool panel\n  '../assets/covalent-recoil-logo.jpg', // icon of devtool panel\n  'panel.html', // html of devtool panel\n);\n"
  },
  {
    "path": "src/extension/build/diff.css",
    "content": ".jsondiffpatch-delta {\n  font-family: 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', Monaco, Courier, monospace;\n  font-size: 12px;\n  margin: 0;\n  padding: 0 0 0 12px;\n  display: inline-block;\n}\n.jsondiffpatch-delta pre {\n  font-family: 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', Monaco, Courier, monospace;\n  font-size: 12px;\n  margin: 0;\n  padding: 0;\n  display: inline-block;\n}\nul.jsondiffpatch-delta {\n  list-style-type: none;\n  padding: 0 0 0 20px;\n  margin: 0;\n}\n.jsondiffpatch-delta ul {\n  list-style-type: none;\n  padding: 0 0 0 20px;\n  margin: 0;\n}\n.jsondiffpatch-added .jsondiffpatch-property-name,\n.jsondiffpatch-added .jsondiffpatch-value pre,\n.jsondiffpatch-modified .jsondiffpatch-right-value pre,\n.jsondiffpatch-textdiff-added {\n  background: #5A6C46;\n}\n.jsondiffpatch-deleted .jsondiffpatch-property-name,\n.jsondiffpatch-deleted pre,\n.jsondiffpatch-modified .jsondiffpatch-left-value pre,\n.jsondiffpatch-textdiff-deleted {\n  background: #7E5C69;\n  text-decoration: line-through;\n}\n.jsondiffpatch-unchanged,\n.jsondiffpatch-movedestination {\n  color: gray;\n}\n.jsondiffpatch-unchanged,\n.jsondiffpatch-movedestination > .jsondiffpatch-value {\n  transition: all 0.5s;\n  -webkit-transition: all 0.5s;\n  overflow-y: hidden;\n}\n.jsondiffpatch-unchanged-showing .jsondiffpatch-unchanged,\n.jsondiffpatch-unchanged-showing .jsondiffpatch-movedestination > .jsondiffpatch-value {\n  max-height: 100px;\n}\n.jsondiffpatch-unchanged-hidden .jsondiffpatch-unchanged,\n.jsondiffpatch-unchanged-hidden .jsondiffpatch-movedestination > .jsondiffpatch-value {\n  max-height: 0;\n}\n.jsondiffpatch-unchanged-hiding .jsondiffpatch-movedestination > .jsondiffpatch-value,\n.jsondiffpatch-unchanged-hidden .jsondiffpatch-movedestination > .jsondiffpatch-value {\n  display: block;\n}\n.jsondiffpatch-unchanged-visible .jsondiffpatch-unchanged,\n.jsondiffpatch-unchanged-visible .jsondiffpatch-movedestination > .jsondiffpatch-value {\n  max-height: 100px;\n}\n.jsondiffpatch-unchanged-hiding .jsondiffpatch-unchanged,\n.jsondiffpatch-unchanged-hiding .jsondiffpatch-movedestination > .jsondiffpatch-value {\n  max-height: 0;\n}\n.jsondiffpatch-unchanged-showing .jsondiffpatch-arrow,\n.jsondiffpatch-unchanged-hiding .jsondiffpatch-arrow {\n  display: none;\n}\n.jsondiffpatch-value {\n  display: inline-block;\n}\n.jsondiffpatch-property-name {\n  display: inline-block;\n  padding-right: 5px;\n  vertical-align: top;\n}\n.jsondiffpatch-property-name:after {\n  content: ': ';\n}\n.jsondiffpatch-child-node-type-array > .jsondiffpatch-property-name:after {\n  content: ': [';\n}\n.jsondiffpatch-child-node-type-array:after {\n  content: '],';\n}\ndiv.jsondiffpatch-child-node-type-array:before {\n  content: '[';\n}\ndiv.jsondiffpatch-child-node-type-array:after {\n  content: ']';\n}\n.jsondiffpatch-child-node-type-object > .jsondiffpatch-property-name:after {\n  content: ': {';\n}\n.jsondiffpatch-child-node-type-object:after {\n  content: '},';\n}\ndiv.jsondiffpatch-child-node-type-object:before {\n  content: '{';\n}\ndiv.jsondiffpatch-child-node-type-object:after {\n  content: '}';\n}\n.jsondiffpatch-value pre:after {\n  content: ',';\n}\nli:last-child > .jsondiffpatch-value pre:after,\n.jsondiffpatch-modified > .jsondiffpatch-left-value pre:after {\n  content: '';\n}\n.jsondiffpatch-modified .jsondiffpatch-value {\n  display: inline-block;\n}\n.jsondiffpatch-modified .jsondiffpatch-right-value {\n  margin-left: 5px;\n}\n.jsondiffpatch-moved .jsondiffpatch-value {\n  display: none;\n}\n.jsondiffpatch-moved .jsondiffpatch-moved-destination {\n  display: inline-block;\n  background: #ffffbb;\n  color: #888;\n}\n.jsondiffpatch-moved .jsondiffpatch-moved-destination:before {\n  content: ' => ';\n}\nul.jsondiffpatch-textdiff {\n  padding: 0;\n}\n.jsondiffpatch-textdiff-location {\n  color: #bbb;\n  display: inline-block;\n  min-width: 60px;\n}\n.jsondiffpatch-textdiff-line {\n  display: inline-block;\n}\n.jsondiffpatch-textdiff-line-number:after {\n  content: ',';\n}\n.jsondiffpatch-error {\n  background: red;\n  color: white;\n  font-weight: bold;\n}\n"
  },
  {
    "path": "src/extension/build/manifest.json",
    "content": "{\n  \"name\": \"Recoilize_Testing\",\n  \"version\": \"3.0.0\",\n  \"devtools_page\": \"devtools.html\",\n  \"description\": \"A Chrome extension that helps debug Recoil applications by memorizing the state of components with every render.\",\n  \"manifest_version\": 2,\n  \"content_security_policy\": \"script-src 'self' 'unsafe-eval' ; object-src 'self'\",\n  \"icons\": {\n    \"16\": \"assets/Recoilize-v2.png\",\n    \"48\": \"assets/Recoilize-v2.png\",\n    \"128\": \"assets/Recoilize-v2.png\"\n  },\n  \"permissions\": [\"storage\"],\n  \"background\": {\n    \"scripts\": [\"bundles/background.bundle.js\"],\n    \"persistent\": false\n  },\n  \"content_scripts\": [\n    {\n      \"matches\": [\"<all_urls>\"],\n      \"js\": [\"bundles/content.bundle.js\"]\n    }\n  ]\n}\n"
  },
  {
    "path": "src/extension/build/panel.html",
    "content": "<!-- The HTML displayed in the DevTools panel. -->\n<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <link rel=\"stylesheet\" href=\"stylesheet.css\">\n  <link rel=\"stylesheet\" href=\"diff.css\">\n</head>\n\n<body>\n  <div id=\"root\"></div>\n  <!-- link to the bulk of our app functionality ex. panel.js/index.js  -->\n  <script type=\"text/javascript\" src=\"bundles/app.bundle.js\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "src/extension/build/stylesheet.css",
    "content": "/* GLOBAL CSS */\nhtml,\nbody {\n  color: #989898;\n  margin: 0;\n  height: 100%;\n  background-color: #212121;\n}\n::-webkit-scrollbar {\n  background-color: transparent;\n}\n::-webkit-scrollbar-track {\n  background-color: rgba(255, 255, 255, 0.1);\n}\n::-webkit-scrollbar-thumb {\n  background-color: rgba(255, 255, 255, 0.15);\n  border: 2px solid rgba(255, 255, 255, 0.1);\n  border-radius: 7px;\n}\n::-webkit-scrollbar-thumb:hover {\n  background-color: rgba(255, 255, 255, 0.25);\n  border: 2px solid rgba(255, 255, 255, 0.2);\n}\n\n/* remove outlines on input boxes and buttons in all pages of the app */\ninput,\nbutton {\n  outline: none;\n}\n/* basic css for the button and button:hover for uniform look */\nbutton {\n  border: 1px solid #989898;\n  border-radius: 5px;\n  color: #e6e6e6;\n  background: none;\n}\nbutton:hover {\n  color: white;\n  border: 1px solid white;\n  background-color: #212121;\n  text-emphasis: bold;\n}\n\n/* APP -> MAIN CONTAINER */\n.App,\n#root {\n  width: 100%;\n  height: 100%;\n}\n\n.notFoundContainer {\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  width: 100%;\n  height: 100%;\n  text-align: center;\n  font-size: 1.2rem;\n}\n.notFoundContainer p {\n  margin: 10px 20px;\n}\n.notFoundContainer a {\n  color: #3578e5;\n}\n\n.logo {\n  width: 100px;\n  height: auto;\n}\n\n/* MAIN CONTAINER ->\n * 0) SNAPSHOTS CONTAINER,\n * 1) VISUAL CONTAINER,\n */\n.MainContainer {\n  height: 100%;\n  width: 100%;\n  display: grid;\n  grid-template-columns: min-content 2fr;\n  grid-template-rows: 8fr 1fr;\n  grid-template-areas:\n    'actions states'\n    'travel travel'\n    'buttons buttons';\n  overflow: hidden;\n}\n\n/* Time Travel Container */\n.travel-container {\n  width: 100%;\n  grid-area: travel;\n  background: linear-gradient(\n\t\t90deg,\n\t\trgba(41, 41, 41, 1) 0%,\n\t\trgba(51, 51, 51, 1) 50%,\n\t\trgba(41, 41, 41, 1) 100%\n\t);\n  position: absolute;\n  bottom: 0px;\n\tdisplay: flex;\n  flex-direction: column;\n}\n\n.main-slider {\n  /* if changed, other css attributes will also be affected:\n  position: absolute;\n  bottom: 0px; */\n  display:inline-flex;\n  /* side effect */\n  /* creates a black box above slider (not covering snapshot list) */\n  margin-top: 10px;\n}\n\n#slider-start-button {\n  width: 100px;\n  height: 25px;\n  margin: 0px 5px 5px 10px;\n}\n\n.backfor-button {\n  width: 30px;\n  height: 25px;\n  margin: 0px 10px 10px 5px;\n  /* margin: 0 0 1% 1%; */\n}\n\n.rc-slider {\n\tposition: relative;\n\twidth: calc(100% - 170px);\n\tmargin: 10px;\n\tborder-radius: 6px;\n\t-ms-touch-action: none;\n\ttouch-action: none;\n\tbox-sizing: border-box;\n\t-webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\n.rc-slider * {\n\tbox-sizing: border-box;\n\t-webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\n.rc-slider-rail {\n\tposition: absolute;\n\twidth: 100%;\n\tbackground-color: #ebf2fa;\n\theight: 4px;\n\tborder-radius: 6px;\n}\n\n.rc-slider-track {\n\tposition: absolute;\n\tleft: 0;\n\theight: 4px;\n\tborder-radius: 6px;\n\tbackground-color: #ff5470;\n}\n\n.rc-slider-handle {\n\tposition: absolute;\n\tmargin-left: 5px;\n\tmargin-top: -10px;\n  padding-left: 10px;\n\twidth: 20px;\n\theight: 20px;\n\tcursor: pointer;\n\tcursor: -webkit-grab;\n\tcursor: grab;\n\tborder-radius: 50%;\n\tbackground-color: #bd4f6c;\n  background-image: linear-gradient(326deg, #bd4f6c 0%, #d7816a 74%);\n\t-ms-touch-action: pan-x;\n\ttouch-action: pan-x;\n}\n\n/* Buttons Container */\n.buttons_container {\n  grid-area: buttons;\n  display: flex;\n}\n\n#docs_button {\n  width: 100px;\n  height: 25px;\n  margin: 0px 5px 15px 10px;\n}\n\n/* SNAPSHOT CONTAINER -> SNAPSHOTLIST COMPONENT */\n.SnapshotsContainer {\n  /* adjustment for snapshot list & scrollbar */\n  height: calc(100% - 40px); \n  display: flex;\n  flex-direction: column;\n  justify-content: stretch;\n  flex-shrink: 1;\n  min-width: 170px;\n  background-color: #2d2d2d;\n  text-align: center;\n  color: #e6e6e6;\n  grid-area: actions;\n  overflow: auto;\n}\n\n.save-series-button {\n  width: 50%;\n  margin-left: 45px;\n  margin-bottom: 5px;\n}\n\n.removeSnapshot {\n  background-color: transparent;\n  border: none;\n  color: white;\n  /* outline: none; */\n  font-size: 0.7em;\n}\n\n.removeSnapshot:hover {\n  opacity: 50%;\n}\n.removeSnapshot:active {\n  opacity: 50%;\n  background-color: black;\n}\n\n.clear-buttons {\n  display: flex;\n  padding-bottom: 1em;\n  border-bottom: 1px solid #646464;\n}\n\n#clear-snapshots-title {\n  padding: 1em;\n  font-size: 12px;\n  font-weight: bold;\n}\n\n#prevClr {\n  margin-left: 0.9em;\n  width: 5em;\n  cursor: pointer;\n}\n\n#fwrdClr {\n  margin-right: 0.9em;\n  width: 5em;\n  margin-left: auto;\n  cursor: pointer;\n}\n\n/* SNAPSHOTLIST COMPONENT */\n.SnapshotsList {\n  height: calc(100% - 40px);\n  overflow: auto;\n  border-style: none;\n}\n\n.individualSnapshot {\n  list-style-type: none;\n  display: flex;\n  flex-grow: 1;\n  padding: 10px;\n  padding-left: 30px;\n  padding-right: 20px;\n  border: none;\n  justify-content: space-between;\n}\n.individualSnapshot:hover {\n  background-color: #212121;\n}\n\n.timeTravelButton {\n  color: #989898;\n}\n.timeTravelButton:focus {\n  color: #e6e6e6;\n  background-color: #212121;\n}\n\n/* VISUAL CONTAINER ->\n * 0) NAVBAR,\n * 1) DIFF,\n * 2) TREE,\n * 3) VISUALIZER,\n * 4) NETWORK,\n * 5) ATOMCOMPONENTVISUAL CONTAINER,\n * 6) SETTINGS,\n */\n.VisualContainer {\n  /* height && margin-top */\n  /* affects how scroll bar looks within State Diff && State Tree tabs */\n  height: calc(100% - 33px - 20px);\n  margin-top: 33px;\n  margin-bottom: 20px;\n  width: calc(100% - 3px);\n  display: flex;\n  flex-grow: 8;\n  flex-direction: column;\n  border-left: 2px solid #484848;\n  grid-area: states;\n  color: #e6e6e6;\n  /* overflow: auto; is important for visual consistency */\n  /* affects initial loading of NavBar && State Diff */\n  /* affects scrolling of State Diff && State Tree */\n  overflow: auto;\n  /* overflow-y: auto messes up the height styling */\n}\n\n/* NAVBAR COMPONENT */\n.NavBar {\n  width: calc(100% - 174px);\n  display: flex;\n  flex-direction: row;\n  text-align: center;\n  border-right: 2px solid #484848;\n  border-bottom: 2px solid #484848;\n  background-color: #2d2d2d;\n  position: absolute;\n  top: 0px;\n  z-index: 1;\n}\n\n.navBarButtons {\n  /* flex-grow: 1; */\n  color: #989898;\n  background-color: #2d2d2d;\n  border: none;\n  padding: 8px;\n  padding-left: 10px;\n  padding-right: 10px;\n}\n.navBarButtons:hover {\n  color: #e6e6e6;\n  border: none;\n}\n\n/* DIFF COMPONENT */\n.Diff {\n  /* 33px is height of navbar */\n  height: calc(100% - 33px); \n  /* height affects how scrollbar works/looks in State Diff tab */\n  border-style: none;\n  display: flex;\n  flex-direction: column;\n  padding: 10px;\n  padding-bottom: 20px;\n  overflow: auto;\n}\n\n.toggleDiv {\n  /* display: flex;\n  justify-content: flex-end; */\n  overflow: auto;\n}\n\n#raw {\n  cursor: pointer;\n}\n\n.rawToggle {\n  background: none;\n  border: none !important;\n}\n\n/* TREE COMPONENT */\n.Tree {\n  /* adjustment for height of navbar (33px) */\n  height: calc(100% - 33px - 20px); \n  width: 90%;\n  /* height & width affect how scrollbar works/looks in State Tree tab */\n  display: flex;\n  flex-direction: column;\n  padding: 10px;\n  /* overflow: auto; */\n  /* not the one needing overflow scrolling */\n}\n\n.json-tree {\n  /* adjustment for height of navbar (33px) */\n  height: calc(100% - 33px - 25px); \n  margin-bottom: 20px;\n  padding-bottom: 20px;\n  width: 100%;\n  /* padding-bottom: 20px; */\n  background-color: none;\n  list-style: none;\n  /* not the one needing overflow scrolling */\n}\n\n/* VISUALIZER COMPONENT  */\n/* Flame Graph */\n#metricsWrapper {\n  height: calc(100% - 33px - 50px);\n  /* width: 98% fixes moving-bars issue */\n  width: 98%;\n  overflow: hidden;\n}\n\n.RankedGraph,\n#canvas { /* #canvas --> Component Graph */\n  flex-grow: 1;\n  width: 100%;\n  overflow: hidden;\n}\n\n#stateGraphContainer {\n  /* 33px is height of navbar */\n  height: calc(100% - 33px);\n  width: 100%;\n  overflow: auto;\n}\n\n/* svg {\n  height: calc(100% - 33px - 50px);\n} */\n\n.svg-container {\n  display: inline-block;\n  position: relative;\n  width: 100%;\n  vertical-align: top;\n  overflow: hidden;\n}\n\n.svg-content-responsive {\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  position: absolute;\n  top: 10px;\n  left: 0;\n}\n\n.graphContainer {\n  padding: 10px;\n}\n\n.graphButton {\n  color: #989898;\n}\n\n.graphButton:hover {\n  color: #e6e6e6;\n  border: 1px solid white;\n}\n\n.graphButton:focus {\n  color: #e6e6e6;\n  background-color: #212121;\n}\n\n/* NETWORK COMPONENT */\n.networkContainer {\n  height: 100%;\n  width: 100%;\n}\n\n.Network {\n  position: relative;\n  height: 100%;\n  width: 100%;\n}\n\n#networkCanvas {\n  position: relative;\n  height: 100%;\n  width: 100%;\n}\n\n#networkSearch {\n  width: 135px;\n  grid-row: 1;\n}\n\n.LegendContainer {\n  display: flex;\n  flex-direction: column;\n  align-items: flex-start;\n  justify-items: flex-start;\n}\n\n.AtomNetworkLegend {\n  position: fixed;\n  top: 58px;\n  left: 179px;\n  padding-left: 5px;\n  padding-top: 5px;\n  display: flex;\n  flex-direction: column;\n  align-items: flex-start;\n}\n\n.graph-slider {\n  position: relative;\n  top: 0;\n  left: 0;\n}\n\n#spacingSliders {\n  position: fixed;\n  top: 72px;\n  left: 375px;\n  width: 150px;\n  height: 70px;\n  display: flex;\n  flex-direction: column;\n  justify-content: space-between;\n  color: #e6e6e6;\n}\n\n.sliderContainer {\n  display: flex;\n  justify-content: space-between;\n}\n\n.sliderLabel {\n  width: 10px;\n  padding: 5px;\n}\n\n.siblingSlider, .parentChildSlider {\n  -webkit-appearance: none;\n  border-radius: 5px;\n  width: 125px;\n  background: none;\n}\n\n.siblingSlider::-webkit-slider-runnable-track,\n.parentChildSlider::-webkit-slider-runnable-track {\n  background-color: #e6e6e6;\n  border-radius: 5px;\n  height: 4px;\n}\n\n.siblingSlider::-webkit-slider-thumb,\n.parentChildSlider::-webkit-slider-thumb {\n  -webkit-appearance: none;\n  background-color: #bd4f6c;\n  background-image: linear-gradient(326deg, #bd4f6c 0%, #d7816a 74%);\n  border: none;\n  height: 20px;\n  width: 20px;\n  border-radius: 50%;\n  cursor: grab;\n  margin-top: -8px;\n  padding-left: 10px;\n}\n\n.graphBtnContainer {\n  position: fixed;\n  top: 150px;\n  left: 375px;\n  width: 150px;\n  height: 65px;\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  padding: 5px 15px;\n}\n\n.graphBtn {\n  display: flex;\n}\n\n#orientationBtn {\n  width: 65px;\n  height: 50px;\n  background: transparent;\n  border: none !important;\n  margin-right: 5px;\n  cursor: pointer;\n  padding-right: 5px;\n  color: #989898;\n}\n\n.AtomDiv {\n  display: grid;\n  grid-row: 2;\n  grid-template-columns: 1fr 1fr;\n  align-items: center;\n  gap: 1em;\n}\n\n.SelectorDiv {\n  display: grid;\n  grid-row: 3;\n  grid-template-columns: 1fr 1fr;\n  align-items: center;\n  gap: 1em;\n}\n\n.AtomDiv:hover,\n.SelectorDiv:hover,\n.AtomP:hover,\n.SelectorP:hover {\n  opacity: 90% !important;\n  cursor: pointer;\n  z-index: 1;\n}\n\n.AtomDiv:active,\n.SelectorDiv:active,\n.AtomP:active,\n.SelectorP:active {\n  opacity: 50% !important;\n}\n\n.AtomLegend {\n  display: inline-block;\n  background-color: #9580ff;\n  width: 20px;\n  height: 20px;\n  border: 1px solid #9580ff;\n  border-radius: 20px;\n  margin: 0px;\n  flex-grow: 1;\n}\n\n.SelectorLegend {\n  display: inline-block;\n  background-color: #ff80bf;\n  width: 20px;\n  height: 20px;\n  border: 1px solid #ff80bf;\n  border-radius: 20px;\n  margin: 0px;\n  flex-grow: 1;\n}\n\n.AtomDropdown,\n.SelectorDropdown {\n  display: grid;\n  grid-row: 4;\n  border: 0.1em solid rgb(43, 34, 34);\n}\n\n.atom-class,\n.selector-class,\n.AtomListItem,\n.SelectorListItem {\n  cursor: pointer;\n}\n\n.SelectorDropdown p:active,\n.AtomDropdown p:active {\n  opacity: 80%;\n}\n\n/* ATOMCOMPONENTVISUAL CONTAINER ->\n * 0) ATOMCOMPONENTVISUAL COMPONENT\n * 1) ATOMSELECTORLEGEND COMPONENT\n */\n.Component {\n  /* position: relative; */\n  height: 100%;\n  width: 100%;\n}\n\n/* ATOMCOMPONENTVISUAL COMPONENT */\n.AtomComponentVisual,\n#canvas {\n  position: relative;\n  height: 100%;\n  width: 100%;\n}\n\n.hoverInfo {\n  height: min-content;\n  width: max-content;\n  border: 0;\n  border-radius: 5px;\n  position: fixed;\n  background-color: #484848;\n  font-size: 100%;\n  padding: 0 1%;\n  z-index: 1;\n}\n\n#componentGraph {\n  transform-origin: 15% -90%;\n}\n\n#fixedButton {\n  width: 65px;\n  height: 50px;\n  background: transparent;\n  border: none !important;\n  margin-right: 5px;\n  cursor: pointer;\n  color: #989898;\n}\n\n.RecoilSearch {\n  position: fixed;\n  top: 58px;\n  left: 179px;\n  display: grid;\n  grid-template-columns: 30px 30px;\n  grid-template-rows: 30px 30px 30px 1fr;\n  align-items: center;\n  padding-left: 5px;\n  padding-top: 5px;\n}\n\n.AtomNetworkLegend {\n  position: fixed;\n  top: 58px;\n  left: 179px;\n  display: grid;\n  grid-template-columns: 30px 30px;\n  grid-template-rows: 30px 30px 30px 1fr;\n  align-items: center;\n  padding-left: 5px;\n  padding-top: 5px;\n}\n\n.AtomNetworkLegendWithSearch {\n  position: fixed;\n  top: 100px;\n  left: 179px;\n  display: grid;\n  grid-template-columns: 30px 30px;\n  grid-template-rows: 30px 30px 30px 1fr;\n  align-items: center;\n  padding-left: 5px;\n  padding-top: 5px;\n}\n\n.AtomLegend {\n  display: inline-block;\n  background-color: #9580ff;\n  width: 20px;\n  height: 20px;\n  border: 1px solid #9580ff;\n  border-radius: 20px;\n  margin: 0px;\n}\n\n.SelectorLegend {\n  display: inline-block;\n  background-color: #ff80bf;\n  width: 20px;\n  height: 20px;\n  border: 1px solid #ff80bf;\n  border-radius: 20px;\n  margin: 0px;\n}\n\n.bothLegend {\n  display: inline-block;\n  background-color: springgreen;\n  width: 20px;\n  height: 20px;\n  border: 1px solid springgreen;\n  border-radius: 20px;\n  margin: 0px;\n}\n\n#atomDrop,\n#selectorDrop {\n  display: grid;\n  grid-row: 5;\n}\n\n.selectorSelected:hover {\n  color: #ff80bf;\n  background-color: rgb(240, 240, 162);\n  border-color: white;\n  width: 120px;\n  opacity: 100%;\n}\n\n.atomSelected:hover {\n  color: #9580ff;\n  background-color: rgb(240, 240, 162);\n  border-color: white;\n  width: 120px;\n  opacity: 100%;\n}\n\n.atomSelected {\n  color: #9580ff;\n  background-color: rgb(240, 240, 162);\n  border-color: white;\n  width: 120px;\n  opacity: 100%;\n}\n\n.atomNotSelected {\n  color: #9580ff;\n  border-color: white;\n  width: 120px;\n  opacity: 30%;\n}\n\n.atomDropDown {\n  color: #9580ff;\n  border-color: white;\n  width: 120px;\n}\n\n.atomLegendDefault {\n  color: #9580ff;\n  border-color: white;\n  width: 120px;\n}\n\n.selectorSelected {\n  color: #ff80bf;\n  background-color: rgb(240, 240, 162);\n  border-color: white;\n  width: 120px;\n  opacity: 100%;\n}\n\n.selectorNotSelected {\n  color: #ff80bf;\n  border-color: white;\n  width: 120px;\n  opacity: 30%;\n}\n\n.selectorDropDown {\n  color: #ff80bf;\n  border-color: white;\n  width: 120px;\n}\n\n.selectorLegendDefault {\n  color: #ff80bf;\n  border-color: white;\n  width: 120px;\n}\n\n.bothLegendDefault {\n  color: springgreen;\n  border-color: white;\n  width: 120px;\n}\n\n.suspenseLegend {\n  display: inline-block;\n  background: transparent;\n  width: 20px;\n  height: 20px;\n  border: 3px solid red;\n  border-radius: 20px;\n  margin: 0px;\n}\n\n.dropDownButtonDiv {\n  margin: 5px;\n}\n\n.LegendButtons {\n  display: flex;\n  flex-direction: column;\n  margin: 5px;\n  margin-top: 60px;\n}\n\n/* ATOMSELECTORLEGEND COMPONENT */\n.AtomSelectorLegend {\n  background-color: rgba(45, 45, 45, 0.65);\n  position: fixed;\n  right: 0;\n  border: 2px solid #484848;\n  top: 2.5rem;\n  width: 250px;\n  max-height: 400px;\n  overflow-y: auto;\n  text-align: center;\n  margin-right: 5px;\n}\n\n.atomLi,\n.selectorLi {\n  list-style-type: none;\n  padding: 10px;\n  padding-left: 20px;\n  padding-right: 20px;\n  border: none;\n  word-wrap: break-word;\n  font-size: 14px;\n  z-index: 1;\n}\n.atomLi:hover,\n.selectorLi:hover {\n  background-color: #212121;\n  z-index: 1;\n}\n\n.minimizeButton {\n  margin: 5px;\n  border-radius: 7px;\n  width: 80px;\n  height: 22px;\n  cursor: pointer;\n  font-weight: bold;\n}\n\n.minimize {\n  display: none;\n}\n\n/* SETTINGS CONTAINER ->\n * 0) ATOM SETTINGS,\n * 1) STATE SETTINGS,\n * 2) THROTTLE SETTINGS,\n */\n.Settings {\n  height: calc(100% - 33);\n  overflow: auto;\n  border-style: none;\n  display: flex;\n  flex-direction: column;\n  padding: 10px;\n  padding-bottom: 20px;\n}\n\n/* ! Extra for settings */\n/* Dropdown Button */\n.dropbtn {\n  background-color: #4caf50;\n  color: white;\n  padding: 16px;\n  font-size: 16px;\n  border: none;\n  cursor: pointer;\n}\n/* Dropdown button on hover & focus */\n.dropbtn:hover,\n.dropbtn:focus {\n  background-color: #3e8e41;\n}\n\n/* The container <div> - needed to position the dropdown content */\n.dropdown {\n  position: relative;\n  display: inline-block;\n  color: white;\n}\n\n/* Dropdown Content (Hidden by Default) */\n.dropdown-content {\n  display: none;\n  position: absolute;\n  background-color: #f9f9f9;\n  min-width: 160px;\n  box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);\n}\n/* Links inside the dropdown */\n.dropdown-content a {\n  color: black;\n  padding: 12px 16px;\n  text-decoration: none;\n  display: block;\n}\n/* Change color of dropdown links on hover */\n.dropdown-content a:hover {\n  background-color: #f1f1f1;\n}\n\n/* Show the dropdown menu (use JS to add this class to the .dropdown-content container when the user clicks on the dropdown button) */\n.show {\n  display: block;\n}\n\n/* STATE SETTINGS COMPONENT */\n.persistContainer {\n  display: table;\n}\n\n.switch {\n  display: inline-block;\n  height: 24px;\n  position: relative;\n  width: 45px;\n}\n.switch input {\n  display: none;\n}\n\n.persistText {\n  display: table-cell;\n  vertical-align: middle;\n  font-size: 14px;\n  padding-left: 10px;\n}\n\n.slider {\n  background-color: #ccc;\n  cursor: pointer;\n  position: absolute;\n  top: 0;\n  bottom: 0;\n  left: 0;\n  right: 0;\n  transition: 0.4s;\n}\n.slider:before {\n  background-color: #fff;\n  bottom: 4px;\n  content: '';\n  height: 15px;\n  left: 4px;\n  position: absolute;\n  transition: 0.4s;\n  width: 15px;\n}\n\ninput:checked + .slider {\n  background-color: #0096fb;\n}\ninput:checked + .slider:before {\n  transform: translateX(22px);\n}\n\n.slider.round {\n  border-radius: 34px;\n}\n.slider.round:before {\n  border-radius: 50%;\n}\n\n/* Doesn't really do anything because other elements stack on top of it,\n * but scrollbar is broken without it\n */\n\n/* Code added for verticle icicle graph */\n\n.path {\n  will-change: d;\n}\n.path:hover {\n  cursor: pointer;\n}\n"
  },
  {
    "path": "src/extension/contentScript.ts",
    "content": "// once chrome tab connects with our content-script\nwindow.postMessage({action: 'contentScriptStarted'}, '*');\n//console.log('content script sending action to the window IN CONTENT SCRIPT');\n\n// Listen to messages from Recoilize module within dev webpage\nwindow.addEventListener('message', msg => {\n  console.log('contentScript message: ', msg.data);\n  chrome.runtime.sendMessage(msg.data);\n});\n\n// listening for messages from the background script\nchrome.runtime.onMessage.addListener(msg => {\n  // send the message to npm package\n  const {action} = msg;\n  switch (action) {\n    case 'snapshotTimeTravel':\n      window.postMessage(msg, '*');\n      break;\n    case 'persistState':\n      window.postMessage(msg, '*');\n      break;\n    case 'throttleEdit':\n      window.postMessage(msg, '*');\n      break;\n  }\n});\n"
  },
  {
    "path": "src/types/index.d.ts",
    "content": "// snapshot taken by recoilize module\nexport type stateSnapshot = {\n  filteredSnapshot: filteredSnapshot;\n  componentAtomTree: componentAtomTree;\n  indexDiff?: number;\n};\n\n// used for the filter state hook\nexport type stateSnapshotDiff = {\n  filteredSnapshot?: filteredSnapshotDiff;\n  componentAtomTree?: componentAtomTreeDiff;\n  indexDiff?: number;\n};\n\nexport type filteredSnapshot = {\n  // key of atom name with the value of an atom\n  [atomName: string]: node;\n};\n\nexport type filteredSnapshotDiff = {\n  [atomName: string]: nodeDiff;\n};\n\n// object of either atom or selector\nexport type node = {\n  // recoil defined string that determines wether an atom or selector distinguished by 'RecoilState' or 'RecoilValueReadOnly'\n  type: string;\n  // user defined node state\n  contents: any;\n  // current node is dependent on this array\n  nodeDeps: string[];\n  // current node is a dependency for the array of nodes\n  nodeToNodeSubscription: string[];\n};\n\nexport type nodeDiff = {\n  string?: string | string[];\n  contents?: any;\n  nodeDeps?: string[] | string[][];\n  nodeToNodeSubscription?: string[] | string[][];\n};\n\nexport type componentAtomTree = {\n  children: object[];\n  name: string;\n  tag: number;\n  recoilNodes: string[];\n  actualDuration: number;\n  treeBaseDuration: number;\n  wasSuspended: boolean;\n};\n\nexport type componentAtomTreeDiff = {\n  children: object[] | object[][];\n  name: string | string[];\n  tag: number | number[];\n  recoilNodes: string[] | string[][];\n  actualDuration: number | number[];\n  treeBaseDuration: number | number[];\n  wasSuspended: boolean | boolean[];\n};\n\nexport type dataDuration = {\n  [name: string]: any;\n};\n\nexport type dataDurationArr = dataDuration[];\n\n//! not possible to be typed better\n// atom is an object consisting of\n// atom state names and their values.\n// Since state can be anything (num, bool, str, etc.)\n// it's impossible to say what properties\n// atom can hold other than some generic key name and\n// a type of any\nexport type atom = {\n  [name: string]: any;\n};\n\n//! not possible to be typed better\n// selector is an object consisting of\n// selector state names and their values.\n// Since state can be anything (num, bool, str, etc.)\n// it's impossible to say what properties\n// selector can hold other than some generic key name and\n// a type of any\nexport type selector = {\n  [name: string]: any;\n};\n\nexport type selectedTypes = {\n  [name: string]: string;\n};\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"outDir\": \"./build\",\n    \"module\": \"commonjs\",\n    \"pretty\": true,\n    \"noImplicitAny\": false,\n    \"removeComments\": true,\n    \"preserveConstEnums\": true,\n    \"sourceMap\": true,\n    \"allowJs\": true,\n    \"jsx\": \"react\",\n    \"target\": \"esnext\",\n    \"esModuleInterop\": true\n  },\n  \"include\": [\"./src/app/**/*\", \"./index.d.ts\"],\n  \"exclude\": [\"node_modules\", \".vscode\", \"__tests__\"]\n}\n"
  },
  {
    "path": "webpack.config.js",
    "content": "const path = require('path');\nconst ChromeExtensionReloader = require('webpack-chrome-extension-reloader');\n\nconst config = {\n  entry: {\n    app: './src/app/index.tsx',\n    background: './src/extension/background.ts',\n    content: './src/extension/contentScript.ts',\n  },\n  output: {\n    path: path.resolve(__dirname, 'src/extension/build/bundles'),\n    filename: '[name].bundle.js',\n  },\n  module: {\n    rules: [\n      {\n        test: /\\.tsx?$/,\n        use: 'ts-loader',\n        exclude: /node_modules/,\n      },\n      {\n        test: /\\.jsx?/,\n        exclude: /(node_modules)/,\n        resolve: {\n          extensions: ['.js', '.jsx'],\n        },\n        use: {\n          loader: 'babel-loader',\n          options: {\n            presets: ['@babel/preset-env', '@babel/preset-react'],\n          },\n        },\n      },\n      {\n        test: /\\.scss$/,\n        use: ['style-loader', 'css-loader', 'sass-loader'],\n      },\n      {\n        test: /\\.css$/,\n        use: ['style-loader', 'css-loader'],\n      },\n    ],\n  },\n  resolve: {\n    extensions: ['.tsx', '.ts', '.js', '.jsx'],\n  },\n  plugins: [],\n};\n\nmodule.exports = (env, argv) => {\n  if (argv.mode === 'development') {\n    config.plugins.push(\n      new ChromeExtensionReloader({\n        entries: {\n          contentScript: ['app', 'content'],\n          background: ['background'],\n        },\n      }),\n    );\n  }\n  return config;\n};\n"
  }
]