├── .babelrc
├── .eslintignore
├── .eslintrc.json
├── .gitignore
├── LICENSE
├── README.md
├── __tests__
├── supertest.js
└── test.js
├── assets
├── capture_state_time_travel.gif
├── connect.gif
├── marquee_promo.png
├── new_memory.gif
└── small_promo.png
├── chrome_extension
├── background.js
├── contentScript.js
├── delorean_logo_128.png
├── devtools.html
├── manifest.json
├── panel.html
├── panel.js
└── src
│ ├── App.svelte
│ ├── State.svelte
│ └── index.js
├── client
├── Child.svelte
├── ClientApp.svelte
└── main.ts
├── jest.config.cjs
├── jest.setup.js
├── package-lock.json
├── package.json
├── public
├── global.css
└── index.html
├── rollup.config.js
├── server
└── server.ts
└── tsconfig.json
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [["@babel/preset-env", { "targets": { "node": "current" } }]],
3 | "env": {
4 | "test": {
5 | "plugins": ["@babel/plugin-transform-modules-commonjs"]
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | /public/*.js
2 | /public/*.css
3 | /public/*.js.map
4 | /chrome_extension/build/*.js
5 | /chrome_extension/build/*.js.map
6 | /chrome_extension/build/*.css
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["airbnb-base", "eslint:recommended", "plugin:import/recommended"],
3 | "root": true,
4 | "plugins": ["import", "svelte3"],
5 | "overrides": [
6 | {
7 | "files": ["**/*.svelte"],
8 | "processor": "svelte3/svelte3"
9 | }
10 | ],
11 | "rules": {
12 | "indent": ["warn", 2],
13 | "no-unused-vars": ["off", { "vars": "local" }],
14 | "import/no-unresolved": "off",
15 | "import/extensions": "off",
16 | "prefer-const": "warn",
17 | "quotes": ["warn", "single"],
18 | "space-infix-ops": "warn",
19 | "no-undef": "off",
20 | "import/no-extraneous-dependencies": "off",
21 | "global-require": "off"
22 | },
23 | "env": {
24 | "es6": true,
25 | "browser": true,
26 | "webextensions": true,
27 | "node": true
28 | },
29 | "globals": {
30 | "Atomics": "readonly",
31 | "SharedArrayBuffer": "readonly",
32 | "fetch": false
33 | },
34 | "parserOptions": {
35 | "ecmaFeatures": {
36 | "jsx": true
37 | },
38 | "ecmaVersion": 2019,
39 | "sourceType": "module"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build/*.js
2 | .DS_Store
3 | node_modules/*
4 | chrome_extension/build/*.js
5 | chrome_extension/build/*.js.map
6 | chrome_extension/build/*.css
7 | public/build/*.js
8 | public/build/*.js.map
9 | public/build/*.css
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 OSLabs Beta
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # DeLorean
4 | The first time-traveling debugger for Svelte applications.
5 |
6 | DeLorean is a debugging tool for Svelte developers. It records snapshots when a target applications's state changes and allows users to jump to any previously recorded state. It also displays the names and values of all variables in your application at the time of the snapshot alongside the state jump button.
7 |
8 | ## Features
9 |
10 | #### Dev tool information panel
11 | All stateful data is easily accessible from within DeLorean’s Chrome Developer Tools panel. The name of each component and the names of all variables that that component contains, alongside their values, are displayed. As you make changes to your app, each component’s state at the time of the state change is stored in a snapshot and cached. These snapshots are then displayed in the Dev Tools panel, and also allow for the next feature:
12 |
13 | #### Application state time-travel
14 | In addition to displaying component and variable information, DeLorean resets your application’s state to the values it contained at any point since DeLorean was connected to your application. This allows for step-by-step examination of state change sequences within an application, easing the challenge of tracking state changes over time and reducing the need to use ```console.log```.
15 |
16 | #### Create new timelines
17 | As DeLorean renders an app in its previous state, a user may want to interact with the application in a different way than before. Apps remain fully functional while being tested with DeLorean, so any changes the user makes will simply create a new timeline that is now tracked in the Dev Tools panel.
18 |
19 | ## Installation
20 | If you're interested in learning more about how DeLorean works, feel free to fork and clone this repo! Otherwise, just download the ```chrome_extension``` folder and save it somewhere on your computer.
21 |
22 | Then navigate to [Chrome's extensions page](chrome://extensions/). Ensure you are in developer mode by clicking the 'developer mode' switch in the top-right corner of the page. Click on 'load unpacked', and select the ```chrome_extension``` folder downloaded earlier. Open up your DevTools panel, and check to make sure DeLorean is available in the dropdown menu of the navbar!
23 |
24 | ## How To Use
25 |
26 | #### Run the target app in dev mode
27 | Importantly, this debugging tool can only operate on Svelte applications being run in dev mode. Without dev mode enabled, Svelte's compiler strips some internal functionality in order to reduce overall bundle size. DeLorean requires that functionality, so ensure dev mode is enabled when you run the application to be tested.
28 |
29 | #### Attach your Svelte app to an element with the id "root"
30 | At this time, DeLorean searches the tested application for an id of "root" to find Svelte components to test. An example format for an html page and its main svelte component:
31 |
32 | *index.html:*
33 | ```
34 |
35 |
36 |
37 |
38 | ```
39 | *main.js:*
40 | ```
41 | import SvelteApp from './SvelteApp.svelte';
42 |
43 | new SvelteApp({
44 | target: document.getElementById('root'),
45 | });
46 | ```
47 | If your application attaches to something other than an element with the id "root", DeLorean won't be able to find it.
48 |
49 | #### Run the application from localhost:*
50 | This extension's permissions are scoped to ```localhost:``` urls only, so make sure to run your application on a localhost port in order to use DeLorean.
51 |
52 | #### Click Connect
53 | Once your app is up and running, open the Dev Tools panel and select DeLorean from the dropdown in the navbar. Then click Connect, and you should see your application's intial state.
54 |
55 | 
56 |
57 | #### Make some state changes, then click the state buttons
58 | Have [fun](https://www.youtube.com/watch?v=FWG3Dfss3Jc)! DeLorean tracks every stateful update and reflects it in real time in the Dev Tool panel. If you click on the state buttons that appear in the Dev Tool panel, you will see your application's state at that moment reflected in the application, as well as each component's variables with their names and values displayed in the devtool.
59 |
60 | 
61 |
62 | Changing state after clicking on a previous state button will lead to the creation of a memory stack. This erases the old changes made to your application's state and allows you to explore a different sequence of state changes on your application, without needing to reresh or restart your application.
63 |
64 | 
65 |
66 | ## Troubleshooting
67 |
68 | #### DeLorean doesn't appear in the Dev Tools panel
69 |
70 | [] Did you [install DeLorean](#installation)?
71 |
72 | [] Try refreshing the extension from the [Chrome extensions page](chrome://extensions/)
73 |
74 | #### DeLorean isn't communicating with my app
75 |
76 | [] Is your application running in dev mode?
77 |
78 | [] Is your Svelte component attached to an element with the id of "root"?
79 |
80 | [] Are you running your app from something other than a localhost url?
81 |
82 | [] Was your app developed with SvelteKit? At this time, DeLorean is untested with SvelteKit applications, and there are likely to be implementation issues. If you want DeLorean to work with SveleteKit now, feel free to [contribute](#contribute) to the project!
83 |
84 | #### DeLorean is showing a blank screen after closing the Dev Tools panel
85 |
86 | [] DeLorean receives initial state after connecting for the first time. If you've closed and reopened the Dev Tools panel, just click Connect and change some state in your application and DeLorean will start to track it, although only from the point of second connection onwards.
87 |
88 | ## Contribute
89 |
90 | DeLorean is fully open-source, and we would love to get more brains working on the project. If you'd like to fix bugs or add a new feature, fork the repo, make some changes and submit a pull request. Our team will review your changes and work to integrate functional code.
91 |
92 | We have a number of features in our backlog to implement, including consistent SvelteKit integration, creation of an ignore list to stop tracking certain variables, and persistent storage, so that state history can be saved even after closing out of the extension.
93 |
94 | If you have a feature request in mind, please submit an issue so our team can determine if it is an appropriate feature for DeLorean.
95 |
96 | ## Learn More
97 |
98 | Visit the [DeLorean website](http://delorean.software)
99 |
100 | Read more at Medium - [Time Travel Debugging in Svelte with DeLorean](https://medium.com/@vantassel.sam/time-travel-debugging-in-svelte-with-delorean-26e04efe9474)
101 |
102 | Special thanks to RedHatter's [Svelte Dev Tools](https://github.com/RedHatter/svelte-devtools) for providing examples of how Svelte's dev mode listeners can be utilized.
103 |
104 | ## Contributors
105 |
106 | **Albert Han** - [@alberthan1](https://github.com/alberthan1)
107 |
108 | **Aram Krakirian** - [@aramkrakirian](https://github.com/aramkrakirian)
109 |
110 | **Erick Maese** - [@ErickMaese](https://github.com/ErickMaese)
111 |
112 | **Trevor Leung** - [@trevleung](https://github.com/trevleung)
113 |
114 | **Sam VanTassel** - [@SamVanTassel](https://github.com/SamVanTassel)
115 |
--------------------------------------------------------------------------------
/__tests__/supertest.js:
--------------------------------------------------------------------------------
1 | import request from 'supertest';
2 | import path from 'path';
3 |
4 | const app = require('../server/server.ts');
5 |
6 | describe('Route integration', () => {
7 | describe('/', () => {
8 | describe('GET', () => {
9 | it('responds with 200 status and text/html content type', () => {
10 | request(app)
11 | .get('/')
12 | .expect('Content-Type', /text\/html/)
13 | .expect(200);
14 | });
15 | });
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/__tests__/test.js:
--------------------------------------------------------------------------------
1 | import { render, fireEvent } from '@testing-library/svelte';
2 | import { screen } from '@testing-library/dom';
3 | import { chrome } from 'jest-chrome'; // importing for intellisense and linting, chrome is defined globally
4 | import App from '../chrome_extension/src/App.svelte';
5 |
6 | /**
7 | * @jest-environment jsdom
8 | */
9 | describe('checking for chrome native functions', () => {
10 | test('chrome is mocked', () => {
11 | expect(chrome).toBeDefined();
12 | expect(window.chrome).toBeDefined();
13 | expect(chrome.storage.local).toBeDefined();
14 | expect(chrome.runtime).toBeDefined();
15 | expect(chrome.runtime.connect).toBeDefined();
16 | });
17 |
18 | test('chrome api functions are mocked', () => {
19 | const manifest = {
20 | name: 'DeLorean',
21 | manifest_version: 2,
22 | version: '1.2',
23 | };
24 | chrome.runtime.getManifest.mockImplementation(() => manifest);
25 | expect(chrome.runtime.getManifest()).toEqual(manifest);
26 | expect(chrome.runtime.getManifest).toBeCalled();
27 | });
28 | });
29 |
30 | describe('rendering of main App component', () => {
31 | let results;
32 |
33 | beforeEach(() => {
34 | results = render(App);
35 | });
36 |
37 | it('only displays the connect button', () => {
38 | expect(() => results.getByText('Connect').not.toThrow());
39 | expect(() => results.getAllByRole('button').toHaveLength(1));
40 | });
41 | });
42 |
43 | describe('on clicking connect button', () => {
44 | let results;
45 | const mainToBgPort = {};
46 |
47 | beforeEach(() => {
48 | results = render(App);
49 | chrome.runtime.connect.mockImplementation(() => ({
50 | onMessage: {
51 | addListener: () => {},
52 | },
53 | postMessage: () => {},
54 | }));
55 | });
56 |
57 | it('hides the connect button once clicked and displays a single state button', async () => {
58 | expect(screen.queryByText('Connect')).not.toBe(null);
59 | await fireEvent.click(screen.getByText('Connect'));
60 | expect(() => screen.getByText('Connect').toThrow());
61 | });
62 |
63 | it('adds events listeners to mainToBgPort', () => {
64 | // checking to see if mainToBgPort has haslistener's function definition(ND)
65 | expect(mainToBgPort.onMessage.hasListeners()).toBe(true);
66 | });
67 | });
68 |
69 | describe('on clicking a state button', () => {
70 | /*
71 | need to mock Svelte app state data from background page... message should be this format:
72 | {
73 | body: {
74 | componentStates: [
75 | [
76 | [
77 | {component, could be {} for testing purposes},
78 | {stateValues ex {counter: 0} },
79 | 'componentName',
80 | ],
81 | [
82 | {component2, could be {} for testing purposes},
83 | {stateValues ex {counter: 0} },
84 | 'component2Name'
85 | ]
86 | ],
87 | [ [different state of component1], [different sate of comp 2] ],
88 | [ [different state of component1], [different sate of comp 2] ],
89 | ],
90 | cacheLength: 3;
91 | }
92 | }
93 | */
94 | let results;
95 | let mainToBgPort = {};
96 |
97 | chrome.runtime.connect.mockImplementation(() => ({
98 | onMessage: {
99 | addListener: () => {},
100 | },
101 | postMessage: jest.fn(() => ({
102 | body: {
103 | componentStates: [{}, { state: 0 }, 'componentName'],
104 | cacheLength: 0,
105 | },
106 | })),
107 | }));
108 |
109 | // mainToBgPort.postMessage({ body: 'updateCtx', ctxIndex: i });
110 |
111 | beforeEach(async () => {
112 | results = render(App);
113 | await fireEvent.click(screen.getByText('Connect'));
114 | // we need to send fake data to make state buttons appear (ND)
115 | setTimeout(await fireEvent.click(screen.getByText('State')), 2000);
116 | mainToBgPort.postMessage.mockImplementation(() => {});
117 | activeIndex = 0;
118 | mainToBgPort = chrome.runtime.connect();
119 | });
120 |
121 | it('receive the correct data types from bg', () => {
122 | expect(mainToBgPort.postMessage.mock.calls.length).toBe(1);
123 | });
124 |
125 | it('posts a message on mainToBgPort', () => {
126 | expect(mainToBgPort.postMessage.mock.calls.length).toBe(1);
127 | });
128 |
129 | it('changes activeIndex value', () => {
130 | expect(results.activeIndex).not.toBe(1);
131 | });
132 | });
133 |
--------------------------------------------------------------------------------
/assets/capture_state_time_travel.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/DeLorean/1bbd52eb57a99369a880acb0f93bc46106f107b6/assets/capture_state_time_travel.gif
--------------------------------------------------------------------------------
/assets/connect.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/DeLorean/1bbd52eb57a99369a880acb0f93bc46106f107b6/assets/connect.gif
--------------------------------------------------------------------------------
/assets/marquee_promo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/DeLorean/1bbd52eb57a99369a880acb0f93bc46106f107b6/assets/marquee_promo.png
--------------------------------------------------------------------------------
/assets/new_memory.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/DeLorean/1bbd52eb57a99369a880acb0f93bc46106f107b6/assets/new_memory.gif
--------------------------------------------------------------------------------
/assets/small_promo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/DeLorean/1bbd52eb57a99369a880acb0f93bc46106f107b6/assets/small_promo.png
--------------------------------------------------------------------------------
/chrome_extension/background.js:
--------------------------------------------------------------------------------
1 | // This is a background page, previously a service worker.
2 | // Its console logs will appear in a service worker console
3 | let mainPort;
4 | // listen for message from main, run content script if asked
5 | chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
6 | // pass any received message along to main.js
7 | if (request) {
8 | if (mainPort) {
9 | mainPort.postMessage({ body: request.body });
10 | }
11 | }
12 | // return true; // this line is needed to set sendReponse to be asynchronous
13 | });
14 |
15 | // listen for port connection from main.js
16 | chrome.runtime.onConnect.addListener((port) => {
17 | mainPort = port;
18 | mainPort.onMessage.addListener((msg) => {
19 | if (msg.body === 'runContentScript') {
20 | chrome.tabs.executeScript({ file: './contentScript.js' });
21 | }
22 | if (msg.body === 'updateScript') {
23 | setTimeout(() => {
24 | chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
25 | chrome.tabs.sendMessage(tabs[0].id, {
26 | body: 'UPDATE',
27 | script: msg.script,
28 | });
29 | });
30 | }, 50);
31 | }
32 | if (msg.body === 'updateCtx') {
33 | chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
34 | chrome.tabs.sendMessage(tabs[0].id, {
35 | body: 'TIME_TRAVEL',
36 | ctxIndex: msg.ctxIndex,
37 | });
38 | });
39 | }
40 | });
41 | });
42 |
--------------------------------------------------------------------------------
/chrome_extension/contentScript.js:
--------------------------------------------------------------------------------
1 | // This injects the string iife below directly into the DOM to be executed and listen for events.
2 | // Check the elements tab of the devtools to see the script tag added in
3 |
4 | let messageListeners = false;
5 |
6 | if (!messageListeners) {
7 | window.addEventListener(
8 | 'message',
9 | (messageEvent) => {
10 | if (messageEvent.source === window && messageEvent.data.body !== 'TIME_TRAVEL') {
11 | chrome.runtime.sendMessage(messageEvent.data);
12 | }
13 | },
14 | false,
15 | );
16 | messageListeners = true;
17 | }
18 |
19 | // testing update script feature
20 | chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
21 | if (req.body === 'TIME_TRAVEL') {
22 | const i = req.ctxIndex;
23 | window.postMessage({ body: 'TIME_TRAVEL', ctxIndex: i });
24 | }
25 |
26 | if (req.body === 'UPDATE') {
27 | if (!window.tag) {
28 | window.tag = document.createElement('script');
29 | const root = document.getElementById('root');
30 | while (root.children.length) {
31 | root.children[0].remove();
32 | }
33 |
34 | window.tag.text = `(function () {
35 | 'use strict';
36 |
37 | const parse = (event) => JSON.parse(JSON.stringify(event));
38 | let cacheState = [];
39 | const components = [];
40 | let lastIndex = 0;
41 |
42 | const sendMessages = (componentStates) => {
43 | window.postMessage({
44 | body: {
45 | componentStates: componentStates,
46 | cacheLength: cacheState.length
47 | }
48 | });
49 | };
50 |
51 | // add all Svelte components to array
52 | window.document.addEventListener('SvelteRegisterComponent', (e) => {
53 | components.push(e.detail.component);
54 | })
55 | setTimeout(saveAndDispatchState, 0);
56 |
57 | function checkIfChanged(componentState, i) {
58 | // if caches state is empty... or the most recent cache state is different
59 | // and the state at the last sent index is different, then state has truly changed
60 | if (!cacheState.length ||
61 | (JSON.stringify(cacheState[cacheState.length - 1][i][1]) !== JSON.stringify(componentState[1])
62 | && JSON.stringify(cacheState[lastIndex][i][1]) !== JSON.stringify(componentState[1]))) {
63 | return true;
64 | } else return false;
65 | }
66 |
67 | function saveAndDispatchState() {
68 | const curState = [];
69 | components.forEach((component) => {
70 | curState.push([component, component.$capture_state(), component.constructor.name]);
71 | })
72 | // only add to cache & send messages if any state has actually changed
73 | if (curState.some(checkIfChanged)) {
74 | // if cacheState is logner than the last index, we are back in time and should start a new branch
75 | if (cacheState.length > lastIndex){
76 | cacheState = cacheState.slice(0, lastIndex + 1)
77 | }
78 | sendMessages(parse(curState));
79 | cacheState.push([...curState]);
80 | lastIndex = cacheState.length - 1;
81 | }
82 | }
83 |
84 | function setupListeners(root) {
85 | root.addEventListener('SvelteRegisterBlock', (e) => saveAndDispatchState());
86 | root.addEventListener('SvelteDOMSetData', (e) => saveAndDispatchState());
87 | root.addEventListener('SvelteDOMInsert', (e) => saveAndDispatchState());
88 |
89 | // These event listeners aren't being used in this version, but could provide valuable data for future versions of this product
90 | // root.addEventListener('SvelteDOMRemove', (e) => (e) => sendMessages(parseEvent(e.detail)));
91 | // root.addEventListener('SvelteDOMAddEventListener', (e) => sendMessages(parseEvent(e.detail)));
92 | // root.addEventListener('SvelteDOMRemoveEventListener',(e) => sendMessages(parseEvent(e.detail)));
93 | // root.addEventListener('SvelteDOMSetProperty', (e) => sendMessages(parseEvent(e.detail)));
94 | // root.addEventListener('SvelteDOMSetAttribute', (e) => sendMessages(parseEvent(e.detail)));
95 | // root.addEventListener('SvelteDOMRemoveAttribute', (e) => sendMessages(parseEvent(e.detail)));
96 | };
97 |
98 | setTimeout(() => setupListeners(window.document));
99 |
100 | ${req.script};
101 |
102 | window.addEventListener(
103 | "message",
104 | (messageEvent) => {
105 | if (messageEvent.data.body === 'TIME_TRAVEL') {
106 | const i = messageEvent.data.ctxIndex;
107 | lastIndex = i;
108 | if (cacheState[i]) {
109 | cacheState[i].forEach((componentState) => {
110 | componentState[0].$inject_state(componentState[1])
111 | })
112 | }
113 | }
114 | },
115 | false
116 | );
117 | })();
118 | `;
119 | document.children[0].append(window.tag);
120 | }
121 | }
122 | });
123 |
--------------------------------------------------------------------------------
/chrome_extension/delorean_logo_128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/DeLorean/1bbd52eb57a99369a880acb0f93bc46106f107b6/chrome_extension/delorean_logo_128.png
--------------------------------------------------------------------------------
/chrome_extension/devtools.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/chrome_extension/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "DeLorean",
3 | "description": "Time-travel debugger for Svelete applications",
4 | "version": "1.2",
5 | "manifest_version": 2,
6 | "devtools_page": "./devtools.html",
7 | "background": {
8 | "scripts": ["background.js"]
9 | },
10 | "permissions": ["tabs", "activeTab", "*://localhost/*"],
11 | "icons": {
12 | "16": "delorean_logo_128.png",
13 | "48": "delorean_logo_128.png",
14 | "128": "delorean_logo_128.png"
15 | },
16 | "web_accessible_resources": ["./contentScript.js"]
17 | }
18 |
--------------------------------------------------------------------------------
/chrome_extension/panel.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Document
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/chrome_extension/panel.js:
--------------------------------------------------------------------------------
1 | //creates a panel within Chrome DevTools named DeLorean
2 |
3 | chrome.devtools.panels.create(
4 | 'DeLorean', // title
5 | './SvelteDeLorean.png', // icon
6 | './panel.html',
7 | (panel) => {}
8 | );
9 |
--------------------------------------------------------------------------------
/chrome_extension/src/App.svelte:
--------------------------------------------------------------------------------
1 |
65 |
66 |