"]
37 | }
--------------------------------------------------------------------------------
/src/lib/containers/SingleTab.svelte:
--------------------------------------------------------------------------------
1 |
35 |
36 |
37 | {#if currentData}
38 |
39 | {/if}
40 |
41 |
--------------------------------------------------------------------------------
/static/ContentScripts/svelte-listener/profiler.js:
--------------------------------------------------------------------------------
1 | import { profile } from './listener.js'
2 |
3 | let topFrame = {}
4 | let currentFrame = topFrame
5 | let profilerEnabled = false
6 |
7 | export function startProfiler() {
8 | topFrame = {
9 | type: 'top',
10 | start: performance.now(),
11 | children: [],
12 | }
13 | currentFrame = topFrame
14 | profilerEnabled = true
15 | }
16 |
17 | export function stopProfiler() {
18 | topFrame.end = performance.now(),
19 | profilerEnabled = false
20 | }
21 |
22 | export function clearProfiler() {
23 | topFrame.children = []
24 | topFrame.start = performance.now()
25 | }
26 |
27 | export function updateProfile(node, type, fn, ...args) {
28 | if (!profilerEnabled) {
29 | fn(...args)
30 | return
31 | }
32 |
33 | const parentFrame = currentFrame
34 | currentFrame = {
35 | type,
36 | node: node.id,
37 | start: performance.now(),
38 | children: [],
39 | }
40 | parentFrame.children.push(currentFrame)
41 | fn(...args)
42 | currentFrame.end = performance.now()
43 | currentFrame.duration = currentFrame.end - currentFrame.start
44 | currentFrame = parentFrame
45 |
46 | if (currentFrame.type == 'top')
47 | topFrame.duration = topFrame.children[topFrame.children.length - 1].end - topFrame.children[0].start
48 |
49 | profile(topFrame)
50 | }
--------------------------------------------------------------------------------
/src/lib/components/SplitpaneContainer.svelte:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 | Svelte Version: {svelteVersion}
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
71 |
--------------------------------------------------------------------------------
/static/Popup/Popup.svelte:
--------------------------------------------------------------------------------
1 |
39 |
40 |
41 |
42 | {#if version}
43 |
This webpage is using Svelte
44 |
Svelte version: {version}
45 | {:else}
46 |
This webpage is NOT using Svelte
47 |
48 | SvelteScope cannot inject this page
49 |
50 | {/if}
51 |
52 |
--------------------------------------------------------------------------------
/src/app.css:
--------------------------------------------------------------------------------
1 | * {
2 | -webkit-font-smoothing: antialiased;
3 | -moz-osx-font-smoothing: grayscale;
4 | }
5 |
6 | :root {
7 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
8 | line-height: 1.5;
9 | font-weight: 400;
10 |
11 | color-scheme: light dark;
12 | color: rgba(255, 255, 255, 0.87);
13 | background-color: #242424;
14 |
15 | font-synthesis: none;
16 | text-rendering: optimizeLegibility;
17 | -webkit-font-smoothing: antialiased;
18 | -moz-osx-font-smoothing: grayscale;
19 | }
20 |
21 | a {
22 | font-weight: 500;
23 | color: #646cff;
24 | text-decoration: inherit;
25 | }
26 | a:hover {
27 | color: #535bf2;
28 | }
29 |
30 | body {
31 | margin: 0;
32 | display: flex;
33 | place-items: center;
34 | width: 100vw;
35 | height: 100vh;
36 | /* min-width: 320px;
37 | min-height: 100vh; */
38 | }
39 |
40 | h1 {
41 | font-size: 3.2em;
42 | line-height: 1.1;
43 | }
44 |
45 | .card {
46 | padding: 2em;
47 | }
48 |
49 | #app {
50 | width: 100%;
51 | height: 100%;
52 | /* max-width: 1280px; */
53 | /* margin: 0 auto; */
54 | /* padding: 2rem; */
55 | /* text-align: center; */
56 | }
57 |
58 | button {
59 | border-radius: 8px;
60 | border: 1px solid transparent;
61 | padding: 0.6em 1.2em;
62 | font-size: 1em;
63 | font-weight: 500;
64 | font-family: inherit;
65 | background-color: #1a1a1a;
66 | cursor: pointer;
67 | transition: border-color 0.25s;
68 | }
69 | button:hover {
70 | border-color: #646cff;
71 | }
72 | button:focus,
73 | button:focus-visible {
74 | outline: 4px auto -webkit-focus-ring-color;
75 | }
76 |
77 | @media (prefers-color-scheme: light) {
78 | :root {
79 | color: #213547;
80 | background-color: #ffffff;
81 | }
82 | a:hover {
83 | color: #747bff;
84 | }
85 | button {
86 | background-color: #f9f9f9;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/test/App/App.test.js:
--------------------------------------------------------------------------------
1 | // import {fireEvent, render} from '@testing-library/svelte';
2 |
3 | // import App from './App.svelte';
4 |
5 | // describe('App', () => {
6 | // test('message and input have alwayys same values', async () => {
7 | // const {container, getByText} = render(App);
8 | // const input = container.querySelector('input');
9 | // const button = container.querySelector('button');
10 |
11 | // // initial state
12 | // expect(getByText('hi'));
13 | // expect(input.value).toBe('hi');
14 |
15 | // // // type in input
16 | // // await fireEvent.input(input, {target: {value: 'yum yum'}});
17 | // // expect(getByText('yum yum'));
18 | // // expect(input.value).toBe('yum yum');
19 | // // // click button
20 | // // await fireEvent.click(button);
21 | // // expect(getByText('bye'));
22 | // // expect(input.value).toBe('bye');
23 | // });
24 | // });
25 |
26 |
27 |
28 |
29 | import { fireEvent, render} from '@testing-library/svelte';
30 | import App from './App.svelte';
31 |
32 | describe('App', () => {
33 | test('message and input have always the same values', async () => {
34 | const { container, getByText } = render(App);
35 | const input = container.querySelector('input');
36 | const button = container.querySelector('button');
37 |
38 | // Perform null checks before using the variables
39 | if (input !== null && button !== null) {
40 | // initial state
41 | expect(getByText('hi'));
42 | expect(input.value).toBe('hi');
43 | // type in input
44 | await fireEvent.input(input, { target: { value: 'yum yum' } });
45 | expect(getByText('yum yum'));
46 | expect(input.value).toBe('yum yum');
47 | // click button
48 | await fireEvent.click(button);
49 | expect(getByText('bye'));
50 | expect(input.value).toBe('bye');
51 | } else {
52 | // Handle case when input or button is not found
53 | throw new Error('Input or button not found');
54 | }
55 | });
56 | });
57 |
58 |
--------------------------------------------------------------------------------
/src/lib/components/Editor/Editable.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 | {#if editing}
13 | {
16 | editing = false;
17 | // @ts-expect-error - target and value exists
18 | const updated = target.value;
19 | value = JSON.parse(updated);
20 | dispatch('change', updated);
21 | }}
22 | on:keydown={({ key, target }) => {
23 | if (key !== 'Enter') return;
24 | editing = false;
25 | // @ts-expect-error - target and value exists
26 | const updated = target.value;
27 | value = JSON.parse(updated);
28 | dispatch('change', updated);
29 | }}
30 | />
31 | {:else}
32 |
33 |
34 | (editing = !readonly)}>
35 | {JSON.stringify(value)}
36 |
37 | {/if}
38 |
39 |
85 |
--------------------------------------------------------------------------------
/src/routes/+layout.svelte:
--------------------------------------------------------------------------------
1 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "svelte-component-explorer",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "NODE_ENV=development vite build --watch",
8 | "build": "vite build && rollup -c",
9 | "preview": "vite preview",
10 | "check": "svelte-check --tsconfig ./tsconfig.json",
11 | "test": "npx --node-options=\"--experimental-vm-modules\" jest src",
12 | "test:watch": "npx --node-options=\"--experimental-vm-modules\" jest src --watch"
13 | },
14 | "devDependencies": {
15 | "@babel/preset-env": "^7.24.3",
16 | "@babel/preset-typescript": "^7.24.1",
17 | "@iconify/svelte": "^3.1.6",
18 | "@rollup/plugin-commonjs": "^25.0.7",
19 | "@rollup/plugin-node-resolve": "^15.2.3",
20 | "@sveltejs/vite-plugin-svelte": "^3.0.2",
21 | "@testing-library/jest-dom": "^6.4.2",
22 | "@testing-library/svelte": "^4.1.0",
23 | "@tsconfig/svelte": "^5.0.2",
24 | "@types/chrome": "^0.0.263",
25 | "@types/d3": "^7.4.3",
26 | "@types/jest": "^29.5.12",
27 | "@types/vis": "^4.21.27",
28 | "@vitest/ui": "^1.4.0",
29 | "babel-jest": "^29.7.0",
30 | "esm": "^3.2.25",
31 | "hot-reload-extension-vite": "^1.0.13",
32 | "jest": "^29.7.0",
33 | "jest-environment-jsdom": "^29.7.0",
34 | "jest-preview": "^0.3.1",
35 | "jsdom": "^24.0.0",
36 | "rollup-plugin-chrome-extension": "^3.6.12",
37 | "rollup-plugin-dev-server": "^0.4.3",
38 | "rollup-plugin-node-resolve": "^5.2.0",
39 | "rollup-plugin-sourcemaps": "^0.6.3",
40 | "rollup-plugin-svelte": "^7.0.0",
41 | "rollup-plugin-typescript2": "^0.36.0",
42 | "svelte": "^4.2.12",
43 | "svelte-check": "^3.6.6",
44 | "svelte-jester": "^3.0.0",
45 | "svelte-listener": "git+https://github.com/RedHatter/svelte-listener.git",
46 | "ts-jest": "^29.1.2",
47 | "ts-node": "^10.9.2",
48 | "tslib": "^2.6.2",
49 | "typescript": "^5.4.3",
50 | "vite": "^5.1.6"
51 | },
52 | "dependencies": {
53 | "@types/node": "^20.11.27",
54 | "d3": "^7.9.0",
55 | "path": "^0.12.7",
56 | "rollup": "^2.79.1",
57 | "rollup-plugin-css-only": "^4.5.2",
58 | "svelte-splitpanes": "^0.8.0",
59 | "vis": "^4.21.0-EOL",
60 | "vis-network": "^9.1.9"
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/static/ContentScripts/contentScriptIsolate.js:
--------------------------------------------------------------------------------
1 | /*
2 | This is the ISOLATED content script! This can communicate with our MAIN content
3 | script. It can also communicate with the Popup and Panel. So it's kind of like
4 | the middle man of this Chrome extension
5 | */
6 |
7 | let port = null;
8 | // Listens to messages from ContentScriptMain
9 | // and forwards them to other parts of the extension
10 | window.addEventListener('message', async (msg) => {
11 | if (
12 | typeof msg !== 'object' ||
13 | msg === null ||
14 | msg.data?.source !== 'contentScript.js'
15 | ) {
16 | return;
17 | }
18 |
19 | switch (msg.data.type) {
20 | case 'updateRootComponent':
21 | case 'returnRootComponent':
22 | case 'returnTempRoot':
23 | chrome.runtime.sendMessage({
24 | type: msg.data.type,
25 | rootComponent: msg.data.rootComponent,
26 | });
27 | break;
28 | case 'returnSvelteVersion':
29 | chrome.runtime.sendMessage({
30 | type: msg.data.type,
31 | svelteVersion: msg.data.svelteVersion,
32 | });
33 | break;
34 | case 'handleBrowserRefresh':
35 | chrome.runtime.sendMessage({
36 | type: msg.data.type,
37 | })
38 | break;
39 | default:
40 | break;
41 | }
42 | });
43 |
44 |
45 | // Listens for a message from the Popup and Panel
46 | // Forwards them to ContentScriptMain/index.js
47 |
48 | chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
49 |
50 | switch (request.message) {
51 | case 'getRootComponent':
52 | case 'getSvelteVersion':
53 | case 'handleClosedPanel':
54 | window.postMessage({
55 | type: request.message,
56 | source: 'contentScriptIsolate.js',
57 | });
58 | break;
59 |
60 | case 'injectState':
61 | window.postMessage({
62 | type: request.message,
63 | newState: request.newState,
64 | componentId: request.componentId,
65 | source: 'contentScriptIsolate.js',
66 | });
67 | break;
68 |
69 | case 'injectSnapshot':
70 | window.postMessage({
71 | type: request.message,
72 | snapshot: request.snapshot,
73 | source: 'contentScriptIsolate.js',
74 | });
75 | break;
76 | }
77 | });
78 |
79 |
--------------------------------------------------------------------------------
/src/lib/components/Editor/Props.svelte:
--------------------------------------------------------------------------------
1 |
60 |
61 |
62 | {#if currentProps.length}
63 |
64 | {#each currentProps as { key, value } (key)}
65 | change(key, e.detail, value)}
71 | />
72 | {/each}
73 |
74 | {:else}
75 | None
76 | {/if}
77 |
78 |
86 |
--------------------------------------------------------------------------------
/src/lib/components/Editor.svelte:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 | {#if currentData2 && currentData2.type === 'component'}
13 |
20 |
21 | {@const events = currentData2.detail.listeners?.map((l) => {
22 | const suffix = l.modifiers?.length ? `|${l.modifiers.join('|')}` : '';
23 | const value = { __is: 'function', source: l.handler };
24 | return { key: l.event + suffix, value };
25 | })}
26 |
30 |
34 |
35 |
36 | {:else if (currentData2 && currentData2.type === 'block') || (currentData2 && currentData2.type === 'iteration')}
37 |
45 |
46 |
47 | {:else if currentData2 && currentData2.type === 'element'}
48 |
49 |
Attributes
50 |
55 |
56 |
57 | {@const events = currentData2.detail.listeners?.map((l) => {
58 | const suffix = l.modifiers?.length ? `|${l.modifiers.join('|')}` : '';
59 | const value = { __is: 'function', source: l.handler };
60 | return { key: l.event + suffix, value };
61 | })}
62 |
66 | {/if}
67 |
68 |
69 |
99 |
--------------------------------------------------------------------------------
/__mocks__/chrome.ts:
--------------------------------------------------------------------------------
1 | // import { Component } from '../src/pages/Panel/slices/highlightedComponentSlice';
2 | import initialData from "./mockData";
3 |
4 | let data = JSON.parse(JSON.stringify(initialData));
5 |
6 | type ChromeMessageListener = (message: any) => void;
7 |
8 | interface MockRuntime {
9 | onMessage: {
10 | addListener: (callback: ChromeMessageListener) => void;
11 | removeListener: (callback: ChromeMessageListener) => void;
12 | _triggerMessage: (message: any) => void;
13 | };
14 | sendMessage: (message: MockMessageType) => void;
15 | }
16 |
17 | interface MockMessageType {
18 | message:
19 | | "getRootComponent"
20 | | "getSvelteVersion"
21 | | "handleClosedPanel"
22 | | "injectState"
23 | | "injectSnapshot";
24 | snapshot?: 'component';
25 | componentId?: number;
26 | newState?: {
27 | [stateKey: string]: number | string;
28 | };
29 | }
30 | interface QueryInfo {
31 | active: boolean;
32 | lastFocusedWindow: boolean;
33 | }
34 |
35 | interface MockTabs {
36 | query: (queryInfo: QueryInfo) => [{ id: number; url: string }];
37 | sendMessage: (tabId: number, message: MockMessageType) => void;
38 | }
39 |
40 | export interface MockChrome {
41 | runtime: MockRuntime;
42 | tabs: MockTabs;
43 | clearListeners: () => void;
44 | resetMockData: () => void;
45 | sendEmptyDataOnNextRequest: () => void;
46 | }
47 |
48 | let listeners: ChromeMessageListener[] = [];
49 | let _sendEmptyDataOnNextRequest = false;
50 |
51 |
52 |
53 | const chrome: MockChrome = {
54 | runtime: {
55 | onMessage: {
56 | addListener: (callback) => {
57 | listeners.push(callback);
58 | },
59 | _triggerMessage: (message) => {
60 | listeners.forEach((callback) => callback(message));
61 | },
62 | removeListener: (callback) => {
63 | listeners = listeners.filter((c) => c === callback);
64 | },
65 | },
66 | sendMessage: function (message) {},
67 | },
68 | tabs: {
69 | query: (queryInfo: QueryInfo) => {
70 | return [{ id: 0, url: "" }];
71 | },
72 | sendMessage: (tabId, request) => {
73 | switch (request.message) {
74 | case "getRootComponent":
75 | {
76 | let message: {};
77 | if (_sendEmptyDataOnNextRequest) {
78 | message = {
79 | type: "returnRootComponent",
80 | rootComponent: undefined,
81 | };
82 | _sendEmptyDataOnNextRequest = false;
83 | } else {
84 | message = {
85 | type: "returnRootComponent",
86 | rootComponent: JSON.parse(JSON.stringify(data)),
87 | };
88 | }
89 | listeners.forEach((f) => f(message));
90 | }
91 | break;
92 | case "getSvelteVersion":
93 | {
94 | }
95 | break;
96 | case "handleClosedPanel":
97 | {
98 | }
99 | break;
100 |
101 | }
102 | },
103 | },
104 | clearListeners: function () {
105 | listeners = [];
106 | },
107 | resetMockData: function () {
108 | data = JSON.parse(JSON.stringify(initialData));
109 | },
110 | sendEmptyDataOnNextRequest: function () {
111 | _sendEmptyDataOnNextRequest = true;
112 | },
113 | };
114 |
115 | export default chrome;
116 |
--------------------------------------------------------------------------------
/src/lib/components/Editor/Expandable.svelte:
--------------------------------------------------------------------------------
1 |
46 |
47 |
48 |
49 | (expanded = !expanded)}
56 | >
57 | {key}:
58 |
59 |
60 | {#if type === "string"}
61 |
62 | {:else if value == null || value !== value}
63 |
64 | {:else if type === "number" || type === "boolean"}
65 |
66 | {:else if Array.isArray(value)}
67 | Array [{value.length || ""}]
68 |
69 | {#if value.length && expanded}
70 |
71 | {#each value as v, key}
72 |
77 | dispatch("change", stringify(value, key, e.detail))}
78 | />
79 | {/each}
80 |
81 | {/if}
82 | {:else if type === "object"}
83 | {#if value.__is === "function"}
84 | function {value.name || ""}()
85 | {#if expanded}{value.source} {/if}
86 | {:else if value.__is === "symbol"}
87 | {value.name || "Symbol()"}
88 | {:else if Object.keys(value).length}
89 | Object {…}
90 | {#if expanded}
91 |
92 | {#each Object.entries(value) as [key, v] (key)}
93 |
98 | dispatch("change", stringify(value, key, e.detail))}
99 | />
100 | {/each}
101 |
102 | {/if}
103 | {:else}
104 | Object { }
105 | {/if}
106 | {/if}
107 |
108 |
109 |
163 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | SvelteScope is a Google Chrome Extension specifically designed for Svelte webpages, offering developers a platform to test webpage behaviors and conduct component experimentation within the browser environment. Its features include an interactive visual tree diagram, real-time rendering of state changes, and the ability for developers to create multiple snapshots, all aimed at streamlining front-end testing processes.
11 |
12 | ## Key Features
13 |
14 | SvelteScope provides developers with an interactive tree diagram to explore component relationships, streamlining the comprehension of the application's structure. Additionally, developers can click on individual components to navigate their properties, state, and events.
15 |
16 |
17 |
18 |
19 |
20 |
21 | Generate test data scenarios by adjusting component props directly on the SvelteScope app and observe immediate rendering updates directly within the webpage.
22 |
23 |
24 |
25 |
26 |
27 |
28 | Front-end testing often involves extensive trials to pinpoint the source of issues. To facilitate tracking and comparison of tests, SvelteScope offers developers the ability to create multiple snapshots. Within SvelteScope, developers can organize these snapshots into tabs, enabling easy addition and removal as needed.
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | ## Installation and Usage Guide for SvelteScope
37 |
38 | To install the SvelteScope developer tool onto Chrome browser, please visit Chrome Web Store.
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | (Developer: If you're interested in contributing to this project, please refer to the Contribution section below for instructions)
48 |
49 |
50 | If you haven't already, enable the extension by following these steps:
51 | 1. Click on the three dots (⋮) located at the top right corner of the browser window to open the Chrome menu.
52 | 2. From the menu, select "More tools," then choose "Extensions." This will open the Extensions page.
53 | 3. Locate the extension you want to enable in the list of installed extensions.
54 | 4. Toggle the switch next to the extension to the right (gray to blue) to enable it.
55 |
56 |
57 | **Usuage Guidelines**
58 |
59 | SvelteScope only functions on locally hosted Svelte web pages.
60 | * Access the developer tools by right-clicking anywhere on the webpage and selecting "Inspect" from the context menu.
61 | * Locate and click on 'SvelteScope' in the top ribbon of the developer tools.
62 | * Explore webpages to observe the real-time rendering of a visual tree component for the Svelte App.
63 | * Select components in the visual tree diagram to access their states.
64 | * Modify component props, events, and states using the editor box, and observe live changes on the webpage.
65 | * Generate additional snapshots by clicking the "+" icon. Delete snapshots by clicking the "x" icon. Reset all tabs by clicking "reset".
66 |
67 |
68 |
69 |
70 |
71 |
72 | We greatly encourage contributions from the community. Your input, suggestions, and feedback play a crucial role in advancing the project. Below are ways in which you can contribute:
73 |
74 | - Review our code and provide feedback.
75 | - Test the extension and report any bugs or issues you encounter.
76 | - Suggest new features or improvements.
77 | - Submit pull requests to address open issues or implement new features.
78 |
79 | Instructions to download repository.
80 |
81 | 1. Access the SvelteScope GitHub repository.
82 | 2. Fork the repository to create personal working of the repository.
83 | 3. Clone or download the the fork to your local machine.
84 | 4. Install the dependencies
85 | ``` npm install ```
86 | 5. Once the dependencies are installed, run the following command to build the Svelte extension to compile the code.
87 | ``` npm run build ```
88 | 6. Save all changes.
89 | 7. Open Chrome and go to chrome://extensions/.
90 | 7. Enable developer mode by toggling the switch in the top right corner.
91 | 8. Click on "Load unpacked" in the top left corner.
92 | 9. Navigate to the directory where your local repository is located and select the build folder to load.
93 |
94 | ## Contributors
95 | - Branden Basche • [LinkedIn](https://www.linkedin.com/in/brandenbasche/) • [Github](https://github.com/brandenrbasche)
96 | - James Lee • [LinkedIn](https://www.linkedin.com/in/james-lee-a7b2842b6/) • [Github](https://github.com/alphajames258)
97 | - Binh Nguyen • [LinkedIn](https://www.linkedin.com/in/binh-nguyen-a07731101/) • [Github](https://github.com/binhnguyen96/)
98 | - Gregory Valentin • [LinkedIn](https://www.linkedin.com/in/gregory-valentin-a389b3221/) • [Github](https://github.com/punkygreg)
99 | - Donald Wong • [LinkedIn](https://www.linkedin.com/in/donald-wong-93702931) • [Github](https://github.com/dwong92)
100 |
101 | Visit us online
102 |
103 |
104 |
105 |
106 |
107 |
108 |
--------------------------------------------------------------------------------
/static/ContentScripts/svelte-listener/svelte.js:
--------------------------------------------------------------------------------
1 | import { add, update, remove } from "./listener.js";
2 | import { updateProfile } from "./profiler.js";
3 |
4 | const nodeMap = new Map();
5 | let _id = 0;
6 | let currentBlock;
7 |
8 | export function getNode(id) {
9 | return nodeMap.get(id);
10 | }
11 |
12 | export function getAllNodes() {
13 | nodeMap.values();
14 | }
15 |
16 | const rootNodes = [];
17 | export function getRootNodes() {
18 | return rootNodes;
19 | }
20 |
21 | let svelteVersion = null;
22 | export function getSvelteVersion() {
23 | return svelteVersion;
24 | }
25 |
26 | function addNode(node, target, anchor) {
27 | nodeMap.set(node.id, node);
28 | nodeMap.set(node.detail, node);
29 |
30 | let targetNode = nodeMap.get(target);
31 | if (!targetNode || targetNode.parentBlock != node.parentBlock) {
32 | targetNode = node.parentBlock;
33 | }
34 |
35 | node.parent = targetNode;
36 |
37 | const anchorNode = nodeMap.get(anchor);
38 |
39 | if (targetNode) {
40 | let index = -1;
41 | if (anchorNode) index = targetNode.children.indexOf(anchorNode);
42 |
43 | if (index != -1) {
44 | targetNode.children.splice(index, 0, node);
45 | } else {
46 | targetNode.children.push(node);
47 | }
48 | } else {
49 | rootNodes.push(node);
50 | }
51 |
52 | add(node, anchorNode);
53 | }
54 |
55 | function removeNode(node) {
56 | if (!node) return;
57 |
58 | nodeMap.delete(node.id);
59 | nodeMap.delete(node.detail);
60 |
61 | const index = node.parent.children.indexOf(node);
62 | node.parent.children.splice(index, 1);
63 | node.parent = null;
64 |
65 | remove(node);
66 | }
67 |
68 | function updateElement(element) {
69 | const node = nodeMap.get(element);
70 | if (!node) return;
71 |
72 | if (node.type == "anchor") node.type = "text";
73 |
74 | update(node);
75 | }
76 |
77 | function insert(element, target, anchor) {
78 | const node = {
79 | id: _id++,
80 | type:
81 | element.nodeType == 1
82 | ? "element"
83 | : element.nodeValue && element.nodeValue != " "
84 | ? "text"
85 | : "anchor",
86 | detail: element,
87 | tagName: element.nodeName.toLowerCase(),
88 | parentBlock: currentBlock,
89 | children: [],
90 | };
91 | addNode(node, target, anchor);
92 |
93 | for (const child of element.childNodes) {
94 | if (!nodeMap.has(child)) insert(child, element);
95 | }
96 | }
97 |
98 | function svelteRegisterComponent(e) {
99 | if (e.detail !== null) {
100 | const { component, tagName } = e.detail;
101 |
102 | const node = nodeMap.get(component.$$.fragment);
103 | if (node) {
104 | nodeMap.delete(component.$$.fragment);
105 |
106 | node.detail = component;
107 | node.tagName = tagName;
108 |
109 | update(node);
110 | } else {
111 | nodeMap.set(component.$$.fragment, {
112 | type: "component",
113 | detail: component,
114 | tagName,
115 | });
116 | }
117 | }
118 | }
119 |
120 | // Ugly hack b/c promises are resolved/rejected outside of normal render flow
121 | let lastPromiseParent = null;
122 | function svelteRegisterBlock(e) {
123 | if (e.detail !== null) {
124 | const { type, id, block, ...detail } = e.detail;
125 |
126 | const tagName = type == "pending" ? "await" : type;
127 | const nodeId = _id++;
128 |
129 | if (block.m) {
130 | const mountFn = block.m;
131 | block.m = (target, anchor) => {
132 | const parentBlock = currentBlock;
133 | let node = {
134 | id: nodeId,
135 | type: "block",
136 | detail,
137 | tagName,
138 | parentBlock,
139 | children: [],
140 | };
141 |
142 | switch (type) {
143 | case "then":
144 | case "catch":
145 | if (!node.parentBlock) node.parentBlock = lastPromiseParent;
146 | break;
147 |
148 | case "slot":
149 | node.type = "slot";
150 | break;
151 |
152 | case "component":
153 | const componentNode = nodeMap.get(block);
154 | if (componentNode) {
155 | nodeMap.delete(block);
156 | Object.assign(node, componentNode);
157 | } else {
158 | Object.assign(node, {
159 | type: "component",
160 | tagName: "Unknown",
161 | detail: {},
162 | });
163 | nodeMap.set(block, node);
164 | }
165 |
166 | Promise.resolve().then(
167 | () =>
168 | node.detail.$$ &&
169 | Object.keys(node.detail.$$.bound).length &&
170 | update(node)
171 | );
172 | break;
173 | }
174 |
175 | if (type == "each") {
176 | let group = nodeMap.get(parentBlock.id + id);
177 | if (!group) {
178 | group = {
179 | id: _id++,
180 | type: "block",
181 | detail: {
182 | ctx: {},
183 | source: detail.source,
184 | },
185 | tagName: "each",
186 | parentBlock,
187 | children: [],
188 | };
189 | nodeMap.set(parentBlock.id + id, group);
190 | addNode(group, target, anchor);
191 | }
192 | node.parentBlock = group;
193 | node.type = "iteration";
194 | addNode(node, group, anchor);
195 | } else {
196 | addNode(node, target, anchor);
197 | }
198 |
199 | currentBlock = node;
200 | updateProfile(node, "mount", mountFn, target, anchor);
201 | currentBlock = parentBlock;
202 | };
203 | }
204 |
205 | if (block.p) {
206 | const patchFn = block.p;
207 | block.p = (changed, ctx) => {
208 | const parentBlock = currentBlock;
209 | currentBlock = nodeMap.get(nodeId);
210 |
211 | update(currentBlock);
212 |
213 | updateProfile(currentBlock, "patch", patchFn, changed, ctx);
214 |
215 | currentBlock = parentBlock;
216 | };
217 | }
218 |
219 | if (block.d) {
220 | const detachFn = block.d;
221 | block.d = (detaching) => {
222 | const node = nodeMap.get(nodeId);
223 |
224 | if (node) {
225 | if (node.tagName == "await") lastPromiseParent = node.parentBlock;
226 |
227 | removeNode(node);
228 | }
229 |
230 | updateProfile(node, "detach", detachFn, detaching);
231 | };
232 | }
233 | }
234 | }
235 |
236 | function svelteDOMInsert(e) {
237 | if (e.detail !== null) {
238 | const { node: element, target, anchor } = e.detail;
239 |
240 | insert(element, target, anchor);
241 | }
242 | }
243 |
244 | function svelteDOMRemove(e) {
245 | if (e.detail !== null) {
246 | const node = nodeMap.get(e.detail.node);
247 | if (!node) return;
248 |
249 | removeNode(node);
250 | }
251 | }
252 |
253 | function svelteDOMAddEventListener(e) {
254 | if (e.detail !== null) {
255 | const { node, ...detail } = e.detail;
256 |
257 | if (!node.__listeners) node.__listeners = [];
258 |
259 | node.__listeners.push(detail);
260 | }
261 | }
262 |
263 | function svelteDOMRemoveEventListener(e) {
264 | if(e.detail !== null){
265 | const { node, event, handler, modifiers } = e.detail;
266 |
267 | if (!node.__listeners) return;
268 |
269 | const index = node.__listeners.findIndex(
270 | (o) => o.event == event && o.handler == handler && o.modifiers == modifiers
271 | );
272 |
273 | if (index == -1) return;
274 |
275 | node.__listeners.splice(index, 1);
276 | }
277 | }
278 |
279 | function svelteUpdateNode(e) {
280 | if (e.detail !== null) {
281 | updateElement(e.detail.node);
282 | }
283 | }
284 |
285 | function setup(root) {
286 | root.addEventListener("SvelteRegisterBlock", (e) => {
287 | if (e.detail !== null) {
288 | (svelteVersion = e.detail.version), { once: true };
289 | }
290 | });
291 |
292 | root.addEventListener("SvelteRegisterComponent", svelteRegisterComponent);
293 | root.addEventListener("SvelteRegisterBlock", svelteRegisterBlock);
294 | root.addEventListener("SvelteDOMInsert", svelteDOMInsert);
295 | root.addEventListener("SvelteDOMRemove", svelteDOMRemove);
296 | root.addEventListener("SvelteDOMAddEventListener", svelteDOMAddEventListener);
297 | root.addEventListener(
298 | "SvelteDOMRemoveEventListener",
299 | svelteDOMRemoveEventListener
300 | );
301 | root.addEventListener("SvelteDOMSetData", svelteUpdateNode);
302 | root.addEventListener("SvelteDOMSetProperty", svelteUpdateNode);
303 | root.addEventListener("SvelteDOMSetAttribute", svelteUpdateNode);
304 | root.addEventListener("SvelteDOMRemoveAttribute", svelteUpdateNode);
305 | }
306 |
307 | setup(window.document);
308 | for (let i = 0; i < window.frames.length; i++) {
309 | const frame = window.frames[i];
310 | const root = frame.document;
311 | setup(root);
312 | const timer = setInterval(() => {
313 | if (root == frame.document) return;
314 | clearTimeout(timer);
315 | setup(frame.document);
316 | }, 0);
317 | root.addEventListener("readystatechange", (e) => clearTimeout(timer), {
318 | once: true,
319 | });
320 | }
321 |
--------------------------------------------------------------------------------
/static/ContentScripts/contentScript.js:
--------------------------------------------------------------------------------
1 | /*
2 | This is the MAIN content script! This is the script that gets injected into
3 | whatever webpage you're on. This means it not only has the ability to access
4 | the DOM, it can listen for events that get emitted from developer builds of
5 | Svelte applications.
6 | */
7 |
8 | // import { getNode, getSvelteVersion, getRootNodes } from "svelte-listener";
9 | import { getNode, getSvelteVersion, getRootNodes } from './svelte-listener/index'
10 |
11 | console.log('You are using SvelteScope: Svelte Debugging Tool')
12 |
13 | // @ts-ignore - possibly find an alternative
14 | window.__svelte_devtools_inject_state = function (id, key, value) {
15 | const { detail: component } = getNode(id) || {};
16 | if (typeof value === "object") {
17 | component && component.$inject_state({ [key]: value });
18 | }
19 | component && component.$inject_state({ [key]: value });
20 |
21 | };
22 |
23 |
24 |
25 |
26 | function clone(value, seen = new Map()) {
27 | switch (typeof value) {
28 | case "function":
29 | return { __isFunction: true, source: value.toString(), name: value.name };
30 | case "symbol":
31 | return { __isSymbol: true, name: value.toString() };
32 | case "object": {
33 | if (value === window || value === null) return null;
34 | if (Array.isArray(value)) return value.map((o) => clone(o, seen));
35 | if (seen.has(value)) return {};
36 | const o = {};
37 | seen.set(value, o);
38 | for (const [key, v] of Object.entries(value)) {
39 | o[key] = clone(v, seen);
40 | }
41 | return o;
42 | }
43 | default:
44 | return value;
45 | }
46 | }
47 |
48 | function gte(major, minor, patch) {
49 | const version = (getSvelteVersion() || "0.0.0")
50 | .split(".")
51 | .map((n) => parseInt(n));
52 | return (
53 | version[0] > major ||
54 | (version[0] == major &&
55 | (version[1] > minor || (version[1] == minor && version[2] >= patch)))
56 | );
57 | }
58 |
59 | let _shouldUseCapture = null;
60 | function shouldUseCapture() {
61 | return _shouldUseCapture == null
62 | ? (_shouldUseCapture = gte(3, 19, 2))
63 | : _shouldUseCapture;
64 | }
65 |
66 | // This is a global variable to let us know if the page has been loaded or not
67 | let pageLoaded = false;
68 |
69 | // At this time, this content script only gets Svelte component data once
70 | window.addEventListener("load", (event) => {
71 | pageLoaded = true;
72 | });
73 |
74 | // Gets the root component from svelte listener and returns
75 | // a component tree starting with the root component
76 | function traverseComponent(node) {
77 | let components = [];
78 | node.children.forEach((child) => {
79 | if (child.type === "component" && child.detail.$$) {
80 | const serialized = {
81 | id: child.id,
82 | type: child.type,
83 | tagName: child.tagName,
84 | children: traverseComponent(child),
85 | };
86 |
87 | const internal = child.detail.$$;
88 | const props = Array.isArray(internal.props)
89 | ? internal.props // Svelte < 3.13.0 stored props names as an array
90 | : Object.keys(internal.props);
91 | let ctx = clone(
92 | shouldUseCapture() ? child.detail.$capture_state() : internal.ctx
93 | );
94 | if (ctx === undefined) ctx = {};
95 | serialized.detail = {
96 | attributes: props.flatMap((key) => {
97 | const value = ctx[key];
98 | delete ctx[key];
99 | return value === undefined
100 | ? []
101 | : { key, value, isBound: key in internal.bound };
102 | }),
103 | listeners: Object.entries(internal.callbacks).flatMap(
104 | ([event, value]) =>
105 | value.map((o) => ({ event, handler: o.toString() }))
106 | ),
107 | ctx: Object.entries(ctx).map(([key, value]) => ({ key, value })),
108 | };
109 | components.push(serialized);
110 | } else {
111 | components = components.concat(traverseComponent(child));
112 | }
113 | });
114 | return components;
115 | }
116 |
117 | // Gets component tree using svelte listener and sends it to the dev tool panel
118 | function sendRootNodeToExtension(messageType) {
119 | if (
120 | messageType !== "updateRootComponent" &&
121 | messageType !== "returnRootComponent" &&
122 | messageType !== "returnTempRoot"
123 | ) {
124 | return;
125 | }
126 |
127 |
128 | const rootNodes = getRootNodes();
129 | const newRootNodes = traverseComponent({
130 | children: rootNodes,
131 | type: "component",
132 | });
133 | if (!newRootNodes) {
134 | return;
135 | }
136 | const newRootNode = newRootNodes[0];
137 | window.postMessage({
138 | type: messageType,
139 | rootComponent: newRootNode,
140 | source: "contentScript.js",
141 | });
142 | }
143 |
144 | // Gets svelte version using svelte listener and sends it to
145 | // the Popup box
146 | function sendSvelteVersionToExtension() {
147 | const svelteVersion = getSvelteVersion();
148 | if (!svelteVersion) {
149 | return;
150 | }
151 |
152 | // Sends a message to ContentScriptIsolated/index.js
153 | window.postMessage({
154 | type: "returnSvelteVersion",
155 | svelteVersion: svelteVersion,
156 | source: "contentScript.js",
157 | });
158 | }
159 |
160 | function injectState(id, newState) {
161 | recentlyUpdated = true;
162 | setTimeout(() => {
163 | recentlyUpdated = false;
164 | }, 0);
165 | const component = id.detail;
166 | component.$inject_state(newState);
167 |
168 | /*
169 | Sends a new snapshot to the panel. If there is a state change that doesn't
170 | modify the DOM, the event listeners won't hear any events. Let's just send
171 | back a new updated root node for all state injections
172 | */
173 | sendRootNodeToExtension("updateRootComponent");
174 | }
175 |
176 | // When state is injected, an event is emitted by the Svelte app
177 | // This forces the app to ignore those updates
178 | let recentlyInjectedASnapshot = false;
179 | function injectSnapshot(snapshot) {
180 | recentlyInjectedASnapshot = true;
181 | const listOfIds = [];
182 | const listOfStates = [];
183 | function getComponentData(component) {
184 | listOfIds.push(component.id);
185 | const newState = {};
186 | component.detail.ctx.forEach((i) => {
187 | newState[i.key] = i.value;
188 | });
189 | listOfStates.push(newState);
190 | component.children.forEach((child) => {
191 | getComponentData(child);
192 | });
193 | }
194 | getComponentData(snapshot);
195 | for (let i = 0; i < listOfIds.length; i++) {
196 | const component = getNode(listOfIds[i]);
197 | if (component) {
198 | const detail = component.detail;
199 | detail.$inject_state(listOfStates[i]);
200 | }
201 | }
202 | // When state is injected, an event is emitted by the Svelte app
203 | // This forces the app to ignore those updates
204 | recentlyUpdated = true;
205 | setTimeout(() => {
206 | recentlyUpdated = false;
207 | recentlyInjectedASnapshot = false;
208 | }, 0);
209 | }
210 |
211 | //--------------------------------------------------------------------------------------
212 | let readyForUpdates = false;
213 | // Listens to events from ContentScriptIsolated/index.js and responds based on
214 | // the event's type
215 | window.addEventListener("message", async (msg) => {
216 | if (
217 | typeof msg !== "object" ||
218 | msg === null ||
219 | msg.data?.source !== "contentScriptIsolate.js"
220 | ) {
221 | return;
222 | }
223 |
224 | const data = msg.data;
225 |
226 | switch (data.type) {
227 | case "getSvelteVersion":
228 | sendSvelteVersionToExtension();
229 | break;
230 | case "getRootComponent":
231 | readyForUpdates = true;
232 | sendRootNodeToExtension("returnRootComponent");
233 | break;
234 | case "handleClosedPanel":
235 | readyForUpdates = false;
236 | break;
237 | case "injectState":
238 | injectState(data.componentId, data.newState);
239 | break;
240 | case "injectSnapshot":
241 | injectSnapshot(data.snapshot);
242 | // Before this setTimeout was added, we were sending back
243 | // a snapshot that hadn't been updated yet
244 | setTimeout(() => {
245 | sendRootNodeToExtension("returnTempRoot");
246 | }, 0);
247 | break;
248 | }
249 | });
250 |
251 | //--------------------------------------------------------------------------------------
252 |
253 | // Whenever nodes are updated, typically a bunch get updated at the same time
254 | // I need to throttle updates so when I get a bunch at once, I only send the
255 | // LATEST update
256 | let recentlyUpdated = false;
257 | function sendUpdateToPanel() {
258 | // This should only happen after the DOM is fully loaded
259 | // And after the Panel is loaded.
260 | // if (!pageLoaded || !readyForUpdates) return;
261 | if (!pageLoaded || !readyForUpdates) return;
262 | if (recentlyInjectedASnapshot) return;
263 | // This needs a setTimeout because it MUST run AFTER the svelte-listener events fire
264 | // Send the devtool panel an updated root component whenever the Svelte DOM changes
265 | if (recentlyUpdated === false) {
266 | setTimeout(() => {
267 | recentlyUpdated = false;
268 | sendRootNodeToExtension("updateRootComponent");
269 | }, 0);
270 | recentlyUpdated = true;
271 | }
272 | }
273 |
274 | window.document.addEventListener("SvelteRegisterComponent", sendUpdateToPanel);
275 | window.document.addEventListener("SvelteRegisterBlock", sendUpdateToPanel);
276 | window.document.addEventListener("SvelteDOMInsert", (e) => {
277 | sendUpdateToPanel;
278 | });
279 | window.document.addEventListener("SvelteDOMRemove", sendUpdateToPanel);
280 | window.document.addEventListener("SvelteDOMSetData", sendUpdateToPanel);
281 | window.document.addEventListener("SvelteDOMSetProperty", sendUpdateToPanel);
282 | window.document.addEventListener("SvelteDOMSetAttribute", sendUpdateToPanel);
283 |
284 |
285 |
--------------------------------------------------------------------------------
/src/lib/components/Tree.svelte:
--------------------------------------------------------------------------------
1 |
295 |
296 |
297 |
298 |
307 |
314 |
317 |
318 |
319 |
320 | Re-center diagram
321 |
322 |
327 |
328 |
329 |
368 |
--------------------------------------------------------------------------------
/src/lib/containers/TabAdder.svelte:
--------------------------------------------------------------------------------
1 |
282 |
283 |
325 |
326 |
327 | {#each Object.entries(items) as [key, value]}
328 |
329 | {key}
330 |
331 | {/each}
332 | +
333 |
334 |
335 | {#each Object.entries(items) as [key, value]}
336 | {#if activeTabValue == value.value}
337 |
338 |
339 | {#if currentdata !== undefined}
340 |
341 | Currently editing: {currentdata}
344 |
345 | {/if}
346 | {#if currentdata === undefined}
347 |
348 | Currently editing: {root}
349 |
350 | {/if}
351 | {#if Object.keys(items).length > 1}
352 | ✖️
353 | {:else}
354 | ✖️
360 | {/if}
361 |
362 |
363 |
364 | {/if}
365 | {/each}
366 |
367 |
562 |
--------------------------------------------------------------------------------
/__mocks__/mockData.ts:
--------------------------------------------------------------------------------
1 | const mockData = {
2 | id: 25,
3 | type: 'component',
4 | tagName: 'App',
5 | children: [
6 | {
7 | id: 24,
8 | type: 'component',
9 | tagName: 'Board',
10 | children: [
11 | {
12 | id: 6,
13 | type: 'component',
14 | tagName: 'Row',
15 | children: [
16 | {
17 | id: 0,
18 | type: 'component',
19 | tagName: 'Box',
20 | children: [],
21 | detail: {
22 | attributes: [
23 | { key: 'boxState', value: '-', isBound: false },
24 | {
25 | key: 'handleClick',
26 | value: {
27 | __isFunction: true,
28 | source:
29 | "function handleClick(x, y) {\n \t\tif (state[x][y] !== '-') return;\n \t\tif (gameOver) return;\n \t\tturnCount++;\n\n \t\tif (turnCount === 9) {\n \t\t\t$$invalidate(2, gameOver = true);\n \t\t\tsetWinner('draw');\n \t\t}\n\n \t\t$$invalidate(1, state[x][y] = turn, state);\n \t\tconst test = [];\n\n \t\tfor (let x = 0; x < state.length; x++) {\n \t\t\tfor (let y = 0; y < state[0].length; y++) {\n \t\t\t\ttest.push(state[x][y]);\n \t\t\t}\n \t\t}\n\n \t\twinningConditions.forEach(c => {\n \t\t\tif (test[c[0]] === test[c[1]] && test[c[1]] === test[c[2]] && test[c[0]] === test[c[2]] && test[c[0]] !== '-') {\n \t\t\t\t$$invalidate(2, gameOver = true);\n \t\t\t\tsetWinner(turn);\n \t\t\t}\n \t\t});\n\n \t\tturn = turn === 'X' ? 'O' : 'X';\n \t}",
30 | name: 'handleClick',
31 | },
32 | isBound: false,
33 | },
34 | { key: 'rowIndex', value: 0, isBound: false },
35 | { key: 'boxIndex', value: 0, isBound: false },
36 | ],
37 | listeners: [],
38 | ctx: [{ key: 'state', value: '' }],
39 | },
40 | },
41 | {
42 | id: 2,
43 | type: 'component',
44 | tagName: 'Box',
45 | children: [],
46 | detail: {
47 | attributes: [
48 | { key: 'boxState', value: '-', isBound: false },
49 | {
50 | key: 'handleClick',
51 | value: {
52 | __isFunction: true,
53 | source:
54 | "function handleClick(x, y) {\n \t\tif (state[x][y] !== '-') return;\n \t\tif (gameOver) return;\n \t\tturnCount++;\n\n \t\tif (turnCount === 9) {\n \t\t\t$$invalidate(2, gameOver = true);\n \t\t\tsetWinner('draw');\n \t\t}\n\n \t\t$$invalidate(1, state[x][y] = turn, state);\n \t\tconst test = [];\n\n \t\tfor (let x = 0; x < state.length; x++) {\n \t\t\tfor (let y = 0; y < state[0].length; y++) {\n \t\t\t\ttest.push(state[x][y]);\n \t\t\t}\n \t\t}\n\n \t\twinningConditions.forEach(c => {\n \t\t\tif (test[c[0]] === test[c[1]] && test[c[1]] === test[c[2]] && test[c[0]] === test[c[2]] && test[c[0]] !== '-') {\n \t\t\t\t$$invalidate(2, gameOver = true);\n \t\t\t\tsetWinner(turn);\n \t\t\t}\n \t\t});\n\n \t\tturn = turn === 'X' ? 'O' : 'X';\n \t}",
55 | name: 'handleClick',
56 | },
57 | isBound: false,
58 | },
59 | { key: 'rowIndex', value: 0, isBound: false },
60 | { key: 'boxIndex', value: 1, isBound: false },
61 | ],
62 | listeners: [],
63 | ctx: [{ key: 'state', value: '' }],
64 | },
65 | },
66 | {
67 | id: 4,
68 | type: 'component',
69 | tagName: 'Box',
70 | children: [],
71 | detail: {
72 | attributes: [
73 | { key: 'boxState', value: '-', isBound: false },
74 | {
75 | key: 'handleClick',
76 | value: {
77 | __isFunction: true,
78 | source:
79 | "function handleClick(x, y) {\n \t\tif (state[x][y] !== '-') return;\n \t\tif (gameOver) return;\n \t\tturnCount++;\n\n \t\tif (turnCount === 9) {\n \t\t\t$$invalidate(2, gameOver = true);\n \t\t\tsetWinner('draw');\n \t\t}\n\n \t\t$$invalidate(1, state[x][y] = turn, state);\n \t\tconst test = [];\n\n \t\tfor (let x = 0; x < state.length; x++) {\n \t\t\tfor (let y = 0; y < state[0].length; y++) {\n \t\t\t\ttest.push(state[x][y]);\n \t\t\t}\n \t\t}\n\n \t\twinningConditions.forEach(c => {\n \t\t\tif (test[c[0]] === test[c[1]] && test[c[1]] === test[c[2]] && test[c[0]] === test[c[2]] && test[c[0]] !== '-') {\n \t\t\t\t$$invalidate(2, gameOver = true);\n \t\t\t\tsetWinner(turn);\n \t\t\t}\n \t\t});\n\n \t\tturn = turn === 'X' ? 'O' : 'X';\n \t}",
80 | name: 'handleClick',
81 | },
82 | isBound: false,
83 | },
84 | { key: 'rowIndex', value: 0, isBound: false },
85 | { key: 'boxIndex', value: 2, isBound: false },
86 | ],
87 | listeners: [],
88 | ctx: [{ key: 'state', value: '' }],
89 | },
90 | },
91 | ],
92 | detail: {
93 | attributes: [
94 | { key: 'rowState', value: ['-', '-', '-'], isBound: false },
95 | {
96 | key: 'handleClick',
97 | value: {
98 | __isFunction: true,
99 | source:
100 | "function handleClick(x, y) {\n \t\tif (state[x][y] !== '-') return;\n \t\tif (gameOver) return;\n \t\tturnCount++;\n\n \t\tif (turnCount === 9) {\n \t\t\t$$invalidate(2, gameOver = true);\n \t\t\tsetWinner('draw');\n \t\t}\n\n \t\t$$invalidate(1, state[x][y] = turn, state);\n \t\tconst test = [];\n\n \t\tfor (let x = 0; x < state.length; x++) {\n \t\t\tfor (let y = 0; y < state[0].length; y++) {\n \t\t\t\ttest.push(state[x][y]);\n \t\t\t}\n \t\t}\n\n \t\twinningConditions.forEach(c => {\n \t\t\tif (test[c[0]] === test[c[1]] && test[c[1]] === test[c[2]] && test[c[0]] === test[c[2]] && test[c[0]] !== '-') {\n \t\t\t\t$$invalidate(2, gameOver = true);\n \t\t\t\tsetWinner(turn);\n \t\t\t}\n \t\t});\n\n \t\tturn = turn === 'X' ? 'O' : 'X';\n \t}",
101 | name: 'handleClick',
102 | },
103 | isBound: false,
104 | },
105 | { key: 'rowIndex', value: 0, isBound: false },
106 | ],
107 | listeners: [],
108 | ctx: [
109 | {
110 | key: 'Box',
111 | value: {
112 | __isFunction: true,
113 | source:
114 | "class Box extends SvelteComponentDev {\n \tconstructor(options) {\n \t\tsuper(options);\n\n \t\tinit(this, options, instance$4, create_fragment$4, safe_not_equal, {\n \t\t\tboxState: 0,\n \t\t\thandleClick: 1,\n \t\t\trowIndex: 2,\n \t\t\tboxIndex: 3\n \t\t});\n\n \t\tdispatch_dev(\"SvelteRegisterComponent\", {\n \t\t\tcomponent: this,\n \t\t\ttagName: \"Box\",\n \t\t\toptions,\n \t\t\tid: create_fragment$4.name\n \t\t});\n \t}\n\n \tget boxState() {\n \t\tthrow new Error(\": Props cannot be read directly from the component instance unless compiling with 'accessors: true' or ' '\");\n \t}\n\n \tset boxState(value) {\n \t\tthrow new Error(\": Props cannot be set directly on the component instance unless compiling with 'accessors: true' or ' '\");\n \t}\n\n \tget handleClick() {\n \t\tthrow new Error(\": Props cannot be read directly from the component instance unless compiling with 'accessors: true' or ' '\");\n \t}\n\n \tset handleClick(value) {\n \t\tthrow new Error(\": Props cannot be set directly on the component instance unless compiling with 'accessors: true' or ' '\");\n \t}\n\n \tget rowIndex() {\n \t\tthrow new Error(\": Props cannot be read directly from the component instance unless compiling with 'accessors: true' or ' '\");\n \t}\n\n \tset rowIndex(value) {\n \t\tthrow new Error(\": Props cannot be set directly on the component instance unless compiling with 'accessors: true' or ' '\");\n \t}\n\n \tget boxIndex() {\n \t\tthrow new Error(\": Props cannot be read directly from the component instance unless compiling with 'accessors: true' or ' '\");\n \t}\n\n \tset boxIndex(value) {\n \t\tthrow new Error(\": Props cannot be set directly on the component instance unless compiling with 'accessors: true' or ' '\");\n \t}\n }",
115 | name: 'Box',
116 | },
117 | },
118 | { key: 'stateeee', value: '' },
119 | ],
120 | },
121 | },
122 | {
123 | id: 14,
124 | type: 'component',
125 | tagName: 'Row',
126 | children: [
127 | {
128 | id: 8,
129 | type: 'component',
130 | tagName: 'Box',
131 | children: [],
132 | detail: {
133 | attributes: [
134 | { key: 'boxState', value: '-', isBound: false },
135 | {
136 | key: 'handleClick',
137 | value: {
138 | __isFunction: true,
139 | source:
140 | "function handleClick(x, y) {\n \t\tif (state[x][y] !== '-') return;\n \t\tif (gameOver) return;\n \t\tturnCount++;\n\n \t\tif (turnCount === 9) {\n \t\t\t$$invalidate(2, gameOver = true);\n \t\t\tsetWinner('draw');\n \t\t}\n\n \t\t$$invalidate(1, state[x][y] = turn, state);\n \t\tconst test = [];\n\n \t\tfor (let x = 0; x < state.length; x++) {\n \t\t\tfor (let y = 0; y < state[0].length; y++) {\n \t\t\t\ttest.push(state[x][y]);\n \t\t\t}\n \t\t}\n\n \t\twinningConditions.forEach(c => {\n \t\t\tif (test[c[0]] === test[c[1]] && test[c[1]] === test[c[2]] && test[c[0]] === test[c[2]] && test[c[0]] !== '-') {\n \t\t\t\t$$invalidate(2, gameOver = true);\n \t\t\t\tsetWinner(turn);\n \t\t\t}\n \t\t});\n\n \t\tturn = turn === 'X' ? 'O' : 'X';\n \t}",
141 | name: 'handleClick',
142 | },
143 | isBound: false,
144 | },
145 | { key: 'rowIndex', value: 1, isBound: false },
146 | { key: 'boxIndex', value: 0, isBound: false },
147 | ],
148 | listeners: [],
149 | ctx: [{ key: 'state', value: '' }],
150 | },
151 | },
152 | {
153 | id: 10,
154 | type: 'component',
155 | tagName: 'Box',
156 | children: [],
157 | detail: {
158 | attributes: [
159 | { key: 'boxState', value: '-', isBound: false },
160 | {
161 | key: 'handleClick',
162 | value: {
163 | __isFunction: true,
164 | source:
165 | "function handleClick(x, y) {\n \t\tif (state[x][y] !== '-') return;\n \t\tif (gameOver) return;\n \t\tturnCount++;\n\n \t\tif (turnCount === 9) {\n \t\t\t$$invalidate(2, gameOver = true);\n \t\t\tsetWinner('draw');\n \t\t}\n\n \t\t$$invalidate(1, state[x][y] = turn, state);\n \t\tconst test = [];\n\n \t\tfor (let x = 0; x < state.length; x++) {\n \t\t\tfor (let y = 0; y < state[0].length; y++) {\n \t\t\t\ttest.push(state[x][y]);\n \t\t\t}\n \t\t}\n\n \t\twinningConditions.forEach(c => {\n \t\t\tif (test[c[0]] === test[c[1]] && test[c[1]] === test[c[2]] && test[c[0]] === test[c[2]] && test[c[0]] !== '-') {\n \t\t\t\t$$invalidate(2, gameOver = true);\n \t\t\t\tsetWinner(turn);\n \t\t\t}\n \t\t});\n\n \t\tturn = turn === 'X' ? 'O' : 'X';\n \t}",
166 | name: 'handleClick',
167 | },
168 | isBound: false,
169 | },
170 | { key: 'rowIndex', value: 1, isBound: false },
171 | { key: 'boxIndex', value: 1, isBound: false },
172 | ],
173 | listeners: [],
174 | ctx: [{ key: 'state', value: '' }],
175 | },
176 | },
177 | {
178 | id: 12,
179 | type: 'component',
180 | tagName: 'Box',
181 | children: [],
182 | detail: {
183 | attributes: [
184 | { key: 'boxState', value: '-', isBound: false },
185 | {
186 | key: 'handleClick',
187 | value: {
188 | __isFunction: true,
189 | source:
190 | "function handleClick(x, y) {\n \t\tif (state[x][y] !== '-') return;\n \t\tif (gameOver) return;\n \t\tturnCount++;\n\n \t\tif (turnCount === 9) {\n \t\t\t$$invalidate(2, gameOver = true);\n \t\t\tsetWinner('draw');\n \t\t}\n\n \t\t$$invalidate(1, state[x][y] = turn, state);\n \t\tconst test = [];\n\n \t\tfor (let x = 0; x < state.length; x++) {\n \t\t\tfor (let y = 0; y < state[0].length; y++) {\n \t\t\t\ttest.push(state[x][y]);\n \t\t\t}\n \t\t}\n\n \t\twinningConditions.forEach(c => {\n \t\t\tif (test[c[0]] === test[c[1]] && test[c[1]] === test[c[2]] && test[c[0]] === test[c[2]] && test[c[0]] !== '-') {\n \t\t\t\t$$invalidate(2, gameOver = true);\n \t\t\t\tsetWinner(turn);\n \t\t\t}\n \t\t});\n\n \t\tturn = turn === 'X' ? 'O' : 'X';\n \t}",
191 | name: 'handleClick',
192 | },
193 | isBound: false,
194 | },
195 | { key: 'rowIndex', value: 1, isBound: false },
196 | { key: 'boxIndex', value: 2, isBound: false },
197 | ],
198 | listeners: [],
199 | ctx: [{ key: 'state', value: '' }],
200 | },
201 | },
202 | ],
203 | detail: {
204 | attributes: [
205 | { key: 'rowState', value: ['-', '-', '-'], isBound: false },
206 | {
207 | key: 'handleClick',
208 | value: {
209 | __isFunction: true,
210 | source:
211 | "function handleClick(x, y) {\n \t\tif (state[x][y] !== '-') return;\n \t\tif (gameOver) return;\n \t\tturnCount++;\n\n \t\tif (turnCount === 9) {\n \t\t\t$$invalidate(2, gameOver = true);\n \t\t\tsetWinner('draw');\n \t\t}\n\n \t\t$$invalidate(1, state[x][y] = turn, state);\n \t\tconst test = [];\n\n \t\tfor (let x = 0; x < state.length; x++) {\n \t\t\tfor (let y = 0; y < state[0].length; y++) {\n \t\t\t\ttest.push(state[x][y]);\n \t\t\t}\n \t\t}\n\n \t\twinningConditions.forEach(c => {\n \t\t\tif (test[c[0]] === test[c[1]] && test[c[1]] === test[c[2]] && test[c[0]] === test[c[2]] && test[c[0]] !== '-') {\n \t\t\t\t$$invalidate(2, gameOver = true);\n \t\t\t\tsetWinner(turn);\n \t\t\t}\n \t\t});\n\n \t\tturn = turn === 'X' ? 'O' : 'X';\n \t}",
212 | name: 'handleClick',
213 | },
214 | isBound: false,
215 | },
216 | { key: 'rowIndex', value: 1, isBound: false },
217 | ],
218 | listeners: [],
219 | ctx: [
220 | {
221 | key: 'Box',
222 | value: {
223 | __isFunction: true,
224 | source:
225 | "class Box extends SvelteComponentDev {\n \tconstructor(options) {\n \t\tsuper(options);\n\n \t\tinit(this, options, instance$4, create_fragment$4, safe_not_equal, {\n \t\t\tboxState: 0,\n \t\t\thandleClick: 1,\n \t\t\trowIndex: 2,\n \t\t\tboxIndex: 3\n \t\t});\n\n \t\tdispatch_dev(\"SvelteRegisterComponent\", {\n \t\t\tcomponent: this,\n \t\t\ttagName: \"Box\",\n \t\t\toptions,\n \t\t\tid: create_fragment$4.name\n \t\t});\n \t}\n\n \tget boxState() {\n \t\tthrow new Error(\": Props cannot be read directly from the component instance unless compiling with 'accessors: true' or ' '\");\n \t}\n\n \tset boxState(value) {\n \t\tthrow new Error(\": Props cannot be set directly on the component instance unless compiling with 'accessors: true' or ' '\");\n \t}\n\n \tget handleClick() {\n \t\tthrow new Error(\": Props cannot be read directly from the component instance unless compiling with 'accessors: true' or ' '\");\n \t}\n\n \tset handleClick(value) {\n \t\tthrow new Error(\": Props cannot be set directly on the component instance unless compiling with 'accessors: true' or ' '\");\n \t}\n\n \tget rowIndex() {\n \t\tthrow new Error(\": Props cannot be read directly from the component instance unless compiling with 'accessors: true' or ' '\");\n \t}\n\n \tset rowIndex(value) {\n \t\tthrow new Error(\": Props cannot be set directly on the component instance unless compiling with 'accessors: true' or ' '\");\n \t}\n\n \tget boxIndex() {\n \t\tthrow new Error(\": Props cannot be read directly from the component instance unless compiling with 'accessors: true' or ' '\");\n \t}\n\n \tset boxIndex(value) {\n \t\tthrow new Error(\": Props cannot be set directly on the component instance unless compiling with 'accessors: true' or ' '\");\n \t}\n }",
226 | name: 'Box',
227 | },
228 | },
229 | { key: 'stateeee', value: '' },
230 | ],
231 | },
232 | },
233 | {
234 | id: 22,
235 | type: 'component',
236 | tagName: 'Row',
237 | children: [
238 | {
239 | id: 16,
240 | type: 'component',
241 | tagName: 'Box',
242 | children: [],
243 | detail: {
244 | attributes: [
245 | { key: 'boxState', value: '-', isBound: false },
246 | {
247 | key: 'handleClick',
248 | value: {
249 | __isFunction: true,
250 | source:
251 | "function handleClick(x, y) {\n \t\tif (state[x][y] !== '-') return;\n \t\tif (gameOver) return;\n \t\tturnCount++;\n\n \t\tif (turnCount === 9) {\n \t\t\t$$invalidate(2, gameOver = true);\n \t\t\tsetWinner('draw');\n \t\t}\n\n \t\t$$invalidate(1, state[x][y] = turn, state);\n \t\tconst test = [];\n\n \t\tfor (let x = 0; x < state.length; x++) {\n \t\t\tfor (let y = 0; y < state[0].length; y++) {\n \t\t\t\ttest.push(state[x][y]);\n \t\t\t}\n \t\t}\n\n \t\twinningConditions.forEach(c => {\n \t\t\tif (test[c[0]] === test[c[1]] && test[c[1]] === test[c[2]] && test[c[0]] === test[c[2]] && test[c[0]] !== '-') {\n \t\t\t\t$$invalidate(2, gameOver = true);\n \t\t\t\tsetWinner(turn);\n \t\t\t}\n \t\t});\n\n \t\tturn = turn === 'X' ? 'O' : 'X';\n \t}",
252 | name: 'handleClick',
253 | },
254 | isBound: false,
255 | },
256 | { key: 'rowIndex', value: 2, isBound: false },
257 | { key: 'boxIndex', value: 0, isBound: false },
258 | ],
259 | listeners: [],
260 | ctx: [{ key: 'state', value: '' }],
261 | },
262 | },
263 | {
264 | id: 18,
265 | type: 'component',
266 | tagName: 'Box',
267 | children: [],
268 | detail: {
269 | attributes: [
270 | { key: 'boxState', value: '-', isBound: false },
271 | {
272 | key: 'handleClick',
273 | value: {
274 | __isFunction: true,
275 | source:
276 | "function handleClick(x, y) {\n \t\tif (state[x][y] !== '-') return;\n \t\tif (gameOver) return;\n \t\tturnCount++;\n\n \t\tif (turnCount === 9) {\n \t\t\t$$invalidate(2, gameOver = true);\n \t\t\tsetWinner('draw');\n \t\t}\n\n \t\t$$invalidate(1, state[x][y] = turn, state);\n \t\tconst test = [];\n\n \t\tfor (let x = 0; x < state.length; x++) {\n \t\t\tfor (let y = 0; y < state[0].length; y++) {\n \t\t\t\ttest.push(state[x][y]);\n \t\t\t}\n \t\t}\n\n \t\twinningConditions.forEach(c => {\n \t\t\tif (test[c[0]] === test[c[1]] && test[c[1]] === test[c[2]] && test[c[0]] === test[c[2]] && test[c[0]] !== '-') {\n \t\t\t\t$$invalidate(2, gameOver = true);\n \t\t\t\tsetWinner(turn);\n \t\t\t}\n \t\t});\n\n \t\tturn = turn === 'X' ? 'O' : 'X';\n \t}",
277 | name: 'handleClick',
278 | },
279 | isBound: false,
280 | },
281 | { key: 'rowIndex', value: 2, isBound: false },
282 | { key: 'boxIndex', value: 1, isBound: false },
283 | ],
284 | listeners: [],
285 | ctx: [{ key: 'state', value: '' }],
286 | },
287 | },
288 | {
289 | id: 20,
290 | type: 'component',
291 | tagName: 'Box',
292 | children: [],
293 | detail: {
294 | attributes: [
295 | { key: 'boxState', value: '-', isBound: false },
296 | {
297 | key: 'handleClick',
298 | value: {
299 | __isFunction: true,
300 | source:
301 | "function handleClick(x, y) {\n \t\tif (state[x][y] !== '-') return;\n \t\tif (gameOver) return;\n \t\tturnCount++;\n\n \t\tif (turnCount === 9) {\n \t\t\t$$invalidate(2, gameOver = true);\n \t\t\tsetWinner('draw');\n \t\t}\n\n \t\t$$invalidate(1, state[x][y] = turn, state);\n \t\tconst test = [];\n\n \t\tfor (let x = 0; x < state.length; x++) {\n \t\t\tfor (let y = 0; y < state[0].length; y++) {\n \t\t\t\ttest.push(state[x][y]);\n \t\t\t}\n \t\t}\n\n \t\twinningConditions.forEach(c => {\n \t\t\tif (test[c[0]] === test[c[1]] && test[c[1]] === test[c[2]] && test[c[0]] === test[c[2]] && test[c[0]] !== '-') {\n \t\t\t\t$$invalidate(2, gameOver = true);\n \t\t\t\tsetWinner(turn);\n \t\t\t}\n \t\t});\n\n \t\tturn = turn === 'X' ? 'O' : 'X';\n \t}",
302 | name: 'handleClick',
303 | },
304 | isBound: false,
305 | },
306 | { key: 'rowIndex', value: 2, isBound: false },
307 | { key: 'boxIndex', value: 2, isBound: false },
308 | ],
309 | listeners: [],
310 | ctx: [{ key: 'state', value: '' }],
311 | },
312 | },
313 | ],
314 | detail: {
315 | attributes: [
316 | { key: 'rowState', value: ['-', '-', '-'], isBound: false },
317 | {
318 | key: 'handleClick',
319 | value: {
320 | __isFunction: true,
321 | source:
322 | "function handleClick(x, y) {\n \t\tif (state[x][y] !== '-') return;\n \t\tif (gameOver) return;\n \t\tturnCount++;\n\n \t\tif (turnCount === 9) {\n \t\t\t$$invalidate(2, gameOver = true);\n \t\t\tsetWinner('draw');\n \t\t}\n\n \t\t$$invalidate(1, state[x][y] = turn, state);\n \t\tconst test = [];\n\n \t\tfor (let x = 0; x < state.length; x++) {\n \t\t\tfor (let y = 0; y < state[0].length; y++) {\n \t\t\t\ttest.push(state[x][y]);\n \t\t\t}\n \t\t}\n\n \t\twinningConditions.forEach(c => {\n \t\t\tif (test[c[0]] === test[c[1]] && test[c[1]] === test[c[2]] && test[c[0]] === test[c[2]] && test[c[0]] !== '-') {\n \t\t\t\t$$invalidate(2, gameOver = true);\n \t\t\t\tsetWinner(turn);\n \t\t\t}\n \t\t});\n\n \t\tturn = turn === 'X' ? 'O' : 'X';\n \t}",
323 | name: 'handleClick',
324 | },
325 | isBound: false,
326 | },
327 | { key: 'rowIndex', value: 2, isBound: false },
328 | ],
329 | listeners: [],
330 | ctx: [
331 | {
332 | key: 'Box',
333 | value: {
334 | __isFunction: true,
335 | source:
336 | "class Box extends SvelteComponentDev {\n \tconstructor(options) {\n \t\tsuper(options);\n\n \t\tinit(this, options, instance$4, create_fragment$4, safe_not_equal, {\n \t\t\tboxState: 0,\n \t\t\thandleClick: 1,\n \t\t\trowIndex: 2,\n \t\t\tboxIndex: 3\n \t\t});\n\n \t\tdispatch_dev(\"SvelteRegisterComponent\", {\n \t\t\tcomponent: this,\n \t\t\ttagName: \"Box\",\n \t\t\toptions,\n \t\t\tid: create_fragment$4.name\n \t\t});\n \t}\n\n \tget boxState() {\n \t\tthrow new Error(\": Props cannot be read directly from the component instance unless compiling with 'accessors: true' or ' '\");\n \t}\n\n \tset boxState(value) {\n \t\tthrow new Error(\": Props cannot be set directly on the component instance unless compiling with 'accessors: true' or ' '\");\n \t}\n\n \tget handleClick() {\n \t\tthrow new Error(\": Props cannot be read directly from the component instance unless compiling with 'accessors: true' or ' '\");\n \t}\n\n \tset handleClick(value) {\n \t\tthrow new Error(\": Props cannot be set directly on the component instance unless compiling with 'accessors: true' or ' '\");\n \t}\n\n \tget rowIndex() {\n \t\tthrow new Error(\": Props cannot be read directly from the component instance unless compiling with 'accessors: true' or ' '\");\n \t}\n\n \tset rowIndex(value) {\n \t\tthrow new Error(\": Props cannot be set directly on the component instance unless compiling with 'accessors: true' or ' '\");\n \t}\n\n \tget boxIndex() {\n \t\tthrow new Error(\": Props cannot be read directly from the component instance unless compiling with 'accessors: true' or ' '\");\n \t}\n\n \tset boxIndex(value) {\n \t\tthrow new Error(\": Props cannot be set directly on the component instance unless compiling with 'accessors: true' or ' '\");\n \t}\n }",
337 | name: 'Box',
338 | },
339 | },
340 | { key: 'stateeee', value: '' },
341 | ],
342 | },
343 | },
344 | ],
345 | detail: {
346 | attributes: [
347 | {
348 | key: 'handleClick',
349 | value: {
350 | __isFunction: true,
351 | source:
352 | "function handleClick(x, y) {\n \t\tif (state[x][y] !== '-') return;\n \t\tif (gameOver) return;\n \t\tturnCount++;\n\n \t\tif (turnCount === 9) {\n \t\t\t$$invalidate(2, gameOver = true);\n \t\t\tsetWinner('draw');\n \t\t}\n\n \t\t$$invalidate(1, state[x][y] = turn, state);\n \t\tconst test = [];\n\n \t\tfor (let x = 0; x < state.length; x++) {\n \t\t\tfor (let y = 0; y < state[0].length; y++) {\n \t\t\t\ttest.push(state[x][y]);\n \t\t\t}\n \t\t}\n\n \t\twinningConditions.forEach(c => {\n \t\t\tif (test[c[0]] === test[c[1]] && test[c[1]] === test[c[2]] && test[c[0]] === test[c[2]] && test[c[0]] !== '-') {\n \t\t\t\t$$invalidate(2, gameOver = true);\n \t\t\t\tsetWinner(turn);\n \t\t\t}\n \t\t});\n\n \t\tturn = turn === 'X' ? 'O' : 'X';\n \t}",
353 | name: 'handleClick',
354 | },
355 | isBound: false,
356 | },
357 | ],
358 | listeners: [],
359 | ctx: [
360 | {
361 | key: 'Row',
362 | value: {
363 | __isFunction: true,
364 | source:
365 | "class Row extends SvelteComponentDev {\n \tconstructor(options) {\n \t\tsuper(options);\n \t\tinit(this, options, instance$3, create_fragment$3, safe_not_equal, { rowState: 0, handleClick: 1, rowIndex: 2 });\n\n \t\tdispatch_dev(\"SvelteRegisterComponent\", {\n \t\t\tcomponent: this,\n \t\t\ttagName: \"Row\",\n \t\t\toptions,\n \t\t\tid: create_fragment$3.name\n \t\t});\n \t}\n\n \tget rowState() {\n \t\tthrow new Error(\": Props cannot be read directly from the component instance unless compiling with 'accessors: true' or ' '\");\n \t}\n\n \tset rowState(value) {\n \t\tthrow new Error(\": Props cannot be set directly on the component instance unless compiling with 'accessors: true' or ' '\");\n \t}\n\n \tget handleClick() {\n \t\tthrow new Error(\": Props cannot be read directly from the component instance unless compiling with 'accessors: true' or ' '\");\n \t}\n\n \tset handleClick(value) {\n \t\tthrow new Error(\": Props cannot be set directly on the component instance unless compiling with 'accessors: true' or ' '\");\n \t}\n\n \tget rowIndex() {\n \t\tthrow new Error(\": Props cannot be read directly from the component instance unless compiling with 'accessors: true' or ' '\");\n \t}\n\n \tset rowIndex(value) {\n \t\tthrow new Error(\": Props cannot be set directly on the component instance unless compiling with 'accessors: true' or ' '\");\n \t}\n }",
366 | name: 'Row',
367 | },
368 | },
369 | {
370 | key: 'WinnerMessage',
371 | value: {
372 | __isFunction: true,
373 | source:
374 | 'class WinnerMessage extends SvelteComponentDev {\n \tconstructor(options) {\n \t\tsuper(options);\n \t\tinit(this, options, instance$2, create_fragment$2, safe_not_equal, { winner: 0 });\n\n \t\tdispatch_dev("SvelteRegisterComponent", {\n \t\t\tcomponent: this,\n \t\t\ttagName: "WinnerMessage",\n \t\t\toptions,\n \t\t\tid: create_fragment$2.name\n \t\t});\n \t}\n\n \tget winner() {\n \t\tthrow new Error(": Props cannot be read directly from the component instance unless compiling with \'accessors: true\' or \' \'");\n \t}\n\n \tset winner(value) {\n \t\tthrow new Error(": Props cannot be set directly on the component instance unless compiling with \'accessors: true\' or \' \'");\n \t}\n }',
375 | name: 'WinnerMessage',
376 | },
377 | },
378 | {
379 | key: 'winningConditions',
380 | value: [
381 | [0, 1, 2],
382 | [3, 4, 5],
383 | [6, 7, 8],
384 | [0, 3, 6],
385 | [1, 4, 7],
386 | [2, 5, 8],
387 | [0, 4, 8],
388 | [2, 4, 6],
389 | ],
390 | },
391 | {
392 | key: 'state',
393 | value: [
394 | ['-', '-', '-'],
395 | ['-', '-', '-'],
396 | ['-', '-', '-'],
397 | ],
398 | },
399 | { key: 'turnCount', value: 0 },
400 | { key: 'turn', value: 'X' },
401 | { key: 'gameOver', value: false },
402 | { key: 'winner', value: null },
403 | {
404 | key: 'setWinner',
405 | value: {
406 | __isFunction: true,
407 | source:
408 | 'function setWinner(newWinner) {\n \t\t$$invalidate(3, winner = newWinner);\n \t}',
409 | name: 'setWinner',
410 | },
411 | },
412 | ],
413 | },
414 | },
415 | ],
416 | detail: {
417 | attributes: [{ key: 'name', value: 'Tic Tac Toe', isBound: false }],
418 | listeners: [],
419 | ctx: [
420 | {
421 | key: 'Board',
422 | value: {
423 | __isFunction: true,
424 | source:
425 | 'class Board extends SvelteComponentDev {\n \tconstructor(options) {\n \t\tsuper(options);\n \t\tinit(this, options, instance$1, create_fragment$1, safe_not_equal, { handleClick: 0 });\n\n \t\tdispatch_dev("SvelteRegisterComponent", {\n \t\t\tcomponent: this,\n \t\t\ttagName: "Board",\n \t\t\toptions,\n \t\t\tid: create_fragment$1.name\n \t\t});\n \t}\n\n \tget handleClick() {\n \t\treturn this.$$.ctx[0];\n \t}\n\n \tset handleClick(value) {\n \t\tthrow new Error(": Props cannot be set directly on the component instance unless compiling with \'accessors: true\' or \' \'");\n \t}\n }',
426 | name: 'Board',
427 | },
428 | },
429 | {
430 | key: 'WinnerMessage',
431 | value: {
432 | __isFunction: true,
433 | source:
434 | 'class WinnerMessage extends SvelteComponentDev {\n \tconstructor(options) {\n \t\tsuper(options);\n \t\tinit(this, options, instance$2, create_fragment$2, safe_not_equal, { winner: 0 });\n\n \t\tdispatch_dev("SvelteRegisterComponent", {\n \t\t\tcomponent: this,\n \t\t\ttagName: "WinnerMessage",\n \t\t\toptions,\n \t\t\tid: create_fragment$2.name\n \t\t});\n \t}\n\n \tget winner() {\n \t\tthrow new Error(": Props cannot be read directly from the component instance unless compiling with \'accessors: true\' or \' \'");\n \t}\n\n \tset winner(value) {\n \t\tthrow new Error(": Props cannot be set directly on the component instance unless compiling with \'accessors: true\' or \' \'");\n \t}\n }',
435 | name: 'WinnerMessage',
436 | },
437 | },
438 | ],
439 | },
440 | };
441 |
442 | export default mockData;
443 |
--------------------------------------------------------------------------------