├── .editorconfig ├── .gitattributes ├── .github └── workflows │ ├── playwright.yml │ ├── pr-check.yml │ └── release.yml ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .lintstagedrc.mjs ├── .nvmrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── app ├── (main) │ ├── header │ │ ├── header.test.tsx │ │ ├── header.tsx │ │ ├── themeToggle.test.tsx │ │ └── themeToggle.tsx │ ├── page.test.tsx │ ├── page.tsx │ └── sections │ │ ├── callstack.test.tsx │ │ ├── callstack.tsx │ │ ├── configurator │ │ ├── configurator.tsx │ │ ├── controls.data.tsx │ │ ├── controls.tsx │ │ ├── controls.ui.tsx │ │ └── editor.tsx │ │ ├── console.test.tsx │ │ ├── console.tsx │ │ ├── eventLoop │ │ ├── eventLoop.tsx │ │ └── wheel.tsx │ │ ├── microtasksQueue.test.tsx │ │ ├── microtasksQueue.tsx │ │ ├── requestAnimationFrame.test.tsx │ │ ├── requestAnimationFrame.tsx │ │ ├── tasksQueue.test.tsx │ │ ├── tasksQueue.tsx │ │ └── webApi.tsx ├── (styles) │ ├── ace-editor.css │ ├── globals.css │ ├── typography.css │ └── utilities.css ├── icon.png ├── icon.svg ├── layout.test.tsx └── layout.tsx ├── commitlint.config.js ├── components.json ├── components ├── card.tsx ├── chadcdn │ ├── button.tsx │ ├── dialog.tsx │ ├── select.tsx │ └── slider.tsx ├── infoContainer.tsx ├── list.tsx ├── modal.tsx ├── progressCard.test.tsx └── progressCard.tsx ├── e2e-tests └── explorer.spec.ts ├── eslint.config.mjs ├── jest.config.js ├── jest.setup.ts ├── next.config.ts ├── package-lock.json ├── package.json ├── playwright.config.ts ├── postcss.config.mjs ├── prettier.config.mjs ├── release.config.mjs ├── store ├── store.test.ts ├── store.ts ├── store.types.ts ├── store.utils.test.ts └── store.utils.ts ├── tsconfig.json └── utils ├── constants.ts ├── getSimulationSteps ├── calculator.traverse.ts ├── calculator.ts ├── getAstFromText.ts ├── getScopeFromAst.ts ├── getSerializedSteps.test.ts ├── getSerializedSteps.ts ├── getSimulationSteps.test.ts ├── getSimulationSteps.ts ├── guards.test.ts ├── guards.ts ├── utils.test.ts └── utils.ts ├── simulate └── simulate.ts ├── types.ts └── utils.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = tab 7 | indent_size = 2 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/workflows/playwright.yml: -------------------------------------------------------------------------------- 1 | name: Playwright Tests 2 | on: 3 | workflow_dispatch: 4 | jobs: 5 | test: 6 | timeout-minutes: 60 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: actions/setup-node@v4 11 | with: 12 | node-version: lts/* 13 | - name: Install dependencies 14 | run: npm ci 15 | - name: Install Playwright Browsers 16 | run: npx playwright install --with-deps 17 | - name: Run Playwright tests 18 | run: npx playwright test 19 | - uses: actions/upload-artifact@v4 20 | if: ${{ !cancelled() }} 21 | with: 22 | name: playwright-report 23 | path: playwright-report/ 24 | retention-days: 30 25 | -------------------------------------------------------------------------------- /.github/workflows/pr-check.yml: -------------------------------------------------------------------------------- 1 | name: PR check 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | check: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@v4 15 | 16 | - name: Enable Corepack 17 | run: corepack enable 18 | 19 | - name: Set up Node 20 | uses: actions/setup-node@v4 21 | with: 22 | node-version: 22.13.0 23 | 24 | - name: Install dependencies 25 | run: npm install 26 | 27 | - name: Run linter 28 | run: npm run lint 29 | 30 | - name: Run type check 31 | run: npm run typecheck 32 | 33 | - name: Run unit tests 34 | run: npm run test 35 | 36 | - name: Run build 37 | run: npm run build 38 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | branches: 5 | - master 6 | 7 | permissions: 8 | contents: write 9 | pull-requests: write 10 | 11 | jobs: 12 | release: 13 | name: Release 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v4 18 | - name: Semantic Release 19 | uses: cycjimmy/semantic-release-action@v4 20 | env: 21 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | 4 | # testing 5 | /coverage 6 | 7 | # next.js 8 | /.next/ 9 | /out/ 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | *.pem 17 | 18 | # debug 19 | npm-debug.log* 20 | 21 | # env files (can opt-in for committing if needed) 22 | .env* 23 | 24 | # vercel 25 | .vercel 26 | 27 | # typescript 28 | *.tsbuildinfo 29 | next-env.d.ts 30 | 31 | #IDEs 32 | .idea 33 | .vscode 34 | 35 | # Playwright 36 | /test-results/ 37 | /playwright-report/ 38 | /blob-report/ 39 | /playwright/.cache/ 40 | 41 | # Jest 42 | /coverage 43 | 44 | 45 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | commitlint --edit $1 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | lint-staged --allow-empty 2 | -------------------------------------------------------------------------------- /.lintstagedrc.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | '*.{js,jsx,ts,tsx}': [ 3 | 'prettier --write', 4 | 'eslint --fix', 5 | 'jest --passWithNoTests', 6 | () => 'npm run typecheck', 7 | ], 8 | '*.{json,css,scss,md}': ['prettier --write', 'eslint --fix'], 9 | }; 10 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 22.13.0 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # [1.16.0](https://github.com/vault-developer/event-loop-explorer/compare/v1.15.0...v1.16.0) (2025-04-21) 2 | 3 | 4 | ### Features 5 | 6 | * **core:** fix url link ([5b6aa3a](https://github.com/vault-developer/event-loop-explorer/commit/5b6aa3a954ab8e773668ef2a21a0c3c8916dcbfd)) 7 | 8 | # [1.15.0](https://github.com/vault-developer/event-loop-explorer/compare/v1.14.2...v1.15.0) (2025-04-21) 9 | 10 | 11 | ### Features 12 | 13 | * **core:** upgrade to next.js ([#42](https://github.com/vault-developer/event-loop-explorer/issues/42)) ([a00e026](https://github.com/vault-developer/event-loop-explorer/commit/a00e026765cbf66063570377642d37e4e30fe752)) 14 | 15 | ## [1.14.2](https://github.com/vault-developer/event-loop-explorer/compare/v1.14.1...v1.14.2) (2025-02-09) 16 | 17 | 18 | ### Bug Fixes 19 | 20 | * **editor:** fix border-radius ([42d9acd](https://github.com/vault-developer/event-loop-explorer/commit/42d9acd71311a94f87448a43751e05a096e34502)) 21 | 22 | ## [1.14.1](https://github.com/vault-developer/event-loop-explorer/compare/v1.14.0...v1.14.1) (2025-02-09) 23 | 24 | 25 | ### Bug Fixes 26 | 27 | * **icon:** fix icon position ([b43df1d](https://github.com/vault-developer/event-loop-explorer/commit/b43df1dd1f5e8813faf5b54c916adf9bf4aad209)) 28 | 29 | # [1.14.0](https://github.com/vault-developer/event-loop-explorer/compare/v1.13.1...v1.14.0) (2025-02-08) 30 | 31 | ### Features 32 | 33 | - update styles ([e485d9d](https://github.com/vault-developer/event-loop-explorer/commit/e485d9d5c03fd81a9b2655f6aa8b66017fd418a1)) 34 | 35 | ## [1.13.1](https://github.com/vault-developer/event-loop-explorer/compare/v1.13.0...v1.13.1) (2025-02-01) 36 | 37 | ### Bug Fixes 38 | 39 | - update font-weight ([f1db8e6](https://github.com/vault-developer/event-loop-explorer/commit/f1db8e6f903b83069da5e7f153f0eb02e60b7db4)) 40 | 41 | # [1.13.0](https://github.com/vault-developer/event-loop-explorer/compare/v1.12.3...v1.13.0) (2025-02-01) 42 | 43 | ### Features 44 | 45 | - update design system tokens ([#39](https://github.com/vault-developer/event-loop-explorer/issues/39)) ([13be30f](https://github.com/vault-developer/event-loop-explorer/commit/13be30f0dd361f081d46b0cbea0f0f264fb5c9d7)) 46 | 47 | ## [1.12.3](https://github.com/vault-developer/event-loop-explorer/compare/v1.12.2...v1.12.3) (2025-01-26) 48 | 49 | ### Bug Fixes 50 | 51 | - **css:** fix flex columns in mobile version ([afe0448](https://github.com/vault-developer/event-loop-explorer/commit/afe044835498cd85893fa18fc1bd03fba2189c59)) 52 | 53 | ## [1.12.2](https://github.com/vault-developer/event-loop-explorer/compare/v1.12.1...v1.12.2) (2025-01-26) 54 | 55 | ### Bug Fixes 56 | 57 | - **zoomin:** fix zoom in animation ([a00671c](https://github.com/vault-developer/event-loop-explorer/commit/a00671cdf69f2c7fa69a6c695210a5c6f2e89816)) 58 | 59 | ## [1.12.1](https://github.com/vault-developer/event-loop-explorer/compare/v1.12.0...v1.12.1) (2025-01-26) 60 | 61 | ### Bug Fixes 62 | 63 | - **colors:** fix transition on page load ([884f3ac](https://github.com/vault-developer/event-loop-explorer/commit/884f3ac2724e4cd22af284dc2a44169fca36c3b6)) 64 | - **transition:** fix wheel transitions ([cc22583](https://github.com/vault-developer/event-loop-explorer/commit/cc22583571a92305ddf7eeb895b352a2bffe2194)) 65 | 66 | # [1.12.0](https://github.com/vault-developer/event-loop-explorer/compare/v1.11.5...v1.12.0) (2025-01-25) 67 | 68 | ### Features 69 | 70 | - **transition:** add color transition ([c57add7](https://github.com/vault-developer/event-loop-explorer/commit/c57add774c09140a844c35ca38d699dfd838f2fe)) 71 | 72 | ## [1.11.5](https://github.com/vault-developer/event-loop-explorer/compare/v1.11.4...v1.11.5) (2025-01-25) 73 | 74 | ### Bug Fixes 75 | 76 | - **colors:** update wheel colors ([a6be71e](https://github.com/vault-developer/event-loop-explorer/commit/a6be71e6e512280842b5ad9683d1f1aaf6ee3d25)) 77 | 78 | ## [1.11.4](https://github.com/vault-developer/event-loop-explorer/compare/v1.11.3...v1.11.4) (2025-01-19) 79 | 80 | ### Bug Fixes 81 | 82 | - fix selected lines ([ed63143](https://github.com/vault-developer/event-loop-explorer/commit/ed63143be5a6f25670e2c04805f940cea9c78bbb)) 83 | 84 | ## [1.11.3](https://github.com/vault-developer/event-loop-explorer/compare/v1.11.2...v1.11.3) (2025-01-14) 85 | 86 | ### Bug Fixes 87 | 88 | - fix styles ([5536070](https://github.com/vault-developer/event-loop-explorer/commit/5536070139a946bbbbbc92bfabae66640a3b11c2)) 89 | 90 | ## [1.11.2](https://github.com/vault-developer/event-loop-explorer/compare/v1.11.1...v1.11.2) (2025-01-13) 91 | 92 | ### Bug Fixes 93 | 94 | - fix animation lags ([#35](https://github.com/vault-developer/event-loop-explorer/issues/35)) ([d51f767](https://github.com/vault-developer/event-loop-explorer/commit/d51f767735d362667026af31cf825415187dd077)) 95 | 96 | ## [1.11.1](https://github.com/vault-developer/event-loop-explorer/compare/v1.11.0...v1.11.1) (2025-01-13) 97 | 98 | ### Bug Fixes 99 | 100 | - fix animation lags ([#34](https://github.com/vault-developer/event-loop-explorer/issues/34)) ([436175c](https://github.com/vault-developer/event-loop-explorer/commit/436175cccfd7698c071c80c601f0bf4c0cdb78a7)) 101 | 102 | # [1.11.0](https://github.com/vault-developer/event-loop-explorer/compare/v1.10.2...v1.11.0) (2025-01-12) 103 | 104 | ### Features 105 | 106 | - **ds:** update design system ([#33](https://github.com/vault-developer/event-loop-explorer/issues/33)) ([e746d3d](https://github.com/vault-developer/event-loop-explorer/commit/e746d3d983db1bb5c292ecdff6229e830f9eb566)) 107 | 108 | ## [1.10.2](https://github.com/vault-developer/event-loop-explorer/compare/v1.10.1...v1.10.2) (2024-12-31) 109 | 110 | ### Bug Fixes 111 | 112 | - revert "build: update dependencies" ([8f034c2](https://github.com/vault-developer/event-loop-explorer/commit/8f034c20bf58b5576a24b778cf4f01b71f092514)) 113 | 114 | ## [1.10.1](https://github.com/vault-developer/event-loop-explorer/compare/v1.10.0...v1.10.1) (2024-12-31) 115 | 116 | ### Bug Fixes 117 | 118 | - fix light theme colors ([81caef4](https://github.com/vault-developer/event-loop-explorer/commit/81caef44013c013f8fc2f1e7766cf56d137cd5af)) 119 | 120 | # [1.10.0](https://github.com/vault-developer/event-loop-explorer/compare/v1.9.4...v1.10.0) (2024-12-31) 121 | 122 | ### Features 123 | 124 | - add light theme ([7a903f1](https://github.com/vault-developer/event-loop-explorer/commit/7a903f159939cacfcecd2fc848b30e16d31a0007)) 125 | 126 | ## [1.9.4](https://github.com/vault-developer/event-loop-explorer/compare/v1.9.3...v1.9.4) (2024-12-29) 127 | 128 | ### Bug Fixes 129 | 130 | - rAF callback and console ([#31](https://github.com/vault-developer/event-loop-explorer/issues/31)) ([88ac9e3](https://github.com/vault-developer/event-loop-explorer/commit/88ac9e39a331532c415735866faca65261dae658)) 131 | 132 | ## [1.9.3](https://github.com/vault-developer/event-loop-explorer/compare/v1.9.2...v1.9.3) (2024-11-22) 133 | 134 | ### Bug Fixes 135 | 136 | - update isMobile function ([ed15f5e](https://github.com/vault-developer/event-loop-explorer/commit/ed15f5e9a34b3c183317c20f1a0fbea9b159e3e8)) 137 | 138 | ## [1.9.2](https://github.com/vault-developer/event-loop-explorer/compare/v1.9.1...v1.9.2) (2024-10-30) 139 | 140 | ### Bug Fixes 141 | 142 | - update husky scripts ([#22](https://github.com/vault-developer/event-loop-explorer/issues/22)) ([2a8b320](https://github.com/vault-developer/event-loop-explorer/commit/2a8b3209719bb65d2f124f07e8ce43e4573b44a7)) 143 | 144 | ## [1.9.1](https://github.com/vault-developer/event-loop-explorer/compare/v1.9.0...v1.9.1) (2024-10-30) 145 | 146 | ### Bug Fixes 147 | 148 | - Add margin to modals ([#21](https://github.com/vault-developer/event-loop-explorer/issues/21)) ([8ed6a5c](https://github.com/vault-developer/event-loop-explorer/commit/8ed6a5ce4b47db15c3a34a1dc088b6f2e4ee17ef)) 149 | 150 | # [1.9.0](https://github.com/vault-developer/event-loop-explorer/compare/v1.8.0...v1.9.0) (2024-10-29) 151 | 152 | ### Features 153 | 154 | - update modal styles ([bd668c8](https://github.com/vault-developer/event-loop-explorer/commit/bd668c80fac3e0a20a64d44e673488facb8b1b6e)) 155 | 156 | # [1.8.0](https://github.com/vault-developer/event-loop-explorer/compare/v1.7.4...v1.8.0) (2024-10-12) 157 | 158 | ### Features 159 | 160 | - Add variable declaration node ([#16](https://github.com/vault-developer/event-loop-explorer/issues/16)) ([fd24cf4](https://github.com/vault-developer/event-loop-explorer/commit/fd24cf4421a55ee451b3ff91020bcb35cc7bf943)) 161 | 162 | ## [1.7.4](https://github.com/vault-developer/event-loop-explorer/compare/v1.7.3...v1.7.4) (2024-10-07) 163 | 164 | ### Bug Fixes 165 | 166 | - fix eslint ([7f1da9b](https://github.com/vault-developer/event-loop-explorer/commit/7f1da9bbfd92e08cfa45dc027536fec401ac2210)) 167 | 168 | ## [1.7.3](https://github.com/vault-developer/event-loop-explorer/compare/v1.7.2...v1.7.3) (2024-10-05) 169 | 170 | ### Bug Fixes 171 | 172 | - add alert for missed serialization ([d2635b9](https://github.com/vault-developer/event-loop-explorer/commit/d2635b98503ebd84b81b607272de0afb55e6cee4)) 173 | 174 | ## [1.7.2](https://github.com/vault-developer/event-loop-explorer/compare/v1.7.1...v1.7.2) (2024-10-04) 175 | 176 | ### Bug Fixes 177 | 178 | - fix broken global styles ([8f4530a](https://github.com/vault-developer/event-loop-explorer/commit/8f4530a7433ab2d312a08395150265eeeb1da484)) 179 | 180 | ## [1.7.1](https://github.com/vault-developer/event-loop-explorer/compare/v1.7.0...v1.7.1) (2024-10-04) 181 | 182 | ### Bug Fixes 183 | 184 | - fix build issue ([3846a8e](https://github.com/vault-developer/event-loop-explorer/commit/3846a8eeea6b231e493944df91b1cfb0df9e1f1e)) 185 | 186 | # [1.7.0](https://github.com/vault-developer/event-loop-explorer/compare/v1.6.0...v1.7.0) (2024-10-04) 187 | 188 | ### Features 189 | 190 | - UI made responsive for mobile screen ([#5](https://github.com/vault-developer/event-loop-explorer/issues/5)) ([0cf4626](https://github.com/vault-developer/event-loop-explorer/commit/0cf462655f3eb40e38e20054c9828687d72d9487)) 191 | 192 | # [1.6.0](https://github.com/vault-developer/event-loop-explorer/compare/v1.5.2...v1.6.0) (2024-10-04) 193 | 194 | ### Bug Fixes 195 | 196 | - add theme types ([f30c433](https://github.com/vault-developer/event-loop-explorer/commit/f30c433e1376cc611a35ecb0f9c03b92c5621bf8)) 197 | 198 | ### Features 199 | 200 | - switch to emotion ([f176794](https://github.com/vault-developer/event-loop-explorer/commit/f17679457dd1c9c273fdeb81fadd762bedb8487c)) 201 | 202 | ## [1.5.2](https://github.com/vault-developer/event-loop-explorer/compare/v1.5.1...v1.5.2) (2024-08-31) 203 | 204 | ### Bug Fixes 205 | 206 | - fix pause animation bug ([c7abf68](https://github.com/vault-developer/event-loop-explorer/commit/c7abf68dba7607a53413de13f2e51ef46e7bd07c)) 207 | 208 | ## [1.5.1](https://github.com/vault-developer/event-loop-explorer/compare/v1.5.0...v1.5.1) (2024-08-31) 209 | 210 | ### Bug Fixes 211 | 212 | - add alert for not implemented nodes ([ca86775](https://github.com/vault-developer/event-loop-explorer/commit/ca86775aa6ac918a583a7da2b05b6762b55b6ce7)) 213 | 214 | # [1.5.0](https://github.com/vault-developer/event-loop-explorer/compare/v1.4.1...v1.5.0) (2024-08-31) 215 | 216 | ### Features 217 | 218 | - add animation to lists and queues ([9a66f19](https://github.com/vault-developer/event-loop-explorer/commit/9a66f19471a3ef116ac78804e2e646d4060eaffb)) 219 | 220 | ## [1.4.1](https://github.com/vault-developer/event-loop-explorer/compare/v1.4.0...v1.4.1) (2024-08-31) 221 | 222 | ### Bug Fixes 223 | 224 | - fix key issue ([6a6c6fa](https://github.com/vault-developer/event-loop-explorer/commit/6a6c6fa44402e8f4ebb4412d285983e646b1c22b)) 225 | - remove redundant functionality ([2f91e48](https://github.com/vault-developer/event-loop-explorer/commit/2f91e4875028e28307eb462c4f1db2ac14e000cf)) 226 | 227 | # [1.4.0](https://github.com/vault-developer/event-loop-explorer/compare/v1.3.1...v1.4.0) (2024-08-31) 228 | 229 | ### Features 230 | 231 | - add icons to CTA buttons ([099aca3](https://github.com/vault-developer/event-loop-explorer/commit/099aca3a98170350adf385e636c29a1051b71d3f)) 232 | 233 | ## [1.3.1](https://github.com/vault-developer/event-loop-explorer/compare/v1.3.0...v1.3.1) (2024-08-30) 234 | 235 | ### Bug Fixes 236 | 237 | - fix marker bug ([6b9a8e1](https://github.com/vault-developer/event-loop-explorer/commit/6b9a8e134d341de85285b2620f53b091374087c9)) 238 | 239 | # [1.3.0](https://github.com/vault-developer/event-loop-explorer/compare/v1.2.0...v1.3.0) (2024-08-30) 240 | 241 | ### Features 242 | 243 | - add debug line functionality ([fa77356](https://github.com/vault-developer/event-loop-explorer/commit/fa77356f8984922eb062d7c9e2528d411092843c)) 244 | - add selected line ([edcb8ea](https://github.com/vault-developer/event-loop-explorer/commit/edcb8ea879676014e4c7b83ee897b164be6b0766)) 245 | 246 | # [1.2.0](https://github.com/vault-developer/event-loop-explorer/compare/v1.1.0...v1.2.0) (2024-08-29) 247 | 248 | ### Features 249 | 250 | - add pause feature ([97b3e47](https://github.com/vault-developer/event-loop-explorer/commit/97b3e470b221a0387f4e8070f06f549e15abe858)) 251 | 252 | # [1.1.0](https://github.com/vault-developer/event-loop-explorer/compare/v1.0.4...v1.1.0) (2024-08-29) 253 | 254 | ### Features 255 | 256 | - add speed feature ([2c44970](https://github.com/vault-developer/event-loop-explorer/commit/2c4497086c5a4f2971600d0c1364905fdf81d791)) 257 | 258 | ## [1.0.4](https://github.com/vault-developer/event-loop-explorer/compare/v1.0.3...v1.0.4) (2024-08-28) 259 | 260 | ### Bug Fixes 261 | 262 | - fix styles issue on mobile ([7a54d4b](https://github.com/vault-developer/event-loop-explorer/commit/7a54d4bcf6a7c0c51008b9286093d7ba4fb97096)) 263 | 264 | ## [1.0.3](https://github.com/vault-developer/event-loop-explorer/compare/v1.0.2...v1.0.3) (2024-08-27) 265 | 266 | ### Bug Fixes 267 | 268 | - disable editor when animation is running ([d9bf803](https://github.com/vault-developer/event-loop-explorer/commit/d9bf8033722e922e1a36fff40147daa63159323c)) 269 | 270 | ## [1.0.2](https://github.com/vault-developer/event-loop-explorer/compare/v1.0.1...v1.0.2) (2024-08-27) 271 | 272 | ### Bug Fixes 273 | 274 | - stop animation when new sample is selected ([8a18b0f](https://github.com/vault-developer/event-loop-explorer/commit/8a18b0f8d95497e5bd0250835749fef6eb7320b9)) 275 | 276 | ## [1.0.1](https://github.com/vault-developer/event-loop-explorer/compare/v1.0.0...v1.0.1) (2024-08-27) 277 | 278 | ### Bug Fixes 279 | 280 | - fix styles issue ([f6791b2](https://github.com/vault-developer/event-loop-explorer/commit/f6791b27ddc45137b325a4307b1397863e59d05d)) 281 | 282 | # 1.0.0 (2024-08-27) 283 | 284 | ### Features 285 | 286 | - add event loop functionality ([e6ab338](https://github.com/vault-developer/event-loop-explorer/commit/e6ab33876a977e145f1c4cf67d36fdfeb967a935)) 287 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Trott Albert 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 | # Event Loop Explorer 2 | 3 | ![license badge](https://img.shields.io/github/license/vault-developer/event-loop-explorer) 4 | ![issues badge](https://img.shields.io/github/issues/vault-developer/event-loop-explorer) 5 | ![prs badge](https://img.shields.io/github/issues-pr/vault-developer/event-loop-explorer) 6 | ![release badge](https://img.shields.io/github/v/release/vault-developer/event-loop-explorer) 7 | ![commit activity badge](https://img.shields.io/github/commit-activity/m/vault-developer/event-loop-explorer) 8 | 9 | ### About the project 10 | 11 | Event Loop Explorer is a tool that helps to understand how JavaScript code is executed in the browser. 12 | It visualizes the Call Stack, Web APIs, Tasks, Microtasks, and Render phase. 13 | 14 | ### Screenshots: 15 | 16 | ![event-loop-image-1](https://github.com/user-attachments/assets/72b9efe9-1480-49a0-88d5-5c31461a4276) 17 | ![event-loop-image-2](https://github.com/user-attachments/assets/b6dccbd4-58a0-44ce-a6be-73bbd549b84e) 18 | 19 | ### Demo: 20 | 21 | Feel free to try it here: https://event-loop-explorer.vercel.app/ 22 | 23 | ### Known limitations & simplifications: 24 | 25 | - Javascript code is parsed to AST using acorn parser, and then order of events are generated. 26 | All default examples are working as expected, you can try to modify the code and see how it is working. 27 | However, not all cases are covered. 28 | Async/await, complex Promises, SetInterval, assignment operators will not work as expected. 29 | - Render phase is usually triggered every 16.66ms (60fps), but in this project it is simplified to just every second Event Loop circle. 30 | We are counting every circle as 360ms for simplicity, so render phase is triggered every 720ms. 31 | - UI is not mobile-friendly, please use desktop devices only. 32 | 33 | ### Contribution: 34 | 35 | If you want to contribute, feel free to fork this repository and create a pull request. 36 | There are a lot of topics in the "Future Plans" section. 37 | Have a question or idea? 38 | Feel free to raise it in our [discussions session](https://github.com/vault-developer/event-loop-explorer/discussions) 👍 39 | 40 | ### Launch locally: 41 | 42 | ``` 43 | git clone git@github.com:vault-developer/event-loop-explorer.git 44 | 45 | cd event-loop-explorer 46 | 47 | npm install 48 | 49 | npm run dev 50 | ``` 51 | 52 | ### Future Plans: 53 | 54 | - [ ] handle todos in the codebase; 55 | - [ ] check js parsing edge cases, including `new Promise((res) => {res(console.log(4))})`; 56 | - [ ] replace window.confirm with error boundary 57 | - [ ] add progress percentage inside wheel 58 | - [ ] add node.js event loop 59 | 60 | ### Inspired by: 61 | 62 | - [JS visualiser by Andrew Dillon](https://www.jsv9000.app/) 63 | - ["In The Loop" presented by Jake Archibald at JSConf.Asia 2018](https://www.youtube.com/watch?v=cCOL7MC4Pl0) 64 | -------------------------------------------------------------------------------- /app/(main)/header/header.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render, screen } from '@testing-library/react'; 3 | import '@testing-library/jest-dom'; 4 | import { Header } from './header'; 5 | 6 | describe('Header Component', () => { 7 | beforeEach(() => render(
)); 8 | 9 | it('should render the header title', () => { 10 | const titleElement = screen.getByText(/Event loop explorer/i); 11 | expect(titleElement).toBeInTheDocument(); 12 | }); 13 | 14 | it('should render the GitHub link', () => { 15 | const githubLink = screen.getByRole('link'); 16 | expect(githubLink).toBeInTheDocument(); 17 | expect(githubLink).toHaveAttribute( 18 | 'href', 19 | 'https://github.com/vault-developer/event-loop-explorer' 20 | ); 21 | }); 22 | 23 | it('should render the ThemeToggle component', () => { 24 | const themeToggle = screen.getByTestId('theme-toggle'); 25 | expect(themeToggle).toBeInTheDocument(); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /app/(main)/header/header.tsx: -------------------------------------------------------------------------------- 1 | import { ThemeToggle } from './themeToggle'; 2 | import { Repeat, Github } from 'lucide-react'; 3 | import { Button } from '@/components/chadcdn/button'; 4 | import Link from 'next/link'; 5 | 6 | export function Header() { 7 | return ( 8 |
9 |
10 | 11 |

Event loop explorer

12 |
13 | 14 |
15 | 23 | 24 |
25 |
26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /app/(main)/header/themeToggle.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen, fireEvent, cleanup } from '@testing-library/react'; 2 | import { ThemeToggle } from './themeToggle'; 3 | import { useTheme } from 'next-themes'; 4 | 5 | jest.mock('next-themes', () => ({ 6 | useTheme: jest.fn(), 7 | })); 8 | 9 | afterEach(cleanup); 10 | 11 | describe('ThemeToggle', () => { 12 | it('should render Sun and Moon icons', () => { 13 | (useTheme as jest.Mock).mockReturnValue({ 14 | theme: 'dark', 15 | setTheme: jest.fn(), 16 | }); 17 | render(); 18 | 19 | const moonIcon = screen.getByTestId('moon-icon'); 20 | const sunIcon = screen.getByTestId('sun-icon'); 21 | expect(moonIcon).toBeInTheDocument(); 22 | expect(sunIcon).toBeInTheDocument(); 23 | }); 24 | 25 | it('should toggle light theme on button click', () => { 26 | const setThemeMock = jest.fn(); 27 | (useTheme as jest.Mock).mockReturnValue({ 28 | theme: 'light', 29 | setTheme: setThemeMock, 30 | }); 31 | render(); 32 | const toggleButton = screen.getByRole('button', { name: 'Toggle theme' }); 33 | fireEvent.click(toggleButton); 34 | expect(setThemeMock).toHaveBeenCalledWith('dark'); 35 | }); 36 | 37 | it('should toggle dark theme on button click', () => { 38 | const setThemeMock = jest.fn(); 39 | (useTheme as jest.Mock).mockReturnValue({ 40 | theme: 'dark', 41 | setTheme: setThemeMock, 42 | }); 43 | render(); 44 | const toggleButton = screen.getByRole('button', { name: 'Toggle theme' }); 45 | fireEvent.click(toggleButton); 46 | expect(setThemeMock).toHaveBeenCalledWith('light'); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /app/(main)/header/themeToggle.tsx: -------------------------------------------------------------------------------- 1 | import { useTheme } from 'next-themes'; 2 | import { Button } from '@/components/chadcdn/button'; 3 | import { Moon, Sun } from 'lucide-react'; 4 | 5 | export function ThemeToggle() { 6 | const { setTheme, theme } = useTheme(); 7 | const onToggle = () => setTheme(theme === 'light' ? 'dark' : 'light'); 8 | 9 | return ( 10 | 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /app/(main)/page.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render, screen } from '@testing-library/react'; 3 | import Page from './page'; 4 | 5 | describe('Page', () => { 6 | it('renders without crashing', () => { 7 | render(); 8 | expect(screen.getByText('Event loop explorer')).toBeInTheDocument(); 9 | }); 10 | 11 | it('renders all sections', () => { 12 | render(); 13 | expect(screen.getByText('Code Editor')).toBeInTheDocument(); 14 | expect(screen.getByText('Web API')).toBeInTheDocument(); 15 | expect( 16 | screen.getByText('RequestAnimationFrame callbacks') 17 | ).toBeInTheDocument(); 18 | expect(screen.getByText('Callstack')).toBeInTheDocument(); 19 | expect(screen.getByText('Console')).toBeInTheDocument(); 20 | expect(screen.getByText('Tasks Queue')).toBeInTheDocument(); 21 | expect(screen.getByText('Microtasks Queue')).toBeInTheDocument(); 22 | expect(screen.getByText('Event loop')).toBeInTheDocument(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /app/(main)/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { Header } from './header/header'; 4 | import { Configurator } from '@/app/(main)/sections/configurator/configurator'; 5 | import { WebApi } from '@/app/(main)/sections/webApi'; 6 | import { RequestAnimationFrame } from '@/app/(main)/sections/requestAnimationFrame'; 7 | import { Callstack } from '@/app/(main)/sections/callstack'; 8 | import { Console } from '@/app/(main)/sections/console'; 9 | import { TasksQueue } from '@/app/(main)/sections/tasksQueue'; 10 | import { MicrotasksQueue } from '@/app/(main)/sections/microtasksQueue'; 11 | import { EventLoop } from '@/app/(main)/sections/eventLoop/eventLoop'; 12 | 13 | export default function Home() { 14 | return ( 15 |
16 |
17 |
18 |
19 |
20 | 21 |
22 |
23 | 24 |
25 |
26 | 27 |
28 |
29 |
30 |
31 | 32 |
33 |
34 | 35 |
36 |
37 |
38 |
39 | 40 |
41 |
42 | 43 |
44 |
45 | 46 |
47 |
48 |
49 |
50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /app/(main)/sections/callstack.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render, screen } from '@testing-library/react'; 3 | import '@testing-library/jest-dom'; 4 | import { Callstack } from '@/app/(main)/sections/callstack'; 5 | 6 | jest.mock('@/store/store', () => ({ 7 | useQueueManagerStore: jest 8 | .fn() 9 | .mockImplementation(() => ['stack-1', 'stack-2']), 10 | })); 11 | 12 | describe('Callstack Component', () => { 13 | beforeEach(() => render()); 14 | 15 | it('should render the callstack title', () => { 16 | const titleElement = screen.getByText(/Callstack/i); 17 | expect(titleElement).toBeInTheDocument(); 18 | }); 19 | 20 | it('should render callstacks', () => { 21 | const callstack1 = screen.getByText(/stack-1/i); 22 | const callstack2 = screen.getByText(/stack-2/i); 23 | expect(callstack1).toBeInTheDocument(); 24 | expect(callstack2).toBeInTheDocument(); 25 | }); 26 | 27 | it('should not render the modal by default', async () => { 28 | const descriptionElements = screen.queryAllByText( 29 | /A call stack is a mechanism for an interpreter to keep track of its place/i 30 | ); 31 | expect(descriptionElements.length).toBe(0); 32 | }); 33 | 34 | it('should render the modal after icon click', async () => { 35 | const modalButton = screen.getByRole('button'); 36 | await modalButton.click(); 37 | const descriptionElement = screen.getByText( 38 | /A call stack is a mechanism for an interpreter to keep track of its place/i 39 | ); 40 | expect(descriptionElement).toBeInTheDocument(); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /app/(main)/sections/callstack.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | import { InfoContainer } from '@/components/infoContainer'; 3 | import { List } from '@/components/list'; 4 | import { Card } from '@/components/card'; 5 | import { useQueueManagerStore } from '@/store/store'; 6 | 7 | const description = ( 8 | <> 9 |

10 | A call stack is a mechanism for an interpreter to keep track of its place 11 | in a script that calls multiple functions — what function is currently 12 | being run and what functions are called from within that function, etc. 13 |

14 |

15 | When a script calls a function, the interpreter adds it to the call stack 16 | and then starts carrying out the function. 17 |

18 |

19 | When the current function is finished, the interpreter takes it off the 20 | stack and resumes execution where it left off in the last code listing. 21 |

22 |

23 | If the stack takes up more space than it was assigned, a "stack 24 | overflow" error is thrown. 25 |

26 | 27 | ); 28 | 29 | export const Callstack: FC = () => { 30 | const stack = useQueueManagerStore((state) => state.callstack); 31 | 32 | return ( 33 | 34 | 35 | {(el) => } 36 | 37 | 38 | ); 39 | }; 40 | -------------------------------------------------------------------------------- /app/(main)/sections/configurator/configurator.tsx: -------------------------------------------------------------------------------- 1 | import { FC, useState } from 'react'; 2 | import { InfoContainer } from '@/components/infoContainer'; 3 | import { Controls } from './controls'; 4 | import { Editor } from './editor'; 5 | import { 6 | DEFAULT_EXAMPLE_KEY, 7 | EXAMPLES, 8 | } from '@/app/(main)/sections/configurator/controls.data'; 9 | 10 | const description = ( 11 | <> 12 |

13 | This code editor allows you to write and visualize JavaScript code 14 | execution within the event loop. 15 |

16 |
    17 |
  • 18 | - select a pre-built example from the dropdown menu or write your own 19 | code from scratch. 20 |
  • 21 |
  • 22 | - use the speed scrollbar to control the execution speed and observe how 23 | the event loop processes your code. 24 |
  • 25 |
  • 26 | - pause the execution when needed to examine the state of the event loop 27 | at any given point. 28 |
  • 29 |
30 | 31 | ); 32 | 33 | export const Configurator: FC = () => { 34 | const [code, setCode] = useState(() => EXAMPLES[DEFAULT_EXAMPLE_KEY].code); 35 | 36 | return ( 37 | 38 | 39 | 40 | 41 | ); 42 | }; 43 | -------------------------------------------------------------------------------- /app/(main)/sections/configurator/controls.data.tsx: -------------------------------------------------------------------------------- 1 | interface Example { 2 | title: string; 3 | code: string; 4 | } 5 | 6 | export const EXAMPLES: Record = { 7 | synchronous: { 8 | title: 'synchronous', 9 | code: `console.log(1); 10 | console.log(2); 11 | console.log(3);`, 12 | }, 13 | tasksQueue: { 14 | title: 'tasks queue', 15 | code: `console.log(1); 16 | setTimeout(()=>console.log(2), 0); 17 | console.log(3);`, 18 | }, 19 | callstack: { 20 | title: 'callstack', 21 | code: `function foo1() { 22 | console.log(1); 23 | foo2(); 24 | } 25 | function foo2() { 26 | console.log(2); 27 | foo3(); 28 | } 29 | function foo3() { 30 | setTimeout(()=>console.log(3), 800); 31 | console.log(4); 32 | } 33 | foo1();`, 34 | }, 35 | microtasks: { 36 | title: 'microtasks', 37 | code: `console.log(1); 38 | setTimeout(() => console.log(2), 0); 39 | queueMicrotask(() => console.log(3)); 40 | Promise.resolve().then(() => { 41 | console.log(4); 42 | }); 43 | setTimeout(() => console.log(5), 500); 44 | console.log(6);`, 45 | }, 46 | requestAnimationFrame: { 47 | title: 'requestAnimationFrame', 48 | code: `console.log(1); 49 | queueMicrotask(() => console.log(2)); 50 | requestAnimationFrame(() => console.log(3)); 51 | requestAnimationFrame(() => console.log(4)); 52 | console.log(5);`, 53 | }, 54 | everything: { 55 | title: 'everything', 56 | code: `function foo1() { 57 | console.log('foo1'); 58 | foo2(); 59 | } 60 | function foo2() { 61 | console.log('foo2'); 62 | foo3(); 63 | } 64 | function foo3() { 65 | setTimeout(() => { 66 | foo4(); 67 | console.log('foo3:1'); 68 | }, 0); 69 | queueMicrotask(() => console.log('foo3:2')); 70 | Promise.resolve().then(() => { 71 | console.log('foo3:3') 72 | }); 73 | setTimeout(() => console.log('foo3:4'), 500); 74 | console.log('foo3:5'); 75 | } 76 | function foo4() { 77 | console.log('foo4'); 78 | } 79 | 80 | console.log('global'); 81 | setTimeout(() => console.log('global:1'), 500); 82 | foo1();`, 83 | }, 84 | }; 85 | 86 | export const EXAMPLES_KEYS = Object.keys(EXAMPLES); 87 | export const DEFAULT_EXAMPLE_KEY = EXAMPLES_KEYS[0]; 88 | -------------------------------------------------------------------------------- /app/(main)/sections/configurator/controls.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | import { ControlsUi } from './controls.ui'; 3 | import { 4 | useControlsStore, 5 | useEditorStore, 6 | useQueueManagerStore, 7 | useWheelStore, 8 | } from '@/store/store'; 9 | import { EXAMPLES } from '@/app/(main)/sections/configurator/controls.data'; 10 | import { getSimulationSteps } from '@/utils/getSimulationSteps/getSimulationSteps'; 11 | import { simulate } from '@/utils/simulate/simulate'; 12 | 13 | interface ControlsProps { 14 | code: string; 15 | setCode: (key: string) => void; 16 | } 17 | 18 | export const Controls: FC = ({ setCode, code }) => { 19 | const { 20 | status, 21 | setStatus, 22 | speed, 23 | setSpeed, 24 | clear: clearControls, 25 | } = useControlsStore(); 26 | const clearWheel = useWheelStore((state) => state.clear); 27 | const clearQueueManager = useQueueManagerStore((state) => state.clear); 28 | const setEditorSource = useEditorStore((state) => state.setSource); 29 | 30 | const serialisedSpeed = Math.log2(speed); 31 | 32 | const onClear = () => { 33 | clearQueueManager(); 34 | clearControls(); 35 | clearWheel(); 36 | }; 37 | const onSerialisedSpeedChange = (value: number[]) => 38 | setSpeed(Math.pow(2, value[0])); 39 | const onPlay = () => { 40 | onClear(); 41 | const steps = getSimulationSteps(code); 42 | setEditorSource(code); 43 | setStatus('running'); 44 | simulate(steps, onFinish); 45 | window.scrollTo({ 46 | top: document.body.scrollHeight, 47 | behavior: 'smooth', 48 | }); 49 | }; 50 | const onResume = () => setStatus('running'); 51 | const onPause = () => setStatus('paused'); 52 | const onFinish = () => setStatus('idle'); 53 | const onStop = () => { 54 | onFinish(); 55 | onClear(); 56 | }; 57 | const onExampleSelect = (key: string) => setCode(EXAMPLES[key].code); 58 | 59 | return ( 60 | 71 | ); 72 | }; 73 | -------------------------------------------------------------------------------- /app/(main)/sections/configurator/controls.ui.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | import { 3 | Select, 4 | SelectContent, 5 | SelectItem, 6 | SelectTrigger, 7 | SelectValue, 8 | } from '@/components/chadcdn/select'; 9 | import { Button } from '@/components/chadcdn/button'; 10 | import { Play, Pause, Square } from 'lucide-react'; 11 | import { Slider } from '@/components/chadcdn/slider'; 12 | import { Controls } from '@/store/store.types'; 13 | import { 14 | DEFAULT_EXAMPLE_KEY, 15 | EXAMPLES, 16 | EXAMPLES_KEYS, 17 | } from '@/app/(main)/sections/configurator/controls.data'; 18 | 19 | interface ControlsUiProps { 20 | status: Controls['status']; 21 | serialisedSpeed: number; 22 | speed: number; 23 | onPlay: () => void; 24 | onResume: () => void; 25 | onPause: () => void; 26 | onStop: () => void; 27 | onSerialisedSpeedChange: (value: number[]) => void; 28 | onExampleSelect: (value: string) => void; 29 | } 30 | 31 | export const ControlsUi: FC = ({ 32 | status, 33 | speed, 34 | serialisedSpeed, 35 | onSerialisedSpeedChange, 36 | onExampleSelect, 37 | onResume, 38 | onPlay, 39 | onPause, 40 | onStop, 41 | }) => { 42 | const isIdle = status === 'idle'; 43 | const isPaused = status === 'paused'; 44 | const isRunning = status === 'running'; 45 | 46 | return ( 47 |
48 | {isIdle && ( 49 |
50 | 65 | 73 |
74 | )} 75 | {isRunning && ( 76 |
77 |
78 |
speed: x{speed}
79 | 86 |
87 |
88 | 96 | 104 |
105 |
106 | )} 107 | {isPaused && ( 108 |
109 |
110 |
speed: x{speed}
111 | 118 |
119 |
120 | 128 | 136 |
137 |
138 | )} 139 |
140 | ); 141 | }; 142 | -------------------------------------------------------------------------------- /app/(main)/sections/configurator/editor.tsx: -------------------------------------------------------------------------------- 1 | import { FC, RefObject, useEffect, useRef } from 'react'; 2 | import AceEditor from 'react-ace'; 3 | import 'ace-builds/src-noconflict/mode-javascript'; 4 | import 'ace-builds/src-noconflict/theme-solarized_dark'; 5 | import 'ace-builds/src-noconflict/theme-textmate'; 6 | import { useControlsStore, useEditorStore } from '@/store/store'; 7 | import { useTheme } from 'next-themes'; 8 | 9 | interface EditorProps { 10 | code: string; 11 | setCode: (key: string) => void; 12 | } 13 | 14 | export const Editor: FC = ({ code, setCode }) => { 15 | const { theme } = useTheme(); 16 | const editorRef = useRef(null); 17 | const setEditorRef = useEditorStore((state) => state.setRef); 18 | const status = useControlsStore((state) => state.status); 19 | const textTheme = theme === 'light' ? 'textmate' : 'solarized_dark'; 20 | const readOnly = status !== 'idle'; 21 | 22 | useEffect(() => { 23 | if (editorRef.current) { 24 | setEditorRef(editorRef as RefObject); 25 | } 26 | }, [editorRef, setEditorRef]); 27 | 28 | return ( 29 | 46 | ); 47 | }; 48 | -------------------------------------------------------------------------------- /app/(main)/sections/console.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render, screen } from '@testing-library/react'; 3 | import '@testing-library/jest-dom'; 4 | import { Console } from './console'; 5 | 6 | jest.mock('@/store/store', () => ({ 7 | useQueueManagerStore: jest.fn().mockImplementation(() => ['log1', 'log2']), 8 | })); 9 | 10 | describe('Console Component', () => { 11 | beforeEach(() => render()); 12 | 13 | it('should render the console title', () => { 14 | const titleElement = screen.getByText(/Console/i); 15 | expect(titleElement).toBeInTheDocument(); 16 | }); 17 | 18 | it('should render logs', () => { 19 | const log1 = screen.getByText(/log1/i); 20 | const log2 = screen.getByText(/log2/i); 21 | expect(log1).toBeInTheDocument(); 22 | expect(log2).toBeInTheDocument(); 23 | }); 24 | 25 | it('should not render the modal by default', async () => { 26 | const descriptionElements = screen.queryAllByText( 27 | /The browser console is a built-in tool/i 28 | ); 29 | expect(descriptionElements.length).toBe(0); 30 | }); 31 | 32 | it('should render the modal after icon click', async () => { 33 | const modalButton = screen.getByRole('button'); 34 | await modalButton.click(); 35 | const descriptionElement = screen.getByText( 36 | /The browser console is a built-in tool/i 37 | ); 38 | expect(descriptionElement).toBeInTheDocument(); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /app/(main)/sections/console.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | import { InfoContainer } from '@/components/infoContainer'; 3 | import { Card } from '@/components/card'; 4 | import { List } from '@/components/list'; 5 | import { useQueueManagerStore } from '@/store/store'; 6 | 7 | const description = ( 8 | <> 9 |

10 | The browser console is a built-in tool within web browsers that allows 11 | developers and users to view and interact with a web page's internal 12 | state. 13 |

14 |

15 | It provides a way to examine error messages, debug JavaScript code, and 16 | monitor network activity, ultimately helping with troubleshooting and 17 | development. 18 |

19 | 20 | ); 21 | 22 | export const Console: FC = () => { 23 | const logs = useQueueManagerStore((state) => state.console); 24 | 25 | return ( 26 | 27 | 28 | {(el) => } 29 | 30 | 31 | ); 32 | }; 33 | -------------------------------------------------------------------------------- /app/(main)/sections/eventLoop/eventLoop.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | import { InfoContainer } from '@/components/infoContainer'; 3 | import { Wheel } from '@/app/(main)/sections/eventLoop/wheel'; 4 | 5 | const description = ( 6 | <> 7 |

8 | The event loop is a fundamental concept in browser that manages the 9 | execution of code, handling of events, and updating of the user interface. 10 |

11 |

12 | The event loop continuously checks for and processes events and queued 13 | tasks in a specific order: 14 |

15 |
    16 |
  1. 1. Execute all synchronous code in the call stack
  2. 17 |
  3. 18 | 2. Check the microtask queue (e.g., Promise callbacks) and execute all 19 | tasks. 20 |
  4. 21 |
  5. 22 | 3. Check the macrotask queue (e.g., setTimeout, DOM events) and execute 23 | one task. 24 |
  6. 25 |
  7. 4. Update the rendering if necessary.
  8. 26 |
  9. 5. Repeat the process.
  10. 27 |
28 | 29 | ); 30 | 31 | export const EventLoop: FC = () => { 32 | return ( 33 | 34 |
35 | 36 |
37 |
38 | ); 39 | }; 40 | -------------------------------------------------------------------------------- /app/(main)/sections/eventLoop/wheel.tsx: -------------------------------------------------------------------------------- 1 | import { useTimeStore, useWheelStore } from '@/store/store'; 2 | import { useEffect } from 'react'; 3 | import { cn } from '@/utils/utils'; 4 | 5 | const POINTER_TOP_ID = 'wheel-pointer-top'; 6 | const POINTER_BOTTOM_ID = 'wheel-pointer-bottom'; 7 | const POINTER_LEFT_ID = 'wheel-pointer-left'; 8 | const POINTER_RIGHT_ID = 'wheel-pointer-right'; 9 | const WHEEL_ID = 'wheel'; 10 | 11 | const MICROTASK_CLASS = 'microtask fill-[var(--chart-1)]'; 12 | const MACROTASK_CLASS = 'macrotask fill-[var(--chart-2)]'; 13 | const RENDER_CLASS = 'render fill-[var(--chart-3)]'; 14 | 15 | const SEGMENT_OFFSET = -9; 16 | const POINTER_OFFSET = -99; 17 | const FONT_WEIGHT = 500; 18 | const FONT_SIZE = 11; 19 | 20 | export const Wheel = () => { 21 | const { render, macrotask, microtask } = useWheelStore(); 22 | 23 | const renderClass = render ? RENDER_CLASS : cn(RENDER_CLASS, 'opacity-25'); 24 | const microtaskClass = microtask 25 | ? MICROTASK_CLASS 26 | : cn(MICROTASK_CLASS, 'opacity-25'); 27 | const macrotaskClass = macrotask 28 | ? MACROTASK_CLASS 29 | : cn(MACROTASK_CLASS, 'opacity-25'); 30 | 31 | useEffect(() => { 32 | return useTimeStore.subscribe(({ grad }) => { 33 | const top = document.getElementById(POINTER_TOP_ID); 34 | const right = document.getElementById(POINTER_RIGHT_ID); 35 | const left = document.getElementById(POINTER_LEFT_ID); 36 | const bottom = document.getElementById(POINTER_BOTTOM_ID); 37 | 38 | const corrected = grad + POINTER_OFFSET; 39 | 40 | if (top && right && left && bottom) { 41 | top.style.transform = `rotate(${corrected}deg)`; 42 | right.style.transform = `rotate(${corrected + 18}deg)`; 43 | left.style.transform = `rotate(${corrected}deg)`; 44 | bottom.style.transform = `rotate(${corrected}deg)`; 45 | } 46 | }); 47 | }, []); 48 | 49 | return ( 50 |
51 | 56 | 57 | 61 | 62 | 68 | 75 | 80 | Microtask 81 | 82 | 83 | Render 84 | 85 | 90 | Microtask 91 | 92 | 97 | Microtask 98 | 99 | 104 | Task 105 | 106 | 111 | Microtask 112 | 113 | 120 | 126 | 133 | 140 | 147 | 154 | mTMicrotask 155 | 156 | 163 | mTMicrotask 164 | 165 | 172 | mTMicrotask 173 | 174 | 181 | mTMicrotask 182 | 183 | 190 | RRender 191 | 192 | 199 | TTask 200 | 201 | 202 |
203 | ); 204 | }; 205 | -------------------------------------------------------------------------------- /app/(main)/sections/microtasksQueue.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render, screen } from '@testing-library/react'; 3 | import '@testing-library/jest-dom'; 4 | import { MicrotasksQueue } from '@/app/(main)/sections/microtasksQueue'; 5 | 6 | jest.mock('@/store/store', () => ({ 7 | useQueueManagerStore: jest.fn().mockImplementation(() => ['ms1', 'ms2']), 8 | })); 9 | 10 | describe('Microtasks Queue Component', () => { 11 | beforeEach(() => render()); 12 | 13 | it('should render the microtask title', () => { 14 | const titleElement = screen.getByText(/Microtasks Queue/i); 15 | expect(titleElement).toBeInTheDocument(); 16 | }); 17 | 18 | it('should render logs', () => { 19 | const ms1 = screen.getByText(/ms1/i); 20 | const ms2 = screen.getByText(/ms2/i); 21 | expect(ms1).toBeInTheDocument(); 22 | expect(ms2).toBeInTheDocument(); 23 | }); 24 | 25 | it('should not render the modal by default', async () => { 26 | const descriptionElements = screen.queryAllByText( 27 | /A microtask is a short function which is executed/i 28 | ); 29 | expect(descriptionElements.length).toBe(0); 30 | }); 31 | 32 | it('should render the modal after icon click', async () => { 33 | const modalButton = screen.getByRole('button'); 34 | await modalButton.click(); 35 | const descriptionElement = screen.getByText( 36 | /A microtask is a short function which is executed/i 37 | ); 38 | expect(descriptionElement).toBeInTheDocument(); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /app/(main)/sections/microtasksQueue.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | import { InfoContainer } from '@/components/infoContainer'; 3 | import { Card } from '@/components/card'; 4 | import { List } from '@/components/list'; 5 | import { useQueueManagerStore } from '@/store/store'; 6 | 7 | const description = ( 8 | <> 9 |

10 | A microtask is a short function which is executed after the function or 11 | program which created it exits and only if the JavaScript execution stack 12 | is empty, but before returning control to the event loop being used by the 13 | user agent to drive the script's execution environment. 14 |

15 |

Events that can trigger new microtasks:

16 |
    17 |
  • - Promise resolution (.then(), .catch(), .finally())
  • 18 |
  • - Occurrence of observed DOM changes
  • 19 |
  • - queueMicrotask() method
  • 20 |
21 | 22 | ); 23 | 24 | export const MicrotasksQueue: FC = () => { 25 | const microtasks = useQueueManagerStore((state) => state.microtask); 26 | 27 | return ( 28 | 29 | 30 | {(el) => } 31 | 32 | 33 | ); 34 | }; 35 | -------------------------------------------------------------------------------- /app/(main)/sections/requestAnimationFrame.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render, screen } from '@testing-library/react'; 3 | import '@testing-library/jest-dom'; 4 | import { RequestAnimationFrame } from '@/app/(main)/sections/requestAnimationFrame'; 5 | 6 | jest.mock('@/store/store', () => ({ 7 | useQueueManagerStore: jest.fn().mockImplementation(() => ['raf1', 'raf2']), 8 | })); 9 | 10 | describe('RequestAnimationFrame Callbacks Component', () => { 11 | beforeEach(() => render()); 12 | 13 | it('should render the requestAnimationFrame title', () => { 14 | const titleElement = screen.getByText(/RequestAnimationFrame callbacks/i); 15 | expect(titleElement).toBeInTheDocument(); 16 | }); 17 | 18 | it('should render elements', () => { 19 | const raf1 = screen.getByText(/raf1/i); 20 | const raf2 = screen.getByText(/raf2/i); 21 | expect(raf1).toBeInTheDocument(); 22 | expect(raf2).toBeInTheDocument(); 23 | }); 24 | 25 | it('should not render the modal by default', async () => { 26 | const descriptionElements = screen.queryAllByText( 27 | /The window.requestAnimationFrame\(\) method tells the browser/i 28 | ); 29 | expect(descriptionElements.length).toBe(0); 30 | }); 31 | 32 | it('should render the modal after icon click', async () => { 33 | const modalButton = screen.getByRole('button'); 34 | await modalButton.click(); 35 | const descriptionElement = screen.getByText( 36 | /The window.requestAnimationFrame\(\) method tells the browser/i 37 | ); 38 | expect(descriptionElement).toBeInTheDocument(); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /app/(main)/sections/requestAnimationFrame.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | import { InfoContainer } from '@/components/infoContainer'; 3 | import { List } from '@/components/list'; 4 | import { Card } from '@/components/card'; 5 | import { useQueueManagerStore } from '@/store/store'; 6 | 7 | const description = ( 8 | <> 9 |

10 | The window.requestAnimationFrame() method tells the browser you wish to 11 | perform an animation. It requests the browser to call a user-supplied 12 | callback function before the next repaint. 13 |

14 |

15 | The frequency of calls to the callback function will generally match the 16 | display refresh rate. The most common refresh rate is 60hz, (60 17 | cycles/frames per second), though 75hz, 120hz, and 144hz are also widely 18 | used. 19 |

20 |

21 | requestAnimationFrame() calls are paused in most browsers when running in 22 | background tabs or hidden iframes, in order to improve performance and 23 | battery life. 24 |

25 | 26 | ); 27 | 28 | export const RequestAnimationFrame: FC = () => { 29 | const rafCallback = useQueueManagerStore((state) => state.rafCallback); 30 | 31 | return ( 32 | 36 | 37 | {(el) => } 38 | 39 | 40 | ); 41 | }; 42 | -------------------------------------------------------------------------------- /app/(main)/sections/tasksQueue.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render, screen } from '@testing-library/react'; 3 | import '@testing-library/jest-dom'; 4 | import { TasksQueue } from '@/app/(main)/sections/tasksQueue'; 5 | 6 | jest.mock('@/store/store', () => ({ 7 | useQueueManagerStore: jest.fn().mockImplementation(() => ['task1', 'task2']), 8 | })); 9 | 10 | describe('Tasks Queue Component', () => { 11 | beforeEach(() => render()); 12 | 13 | it('should render the task title', () => { 14 | const titleElement = screen.getByText(/Tasks Queue/i); 15 | expect(titleElement).toBeInTheDocument(); 16 | }); 17 | 18 | it('should render logs', () => { 19 | const task1 = screen.getByText(/task1/i); 20 | const task2 = screen.getByText(/task2/i); 21 | expect(task1).toBeInTheDocument(); 22 | expect(task2).toBeInTheDocument(); 23 | }); 24 | 25 | it('should not render the modal by default', async () => { 26 | const descriptionElements = screen.queryAllByText( 27 | /A task is anything which is scheduled to be run/i 28 | ); 29 | expect(descriptionElements.length).toBe(0); 30 | }); 31 | 32 | it('should render the modal after icon click', async () => { 33 | const modalButton = screen.getByRole('button'); 34 | await modalButton.click(); 35 | const descriptionElement = screen.getByText( 36 | /A task is anything which is scheduled to be run/i 37 | ); 38 | expect(descriptionElement).toBeInTheDocument(); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /app/(main)/sections/tasksQueue.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react'; 2 | import { InfoContainer } from '@/components/infoContainer'; 3 | import { Card } from '@/components/card'; 4 | import { List } from '@/components/list'; 5 | import { useQueueManagerStore } from '@/store/store'; 6 | 7 | const description = ( 8 | <> 9 |

10 | A task is anything which is scheduled to be run by the standard mechanisms 11 | such as initially starting to run a program, an event being dispatched 12 | asynchronously, or an interval or timeout being fired. These all get 13 | scheduled on the task queue. 14 |

15 |

For example, tasks get added to the task queue when:

16 |
    17 |
  • 18 | - a new JavaScript program or subprogram is executed (such as from a 19 | console, or by running the code in a {'