loop-benchmarking

Controlled experiments across agentic coding configurations. Same task, one variable, what actually works.
git clone https://git.shiptheloop.com/loop-benchmarking.git
Log | Files | Refs | README

commit 15e8256f98d43925d7e98af800ae38d623c035ce
parent 54cab796860c1b93ed256fc088638c4c32b5496e
Author: Brian Graham <brian@buildingbetterteams.de>
Date:   Sat,  4 Apr 2026 23:19:15 +0200

Add 6 new runs (73 total)

Profile: main_effects
Completed: 6 | Skipped: 51 | Failed: 0

Diffstat:
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/index.html | 157+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/package-lock.json | 3415+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/package.json | 24++++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/playwright.config.ts | 14++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/report/jscpd-report.json | 169+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/src/tetris.ts | 707+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/test-results/.last-run.json | 5+++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/tests/tetris.spec.ts | 133+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/tsconfig.json | 12++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/index.html | 199+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/package-lock.json | 2583+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/package.json | 23+++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/report/jscpd-report.json | 145+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/src/game.ts | 763+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/tsconfig.json | 16++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/index.html | 118+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/package-lock.json | 2583+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/package.json | 23+++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/report/jscpd-report.json | 218+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/src/tetris.ts | 543+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/tsconfig.json | 12++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/index.html | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/package-lock.json | 2583+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/package.json | 22++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/report/jscpd-report.json | 173+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/style.css | 161+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/tetris.js | 612+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/tetris.js.map | 2++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/tetris.ts | 693+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/tsconfig.json | 14++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/index.html | 203+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/package-lock.json | 2583+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/package.json | 22++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/report/jscpd-report.json | 157+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/smoke-test.mjs | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/src/tetris.ts | 556+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/tsconfig.json | 11+++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/index.html | 198+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/package-lock.json | 2583+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/package.json | 24++++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/report/jscpd-report.json | 145+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/src/tetris.ts | 673+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/tsconfig.json | 13+++++++++++++
Mresults/analysis/main_effects_cost.json | 303++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mresults/analysis/main_effects_score.json | 279++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mresults/analysis/main_effects_turns.json | 177+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Mresults/analysis/main_effects_wall_time.json | 307++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mresults/index.jsonl | 6++++++
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/claude_output.json | 2++
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/claude_stderr.log | 0
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/eval_results.json | 263+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/gameplay-bot-report.json | 129+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/meta.json | 31+++++++++++++++++++++++++++++++
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/transcript.jsonl | 48++++++++++++++++++++++++++++++++++++++++++++++++
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/claude_output.json | 2++
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/claude_stderr.log | 0
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/eval_results.json | 264+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/gameplay-bot-report.json | 130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/meta.json | 31+++++++++++++++++++++++++++++++
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/transcript.jsonl | 42++++++++++++++++++++++++++++++++++++++++++
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/claude_output.json | 2++
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/claude_stderr.log | 0
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/eval_results.json | 262+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/gameplay-bot-report.json | 128+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/meta.json | 31+++++++++++++++++++++++++++++++
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/transcript.jsonl | 43+++++++++++++++++++++++++++++++++++++++++++
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/claude_output.json | 2++
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/claude_stderr.log | 0
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/eval_results.json | 262+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/gameplay-bot-report.json | 127+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/meta.json | 31+++++++++++++++++++++++++++++++
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/transcript.jsonl | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/claude_output.json | 2++
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/claude_stderr.log | 0
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/eval_results.json | 262+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/gameplay-bot-report.json | 128+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/meta.json | 31+++++++++++++++++++++++++++++++
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/transcript.jsonl | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/claude_output.json | 2++
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/claude_stderr.log | 0
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/eval_results.json | 264+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/gameplay-bot-report.json | 130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/meta.json | 31+++++++++++++++++++++++++++++++
Aresults/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/transcript.jsonl | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
84 files changed, 26852 insertions(+), 523 deletions(-)

diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/index.html b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/index.html @@ -0,0 +1,157 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <title>Tetris</title> + <style> + * { margin: 0; padding: 0; box-sizing: border-box; } + + body { + background: #0a0a1a; + color: #e0e0e0; + font-family: 'Segoe UI', system-ui, -apple-system, sans-serif; + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; + overflow: hidden; + } + + #game-wrapper { + display: flex; + gap: 24px; + align-items: flex-start; + } + + /* Side panels */ + .side-panel { + display: flex; + flex-direction: column; + gap: 16px; + min-width: 100px; + } + + .panel-box { + background: #1a1a2e; + border: 2px solid #2a2a4a; + border-radius: 8px; + padding: 12px; + text-align: center; + } + + .panel-box h3 { + font-size: 11px; + text-transform: uppercase; + letter-spacing: 2px; + color: #888; + margin-bottom: 8px; + } + + .panel-box .value { + font-size: 22px; + font-weight: 700; + color: #fff; + font-variant-numeric: tabular-nums; + } + + /* Canvases */ + #game-area { + position: relative; + } + + #game-canvas { + display: block; + border: 2px solid #2a2a4a; + border-radius: 4px; + } + + #message { + display: none; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: rgba(0, 0, 0, 0.85); + color: #fff; + padding: 16px 28px; + border-radius: 8px; + font-size: 18px; + font-weight: 600; + letter-spacing: 1px; + white-space: nowrap; + pointer-events: none; + z-index: 10; + } + + canvas.preview { + display: block; + border: 2px solid #2a2a4a; + border-radius: 4px; + } + + /* Controls help */ + .controls { + font-size: 11px; + color: #666; + line-height: 1.8; + } + .controls kbd { + background: #2a2a4a; + border-radius: 3px; + padding: 1px 5px; + font-family: inherit; + color: #aaa; + } + </style> +</head> +<body> + <div id="game-wrapper"> + <!-- Left panel --> + <div class="side-panel"> + <div class="panel-box"> + <h3>Hold</h3> + <canvas id="hold-canvas" class="preview"></canvas> + </div> + <div class="panel-box"> + <h3>Score</h3> + <div class="value" id="score">0</div> + </div> + <div class="panel-box"> + <h3>Level</h3> + <div class="value" id="level">1</div> + </div> + <div class="panel-box"> + <h3>Lines</h3> + <div class="value" id="lines">0</div> + </div> + </div> + + <!-- Board --> + <div id="game-area"> + <canvas id="game-canvas"></canvas> + <div id="message"></div> + </div> + + <!-- Right panel --> + <div class="side-panel"> + <div class="panel-box"> + <h3>Next</h3> + <canvas id="preview-canvas" class="preview"></canvas> + </div> + <div class="panel-box controls"> + <h3>Controls</h3> + <kbd>←</kbd> <kbd>→</kbd> Move<br/> + <kbd>↓</kbd> Soft drop<br/> + <kbd>↑</kbd> Rotate CW<br/> + <kbd>Z</kbd> Rotate CCW<br/> + <kbd>Space</kbd> Hard drop<br/> + <kbd>Shift</kbd> / <kbd>C</kbd> Hold<br/> + <kbd>P</kbd> Pause<br/> + </div> + </div> + </div> + + <script type="module" src="./dist/tetris.js"></script> +</body> +</html> diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/package-lock.json b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/package-lock.json @@ -0,0 +1,3415 @@ +{ + "name": "loop-bench-doqfpofi", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "loop-bench-doqfpofi", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "@eslint/js": "^10.0.1", + "@playwright/test": "^1.59.1", + "@types/node": "^25.5.2", + "eslint": "^10.2.0", + "html-validate": "^10.11.3", + "jscpd": "^4.0.8", + "serve": "^14.2.6", + "typescript": "^6.0.2" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.4", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.4.tgz", + "integrity": "sha512-lf19F24LSMfF8weXvW5QEtnLqW70u7kgit5e9PSx0MsHAFclGd1T9ynvWEMDT1w5J4Qt54tomGeAhdoAku1Xow==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.4", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.4.tgz", + "integrity": "sha512-jJhqiY3wPMlWWO3370M86CPJ7pt8GmEwSLglMfQhjXal07RCvhmU0as4IuUEW5SJeunfItiEetHmSxCCe9lDBg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.0.tgz", + "integrity": "sha512-8FTGbNzTvmSlc4cZBaShkC6YvFMG0riksYWRFKXztqVdXaQbcZLXlFbSpC05s70sGEsXAw0qwhx69JiW7hQS7A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.4.tgz", + "integrity": "sha512-55lO/7+Yp0ISKRP0PsPtNTeNGapXaO085aELZmWCVc5SH3jfrqpuU6YgOdIxMS99ZHkQN1cXKE+cdIqwww9ptw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.0.tgz", + "integrity": "sha512-ejvBr8MQCbVsWNZnCwDXjUKq40MDmHalq7cJ6e9s/qzTUFIIo/afzt1Vui9T97FM/V/pN4YsFVoed5NIa96RDg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@html-validate/stylish": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@html-validate/stylish/-/stylish-5.0.0.tgz", + "integrity": "sha512-xjhRV9k1mWfgsOcpYlwsjUOFy3w3EnCDrqUrEw+DWdvOStMK59ts2H7GLKWZtmLI5m6Npp3qMSNReafQy9K2sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^4.0.0" + }, + "engines": { + "node": "^20.11 || >= 22.16" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jscpd/badge-reporter": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@jscpd/badge-reporter/-/badge-reporter-4.0.4.tgz", + "integrity": "sha512-I9b4MmLXPM2vo0SxSUWnNGKcA4PjQlD3GzXvFK60z43cN/EIdLbOq3FVwCL+dg2obUqGXKIzAm7EsDFTg0D+mQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "badgen": "^3.2.3", + "colors": "^1.4.0", + "fs-extra": "^11.2.0" + } + }, + "node_modules/@jscpd/core": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@jscpd/core/-/core-4.0.4.tgz", + "integrity": "sha512-QGMT3iXEX1fI6lgjPH+x8eyJwhwr2KkpSF5uBpjC0Z5Xloj0yFTFLtwJT+RhxP/Ob4WYrtx2jvpKB269oIwgMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.1" + } + }, + "node_modules/@jscpd/finder": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@jscpd/finder/-/finder-4.0.4.tgz", + "integrity": "sha512-qVUWY7Nzuvfd5OIk+n7/5CM98LmFroLqblRXAI2gDABwZrc7qS+WH2SNr0qoUq0f4OqwM+piiwKvwL/VDNn/Cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jscpd/core": "4.0.4", + "@jscpd/tokenizer": "4.0.4", + "blamer": "^1.0.6", + "bytes": "^3.1.2", + "cli-table3": "^0.6.5", + "colors": "^1.4.0", + "fast-glob": "^3.3.2", + "fs-extra": "^11.2.0", + "markdown-table": "^2.0.0", + "pug": "^3.0.3" + } + }, + "node_modules/@jscpd/html-reporter": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@jscpd/html-reporter/-/html-reporter-4.0.4.tgz", + "integrity": "sha512-YiepyeYkeH74Kx59PJRdUdonznct0wHPFkf6FLQN+mCBoy6leAWCcOfHtcexnp+UsBFDlItG5nRdKrDSxSH+Kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "colors": "1.4.0", + "fs-extra": "^11.2.0", + "pug": "^3.0.3" + } + }, + "node_modules/@jscpd/tokenizer": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@jscpd/tokenizer/-/tokenizer-4.0.4.tgz", + "integrity": "sha512-xxYYY/qaLah/FlwogEbGIxx9CjDO+G9E6qawcy26WwrflzJb6wsnhjwdneN6Wb0RNCDsqvzY+bzG453jsin4UQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jscpd/core": "4.0.4", + "reprism": "^0.0.11", + "spark-md5": "^3.0.2" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@playwright/test": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz", + "integrity": "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.59.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.5.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.2.tgz", + "integrity": "sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/sarif": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@types/sarif/-/sarif-2.1.7.tgz", + "integrity": "sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@zeit/schemas": { + "version": "2.36.0", + "resolved": "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.36.0.tgz", + "integrity": "sha512-7kjMwcChYEzMKjeex9ZFXkt1AyNov9R5HZtjBKVsmVpw7pa7ZtlCGvCBC2vnnXctaYN+aRI61HjIqeetZW5ROg==", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-align/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/arch": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/assert-never": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.4.0.tgz", + "integrity": "sha512-5oJg84os6NMQNl27T9LnZkvvqzvAnHu03ShCnoj6bsJwS7L8AO4lf+C/XjK/nvzEqQB744moC6V128RucQd1jA==", + "dev": true, + "license": "MIT" + }, + "node_modules/babel-walk": { + "version": "3.0.0-canary-5", + "resolved": "https://registry.npmjs.org/babel-walk/-/babel-walk-3.0.0-canary-5.tgz", + "integrity": "sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.9.6" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/badgen": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/badgen/-/badgen-3.2.3.tgz", + "integrity": "sha512-svDuwkc63E/z0ky3drpUppB83s/nlgDciH9m+STwwQoWyq7yCgew1qEfJ+9axkKdNq7MskByptWUN9j1PGMwFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/blamer": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/blamer/-/blamer-1.0.7.tgz", + "integrity": "sha512-GbBStl/EVlSWkiJQBZps3H1iARBrC7vt++Jb/TTmCNu/jZ04VW7tSN1nScbFXBUy1AN+jzeL7Zep9sbQxLhXKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^4.0.0", + "which": "^2.0.2" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/blamer/node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/blamer/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/blamer/node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/boxen": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.0.0.tgz", + "integrity": "sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-align": "^3.0.1", + "camelcase": "^7.0.0", + "chalk": "^5.0.1", + "cli-boxes": "^3.0.0", + "string-width": "^5.1.2", + "type-fest": "^2.13.0", + "widest-line": "^4.0.1", + "wrap-ansi": "^8.0.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/camelcase": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", + "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chalk": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", + "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk-template": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz", + "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/chalk-template?sponsor=1" + } + }, + "node_modules/chalk-template/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/chalk-template/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/character-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", + "integrity": "sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-regex": "^1.0.3" + } + }, + "node_modules/cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/cli-table3/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-table3/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cli-table3/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-table3/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/clipboardy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-3.0.0.tgz", + "integrity": "sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg==", + "dev": true, + "license": "MIT", + "dependencies": { + "arch": "^2.2.0", + "execa": "^5.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "compressible": "~2.0.18", + "debug": "2.6.9", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/constantinople": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz", + "integrity": "sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.6.0", + "@babel/types": "^7.6.1" + } + }, + "node_modules/content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/doctypes": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", + "integrity": "sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.2.0.tgz", + "integrity": "sha512-+L0vBFYGIpSNIt/KWTpFonPrqYvgKw1eUI5Vn7mEogrQcWtWYtNQ7dNqC+px/J0idT3BAkiWrhfS7k+Tum8TUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.4", + "@eslint/config-helpers": "^0.5.4", + "@eslint/core": "^1.2.0", + "@eslint/plugin-kit": "^0.7.0", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/fs-extra": { + "version": "11.3.4", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", + "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gitignore-to-glob": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/gitignore-to-glob/-/gitignore-to-glob-0.3.0.tgz", + "integrity": "sha512-mk74BdnK7lIwDHnotHddx1wsjMOFIThpLY3cPNniJ/2fA/tlLzHnFxIdR+4sLOu5KGgQJdij4kjJ2RoUNnCNMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.4 <5 || >=6.9" + } + }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-validate": { + "version": "10.11.3", + "resolved": "https://registry.npmjs.org/html-validate/-/html-validate-10.11.3.tgz", + "integrity": "sha512-wKUq9iR6bukMgiHhs/ORThZzEbQoFiiPNN7aZfQ8dlmhttPb2sM2Ji2p+Fy5Xj1aH7QHJ1biT2SUDw7A01P2oA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/html-validate" + } + ], + "license": "MIT", + "dependencies": { + "@html-validate/stylish": "^5.0.0", + "@sidvind/better-ajv-errors": "4.0.1", + "ajv": "^8.0.0", + "glob": "^13.0.0", + "kleur": "^4.1.0", + "minimist": "^1.2.0", + "prompts": "^2.0.0", + "semver": "^7.0.0" + }, + "bin": { + "html-validate": "bin/html-validate.mjs" + }, + "engines": { + "node": "^20.19.0 || >= 22.16.0" + }, + "peerDependencies": { + "jest": "^28.1.3 || ^29.0.3 || ^30.0.0", + "jest-diff": "^28.1.3 || ^29.0.3 || ^30.0.0", + "jest-snapshot": "^28.1.3 || ^29.0.3 || ^30.0.0", + "vitest": "^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.1" + }, + "peerDependenciesMeta": { + "jest": { + "optional": true + }, + "jest-diff": { + "optional": true + }, + "jest-snapshot": { + "optional": true + }, + "vitest": { + "optional": true + } + } + }, + "node_modules/html-validate/node_modules/@sidvind/better-ajv-errors": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sidvind/better-ajv-errors/-/better-ajv-errors-4.0.1.tgz", + "integrity": "sha512-6arF1ssKxItxgitPYXafUoLmsVBA6K7m9+ZGj6hLDoBl7nWpJ33EInwQUdHTle2METeWGxgQiqSex20KZRykew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "kleur": "^4.1.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "ajv": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/html-validate/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/html-validate/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-expression": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", + "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^7.1.1", + "object-assign": "^4.1.1" + } + }, + "node_modules/is-expression/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-port-reachable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-port-reachable/-/is-port-reachable-4.0.0.tgz", + "integrity": "sha512-9UoipoxYmSk6Xy7QFgRv2HDyaysmgSG75TFQs6S+3pDM7ZhKTF/bskZV+0UlABHzKjNVhPjYCLfeZUEg1wXxig==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-stringify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", + "integrity": "sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==", + "dev": true, + "license": "MIT" + }, + "node_modules/jscpd": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/jscpd/-/jscpd-4.0.8.tgz", + "integrity": "sha512-d2VNT/2Hv4dxT2/59He8Lyda4DYOxPRyRG9zBaOpTZAqJCVf2xLrBlZkT8Va6Lo9u3X2qz8Bpq4HrDi4JsrQhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jscpd/badge-reporter": "4.0.4", + "@jscpd/core": "4.0.4", + "@jscpd/finder": "4.0.4", + "@jscpd/html-reporter": "4.0.4", + "@jscpd/tokenizer": "4.0.4", + "colors": "^1.4.0", + "commander": "^5.0.0", + "fs-extra": "^11.2.0", + "gitignore-to-glob": "^0.3.0", + "jscpd-sarif-reporter": "4.0.6" + }, + "bin": { + "jscpd": "bin/jscpd" + } + }, + "node_modules/jscpd-sarif-reporter": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/jscpd-sarif-reporter/-/jscpd-sarif-reporter-4.0.6.tgz", + "integrity": "sha512-b9Sm3IPZ3+m8Lwa4gZa+4/LhDhlc/ZLEsLXKSOy1DANQ6kx0ueqZT+fUHWEdQ6m0o3+RIVIa7DmvLSojQD05ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "colors": "^1.4.0", + "fs-extra": "^11.2.0", + "node-sarif-builder": "^3.4.0" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jstransformer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", + "integrity": "sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-promise": "^2.0.0", + "promise": "^7.0.1" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/markdown-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz", + "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "repeat-string": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "~1.33.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types/node_modules/mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-sarif-builder": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/node-sarif-builder/-/node-sarif-builder-3.4.0.tgz", + "integrity": "sha512-tGnJW6OKRii9u/b2WiUViTJS+h7Apxx17qsMUjsUeNDiMMX5ZFf8F8Fcz7PAQ6omvOxHZtvDTmOYKJQwmfpjeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/sarif": "^2.1.7", + "fs-extra": "^11.1.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", + "dev": true, + "license": "(WTFPL OR MIT)" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-to-regexp": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz", + "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/playwright": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz", + "integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.59.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz", + "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "asap": "~2.0.3" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prompts/node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pug": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.4.tgz", + "integrity": "sha512-kFfq5mMzrS7+wrl5pLJzZEzemx34OQ0w4SARfhy/3yxTlhbstsudDwJzhf1hP02yHzbjoVMSXUj/Sz6RNfMyXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pug-code-gen": "^3.0.4", + "pug-filters": "^4.0.0", + "pug-lexer": "^5.0.1", + "pug-linker": "^4.0.0", + "pug-load": "^3.0.0", + "pug-parser": "^6.0.0", + "pug-runtime": "^3.0.1", + "pug-strip-comments": "^2.0.0" + } + }, + "node_modules/pug-attrs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-3.0.0.tgz", + "integrity": "sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==", + "dev": true, + "license": "MIT", + "dependencies": { + "constantinople": "^4.0.1", + "js-stringify": "^1.0.2", + "pug-runtime": "^3.0.0" + } + }, + "node_modules/pug-code-gen": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-3.0.4.tgz", + "integrity": "sha512-6okWYIKdasTyXICyEtvobmTZAVX57JkzgzIi4iRJlin8kmhG+Xry2dsus+Mun/nGCn6F2U49haHI5mkELXB14g==", + "dev": true, + "license": "MIT", + "dependencies": { + "constantinople": "^4.0.1", + "doctypes": "^1.1.0", + "js-stringify": "^1.0.2", + "pug-attrs": "^3.0.0", + "pug-error": "^2.1.0", + "pug-runtime": "^3.0.1", + "void-elements": "^3.1.0", + "with": "^7.0.0" + } + }, + "node_modules/pug-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-2.1.0.tgz", + "integrity": "sha512-lv7sU9e5Jk8IeUheHata6/UThZ7RK2jnaaNztxfPYUY+VxZyk/ePVaNZ/vwmH8WqGvDz3LrNYt/+gA55NDg6Pg==", + "dev": true, + "license": "MIT" + }, + "node_modules/pug-filters": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-4.0.0.tgz", + "integrity": "sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "constantinople": "^4.0.1", + "jstransformer": "1.0.0", + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0", + "resolve": "^1.15.1" + } + }, + "node_modules/pug-lexer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-5.0.1.tgz", + "integrity": "sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-parser": "^2.2.0", + "is-expression": "^4.0.0", + "pug-error": "^2.0.0" + } + }, + "node_modules/pug-linker": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-4.0.0.tgz", + "integrity": "sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0" + } + }, + "node_modules/pug-load": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-3.0.0.tgz", + "integrity": "sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "object-assign": "^4.1.1", + "pug-walk": "^2.0.0" + } + }, + "node_modules/pug-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-6.0.0.tgz", + "integrity": "sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pug-error": "^2.0.0", + "token-stream": "1.0.0" + } + }, + "node_modules/pug-runtime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-3.0.1.tgz", + "integrity": "sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==", + "dev": true, + "license": "MIT" + }, + "node_modules/pug-strip-comments": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz", + "integrity": "sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "pug-error": "^2.0.0" + } + }, + "node_modules/pug-walk": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz", + "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/registry-auth-token": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", + "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/registry-url": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", + "integrity": "sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "rc": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/reprism": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/reprism/-/reprism-0.0.11.tgz", + "integrity": "sha512-VsxDR5QxZo08M/3nRypNlScw5r3rKeSOPdU/QhDmu3Ai3BJxHn/qgfXGWQp/tAxUtzwYNo9W6997JZR0tPLZsA==", + "dev": true, + "license": "MIT" + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serve": { + "version": "14.2.6", + "resolved": "https://registry.npmjs.org/serve/-/serve-14.2.6.tgz", + "integrity": "sha512-QEjUSA+sD4Rotm1znR8s50YqA3kYpRGPmtd5GlFxbaL9n/FdUNbqMhxClqdditSk0LlZyA/dhud6XNRTOC9x2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zeit/schemas": "2.36.0", + "ajv": "8.18.0", + "arg": "5.0.2", + "boxen": "7.0.0", + "chalk": "5.0.1", + "chalk-template": "0.4.0", + "clipboardy": "3.0.0", + "compression": "1.8.1", + "is-port-reachable": "4.0.0", + "serve-handler": "6.1.7", + "update-check": "1.5.4" + }, + "bin": { + "serve": "build/main.js" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/serve-handler": { + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.7.tgz", + "integrity": "sha512-CinAq1xWb0vR3twAv9evEU8cNWkXCb9kd5ePAHUKJBkOsUpR1wt/CvGdeca7vqumL1U5cSaeVQ6zZMxiJ3yWsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.0.0", + "content-disposition": "0.5.2", + "mime-types": "2.1.18", + "minimatch": "3.1.5", + "path-is-inside": "1.0.2", + "path-to-regexp": "3.3.0", + "range-parser": "1.2.0" + } + }, + "node_modules/serve-handler/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/serve-handler/node_modules/brace-expansion": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/serve-handler/node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serve-handler/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/serve/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/serve/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/spark-md5": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/spark-md5/-/spark-md5-3.0.2.tgz", + "integrity": "sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw==", + "dev": true, + "license": "(WTFPL OR MIT)" + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/token-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", + "integrity": "sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg==", + "dev": true, + "license": "MIT" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", + "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/update-check": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/update-check/-/update-check-1.5.4.tgz", + "integrity": "sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "registry-auth-token": "3.3.2", + "registry-url": "3.1.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/widest-line": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", + "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^5.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/with": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz", + "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.9.6", + "@babel/types": "^7.9.6", + "assert-never": "^1.2.1", + "babel-walk": "3.0.0-canary-5" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/package.json b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/package.json @@ -0,0 +1,24 @@ +{ + "name": "loop-bench-doqfpofi", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "build": "tsc", + "test": "npx playwright test" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "commonjs", + "devDependencies": { + "@eslint/js": "^10.0.1", + "@playwright/test": "^1.59.1", + "@types/node": "^25.5.2", + "eslint": "^10.2.0", + "html-validate": "^10.11.3", + "jscpd": "^4.0.8", + "serve": "^14.2.6", + "typescript": "^6.0.2" + } +} diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/playwright.config.ts b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/playwright.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from "@playwright/test"; + +export default defineConfig({ + testDir: "./tests", + timeout: 15000, + use: { + baseURL: "http://localhost:3999", + }, + webServer: { + command: "npx serve -l 3999 -s .", + port: 3999, + reuseExistingServer: false, + }, +}); diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/report/jscpd-report.json b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/report/jscpd-report.json @@ -0,0 +1,168 @@ +{ + "statistics": { + "detectionDate": "2026-04-04T21:00:59.619Z", + "formats": { + "typescript": { + "sources": { + "tests/tetris.spec.ts": { + "lines": 132, + "tokens": 1449, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + }, + "src/tetris.ts": { + "lines": 706, + "tokens": 8031, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + }, + "playwright.config.ts": { + "lines": 13, + "tokens": 86, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "total": { + "lines": 851, + "tokens": 9566, + "sources": 3, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "javascript": { + "sources": { + "dist/tetris.js": { + "lines": 612, + "tokens": 7836, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "total": { + "lines": 612, + "tokens": 7836, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "json": { + "sources": { + "tsconfig.json": { + "lines": 11, + "tokens": 76, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + }, + "package.json": { + "lines": 23, + "tokens": 146, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "total": { + "lines": 34, + "tokens": 222, + "sources": 2, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "markup": { + "sources": { + "index.html": { + "lines": 156, + "tokens": 1064, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "total": { + "lines": 156, + "tokens": 1064, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + } + }, + "total": { + "lines": 1653, + "tokens": 18688, + "sources": 7, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "duplicates": [] +} +\ No newline at end of file diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/src/tetris.ts b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/src/tetris.ts @@ -0,0 +1,707 @@ +// ── Types ────────────────────────────────────────────────────────────────── + +type Cell = string | null; +type Board = Cell[][]; +type Shape = number[][]; + +interface Piece { + shape: Shape; + color: string; + x: number; + y: number; +} + +interface Tetromino { + shapes: Shape[]; + color: string; +} + +// ── Constants ────────────────────────────────────────────────────────────── + +const COLS = 10; +const ROWS = 20; +const BLOCK_SIZE = 30; +const PREVIEW_BLOCK = 20; + +const TETROMINOES: Tetromino[] = [ + { // I + shapes: [ + [[0,0,0,0],[1,1,1,1],[0,0,0,0],[0,0,0,0]], + [[0,0,1,0],[0,0,1,0],[0,0,1,0],[0,0,1,0]], + [[0,0,0,0],[0,0,0,0],[1,1,1,1],[0,0,0,0]], + [[0,1,0,0],[0,1,0,0],[0,1,0,0],[0,1,0,0]], + ], + color: "#00f0f0", + }, + { // O + shapes: [ + [[1,1],[1,1]], + [[1,1],[1,1]], + [[1,1],[1,1]], + [[1,1],[1,1]], + ], + color: "#f0f000", + }, + { // T + shapes: [ + [[0,1,0],[1,1,1],[0,0,0]], + [[0,1,0],[0,1,1],[0,1,0]], + [[0,0,0],[1,1,1],[0,1,0]], + [[0,1,0],[1,1,0],[0,1,0]], + ], + color: "#a000f0", + }, + { // S + shapes: [ + [[0,1,1],[1,1,0],[0,0,0]], + [[0,1,0],[0,1,1],[0,0,1]], + [[0,0,0],[0,1,1],[1,1,0]], + [[1,0,0],[1,1,0],[0,1,0]], + ], + color: "#00f000", + }, + { // Z + shapes: [ + [[1,1,0],[0,1,1],[0,0,0]], + [[0,0,1],[0,1,1],[0,1,0]], + [[0,0,0],[1,1,0],[0,1,1]], + [[0,1,0],[1,1,0],[1,0,0]], + ], + color: "#f00000", + }, + { // J + shapes: [ + [[1,0,0],[1,1,1],[0,0,0]], + [[0,1,1],[0,1,0],[0,1,0]], + [[0,0,0],[1,1,1],[0,0,1]], + [[0,1,0],[0,1,0],[1,1,0]], + ], + color: "#0000f0", + }, + { // L + shapes: [ + [[0,0,1],[1,1,1],[0,0,0]], + [[0,1,0],[0,1,0],[0,1,1]], + [[0,0,0],[1,1,1],[1,0,0]], + [[1,1,0],[0,1,0],[0,1,0]], + ], + color: "#f0a000", + }, +]; + +const LINE_POINTS = [0, 100, 300, 500, 800]; +const SOFT_DROP_POINTS = 1; +const HARD_DROP_POINTS = 2; + +// ── Wall Kick Data (SRS) ────────────────────────────────────────────────── + +const WALL_KICKS: Record<string, [number, number][]> = { + "0>1": [[-1,0],[-1,1],[0,-2],[-1,-2]], + "1>0": [[1,0],[1,-1],[0,2],[1,2]], + "1>2": [[1,0],[1,-1],[0,2],[1,2]], + "2>1": [[-1,0],[-1,1],[0,-2],[-1,-2]], + "2>3": [[1,0],[1,1],[0,-2],[1,-2]], + "3>2": [[-1,0],[-1,-1],[0,2],[-1,2]], + "3>0": [[-1,0],[-1,-1],[0,2],[-1,2]], + "0>3": [[1,0],[1,1],[0,-2],[1,-2]], +}; + +const I_WALL_KICKS: Record<string, [number, number][]> = { + "0>1": [[-2,0],[1,0],[-2,-1],[1,2]], + "1>0": [[2,0],[-1,0],[2,1],[-1,-2]], + "1>2": [[-1,0],[2,0],[-1,2],[2,-1]], + "2>1": [[1,0],[-2,0],[1,-2],[-2,1]], + "2>3": [[2,0],[-1,0],[2,1],[-1,-2]], + "3>2": [[-2,0],[1,0],[-2,-1],[1,2]], + "3>0": [[1,0],[-2,0],[1,-2],[-2,1]], + "0>3": [[-1,0],[2,0],[-1,2],[2,-1]], +}; + +// ── Game Class ───────────────────────────────────────────────────────────── + +class TetrisGame { + private canvas: HTMLCanvasElement; + private ctx: CanvasRenderingContext2D; + private previewCanvas: HTMLCanvasElement; + private previewCtx: CanvasRenderingContext2D; + private holdCanvas: HTMLCanvasElement; + private holdCtx: CanvasRenderingContext2D; + + private board: Board; + private current: Piece | null = null; + private currentRotation = 0; + private currentTetrominoIndex = -1; + private nextTetrominoIndex = -1; + private heldTetrominoIndex = -1; + private canHold = true; + + private score = 0; + private level = 1; + private lines = 0; + private gameOver = false; + private paused = false; + private started = false; + + private dropInterval = 1000; + private lastDrop = 0; + private lockDelay = 500; + private lockTimer = 0; + private isLocking = false; + + private animatingRows: number[] = []; + private animationStart = 0; + private readonly ANIMATION_DURATION = 300; + + private bag: number[] = []; + + private scoreEl: HTMLElement; + private levelEl: HTMLElement; + private linesEl: HTMLElement; + private messageEl: HTMLElement; + + constructor() { + this.canvas = document.getElementById("game-canvas") as HTMLCanvasElement; + this.ctx = this.canvas.getContext("2d")!; + this.canvas.width = COLS * BLOCK_SIZE; + this.canvas.height = ROWS * BLOCK_SIZE; + + this.previewCanvas = document.getElementById("preview-canvas") as HTMLCanvasElement; + this.previewCtx = this.previewCanvas.getContext("2d")!; + this.previewCanvas.width = 4 * PREVIEW_BLOCK; + this.previewCanvas.height = 4 * PREVIEW_BLOCK; + + this.holdCanvas = document.getElementById("hold-canvas") as HTMLCanvasElement; + this.holdCtx = this.holdCanvas.getContext("2d")!; + this.holdCanvas.width = 4 * PREVIEW_BLOCK; + this.holdCanvas.height = 4 * PREVIEW_BLOCK; + + this.scoreEl = document.getElementById("score")!; + this.levelEl = document.getElementById("level")!; + this.linesEl = document.getElementById("lines")!; + this.messageEl = document.getElementById("message")!; + + this.board = this.createBoard(); + + this.bindKeys(); + this.showMessage("Press ENTER to start"); + this.drawBoard(); + } + + // ── Board helpers ────────────────────────────────────────────────────── + + private createBoard(): Board { + return Array.from({ length: ROWS }, () => Array(COLS).fill(null)); + } + + // ── 7-bag randomiser ────────────────────────────────────────────────── + + private refillBag(): void { + const indices = [0, 1, 2, 3, 4, 5, 6]; + for (let i = indices.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [indices[i], indices[j]] = [indices[j], indices[i]]; + } + this.bag.push(...indices); + } + + private nextFromBag(): number { + if (this.bag.length < 2) this.refillBag(); + return this.bag.shift()!; + } + + // ── Piece management ────────────────────────────────────────────────── + + private spawnPiece(tetrominoIndex?: number): void { + const idx = tetrominoIndex ?? this.nextTetrominoIndex; + this.nextTetrominoIndex = this.nextFromBag(); + + const t = TETROMINOES[idx]; + this.currentTetrominoIndex = idx; + this.currentRotation = 0; + this.current = { + shape: t.shapes[0], + color: t.color, + x: Math.floor((COLS - t.shapes[0][0].length) / 2), + y: 0, + }; + this.isLocking = false; + this.canHold = true; + + if (this.collides(this.current.shape, this.current.x, this.current.y)) { + this.gameOver = true; + this.showMessage("GAME OVER — press ENTER"); + } + } + + // ── Collision detection ─────────────────────────────────────────────── + + private collides(shape: Shape, px: number, py: number): boolean { + for (let r = 0; r < shape.length; r++) { + for (let c = 0; c < shape[r].length; c++) { + if (!shape[r][c]) continue; + const bx = px + c; + const by = py + r; + if (bx < 0 || bx >= COLS || by >= ROWS) return true; + if (by >= 0 && this.board[by][bx]) return true; + } + } + return false; + } + + // ── Movement ────────────────────────────────────────────────────────── + + private moveLeft(): void { + if (!this.current || this.gameOver || this.paused) return; + if (!this.collides(this.current.shape, this.current.x - 1, this.current.y)) { + this.current.x--; + if (this.isLocking) this.lockTimer = performance.now(); + } + } + + private moveRight(): void { + if (!this.current || this.gameOver || this.paused) return; + if (!this.collides(this.current.shape, this.current.x + 1, this.current.y)) { + this.current.x++; + if (this.isLocking) this.lockTimer = performance.now(); + } + } + + private moveDown(): boolean { + if (!this.current || this.gameOver || this.paused) return false; + if (!this.collides(this.current.shape, this.current.x, this.current.y + 1)) { + this.current.y++; + this.isLocking = false; + return true; + } + return false; + } + + private softDrop(): void { + if (this.moveDown()) { + this.score += SOFT_DROP_POINTS; + this.updateScore(); + } + } + + private hardDrop(): void { + if (!this.current || this.gameOver || this.paused) return; + let dropped = 0; + while (!this.collides(this.current.shape, this.current.x, this.current.y + 1)) { + this.current.y++; + dropped++; + } + this.score += dropped * HARD_DROP_POINTS; + this.updateScore(); + this.lockPiece(); + } + + private getGhostY(): number { + if (!this.current) return 0; + let gy = this.current.y; + while (!this.collides(this.current.shape, this.current.x, gy + 1)) { + gy++; + } + return gy; + } + + // ── Rotation with SRS wall kicks ────────────────────────────────────── + + private rotate(dir: 1 | -1): void { + if (!this.current || this.gameOver || this.paused) return; + + const t = TETROMINOES[this.currentTetrominoIndex]; + const from = this.currentRotation; + const to = (from + dir + 4) % 4; + const newShape = t.shapes[to]; + + // Basic rotation + if (!this.collides(newShape, this.current.x, this.current.y)) { + this.current.shape = newShape; + this.currentRotation = to; + if (this.isLocking) this.lockTimer = performance.now(); + return; + } + + // Wall kicks + const kickKey = `${from}>${to}`; + const kicks = this.currentTetrominoIndex === 0 ? I_WALL_KICKS[kickKey] : WALL_KICKS[kickKey]; + if (!kicks) return; + + for (const [dx, dy] of kicks) { + if (!this.collides(newShape, this.current.x + dx, this.current.y - dy)) { + this.current.x += dx; + this.current.y -= dy; + this.current.shape = newShape; + this.currentRotation = to; + if (this.isLocking) this.lockTimer = performance.now(); + return; + } + } + } + + // ── Hold ────────────────────────────────────────────────────────────── + + private holdPiece(): void { + if (!this.current || !this.canHold || this.gameOver || this.paused) return; + this.canHold = false; + const prev = this.heldTetrominoIndex; + this.heldTetrominoIndex = this.currentTetrominoIndex; + if (prev === -1) { + this.spawnPiece(); + } else { + this.spawnPiece(prev); + } + } + + // ── Lock & clear ───────────────────────────────────────────────────── + + private lockPiece(): void { + if (!this.current) return; + const { shape, color, x, y } = this.current; + for (let r = 0; r < shape.length; r++) { + for (let c = 0; c < shape[r].length; c++) { + if (!shape[r][c]) continue; + const by = y + r; + const bx = x + c; + if (by >= 0 && by < ROWS && bx >= 0 && bx < COLS) { + this.board[by][bx] = color; + } + } + } + this.current = null; + this.clearLines(); + } + + private clearLines(): void { + const fullRows: number[] = []; + for (let r = 0; r < ROWS; r++) { + if (this.board[r].every((c) => c !== null)) { + fullRows.push(r); + } + } + + if (fullRows.length === 0) { + this.spawnPiece(); + return; + } + + // Start line-clear animation + this.animatingRows = fullRows; + this.animationStart = performance.now(); + } + + private finishClear(): void { + const fullRows = this.animatingRows; + this.animatingRows = []; + + // Remove rows top-to-bottom so indices stay stable when shifting + for (const row of fullRows.sort((a, b) => a - b)) { + this.board.splice(row, 1); + this.board.unshift(Array(COLS).fill(null)); + } + + const cleared = fullRows.length; + this.lines += cleared; + this.score += LINE_POINTS[cleared] * this.level; + this.level = Math.floor(this.lines / 10) + 1; + this.dropInterval = Math.max(50, 1000 - (this.level - 1) * 80); + this.updateScore(); + this.spawnPiece(); + } + + // ── Rendering ───────────────────────────────────────────────────────── + + private drawBlock( + ctx: CanvasRenderingContext2D, + x: number, + y: number, + color: string, + size: number, + alpha = 1, + ): void { + ctx.globalAlpha = alpha; + ctx.fillStyle = color; + ctx.fillRect(x * size, y * size, size, size); + + // Highlight + ctx.fillStyle = "rgba(255,255,255,0.25)"; + ctx.fillRect(x * size, y * size, size, size * 0.15); + ctx.fillRect(x * size, y * size, size * 0.15, size); + + // Shadow + ctx.fillStyle = "rgba(0,0,0,0.25)"; + ctx.fillRect(x * size, y * size + size * 0.85, size, size * 0.15); + ctx.fillRect(x * size + size * 0.85, y * size, size * 0.15, size); + + // Border + ctx.strokeStyle = "rgba(0,0,0,0.4)"; + ctx.lineWidth = 1; + ctx.strokeRect(x * size + 0.5, y * size + 0.5, size - 1, size - 1); + + ctx.globalAlpha = 1; + } + + private drawBoard(): void { + const ctx = this.ctx; + // Background + ctx.fillStyle = "#111"; + ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); + + // Grid lines + ctx.strokeStyle = "rgba(255,255,255,0.04)"; + ctx.lineWidth = 1; + for (let r = 0; r <= ROWS; r++) { + ctx.beginPath(); + ctx.moveTo(0, r * BLOCK_SIZE); + ctx.lineTo(COLS * BLOCK_SIZE, r * BLOCK_SIZE); + ctx.stroke(); + } + for (let c = 0; c <= COLS; c++) { + ctx.beginPath(); + ctx.moveTo(c * BLOCK_SIZE, 0); + ctx.lineTo(c * BLOCK_SIZE, ROWS * BLOCK_SIZE); + ctx.stroke(); + } + + // Compute animation flash alpha + const now = performance.now(); + let flashAlpha = 1; + if (this.animatingRows.length > 0) { + const elapsed = now - this.animationStart; + flashAlpha = 0.3 + 0.7 * Math.abs(Math.sin((elapsed / this.ANIMATION_DURATION) * Math.PI * 3)); + } + + // Locked cells + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (this.board[r][c]) { + const alpha = this.animatingRows.includes(r) ? flashAlpha : 1; + this.drawBlock(ctx, c, r, this.board[r][c]!, BLOCK_SIZE, alpha); + } + } + } + + if (this.current && this.animatingRows.length === 0) { + // Ghost piece + const gy = this.getGhostY(); + for (let r = 0; r < this.current.shape.length; r++) { + for (let c = 0; c < this.current.shape[r].length; c++) { + if (!this.current.shape[r][c]) continue; + this.drawBlock(ctx, this.current.x + c, gy + r, this.current.color, BLOCK_SIZE, 0.2); + } + } + + // Active piece + for (let r = 0; r < this.current.shape.length; r++) { + for (let c = 0; c < this.current.shape[r].length; c++) { + if (!this.current.shape[r][c]) continue; + const py = this.current.y + r; + if (py >= 0) { + this.drawBlock(ctx, this.current.x + c, py, this.current.color, BLOCK_SIZE); + } + } + } + } + } + + private drawPreview(): void { + const ctx = this.previewCtx; + ctx.fillStyle = "#1a1a2e"; + ctx.fillRect(0, 0, this.previewCanvas.width, this.previewCanvas.height); + if (this.nextTetrominoIndex < 0) return; + const t = TETROMINOES[this.nextTetrominoIndex]; + const shape = t.shapes[0]; + const ox = (4 - shape[0].length) / 2; + const oy = (4 - shape.length) / 2; + for (let r = 0; r < shape.length; r++) { + for (let c = 0; c < shape[r].length; c++) { + if (shape[r][c]) { + this.drawBlock(ctx, ox + c, oy + r, t.color, PREVIEW_BLOCK); + } + } + } + } + + private drawHold(): void { + const ctx = this.holdCtx; + ctx.fillStyle = "#1a1a2e"; + ctx.fillRect(0, 0, this.holdCanvas.width, this.holdCanvas.height); + if (this.heldTetrominoIndex < 0) return; + const t = TETROMINOES[this.heldTetrominoIndex]; + const shape = t.shapes[0]; + const ox = (4 - shape[0].length) / 2; + const oy = (4 - shape.length) / 2; + const alpha = this.canHold ? 1 : 0.35; + for (let r = 0; r < shape.length; r++) { + for (let c = 0; c < shape[r].length; c++) { + if (shape[r][c]) { + this.drawBlock(ctx, ox + c, oy + r, t.color, PREVIEW_BLOCK, alpha); + } + } + } + } + + // ── HUD helpers ─────────────────────────────────────────────────────── + + private updateScore(): void { + this.scoreEl.textContent = this.score.toString(); + this.levelEl.textContent = this.level.toString(); + this.linesEl.textContent = this.lines.toString(); + } + + private showMessage(msg: string): void { + this.messageEl.textContent = msg; + this.messageEl.style.display = "block"; + } + + private hideMessage(): void { + this.messageEl.style.display = "none"; + } + + // ── Input ───────────────────────────────────────────────────────────── + + private bindKeys(): void { + document.addEventListener("keydown", (e: KeyboardEvent) => { + if (e.key === "Enter") { + if (this.gameOver || !this.started) { + this.reset(); + return; + } + } + + if (e.key === "p" || e.key === "P" || e.key === "Escape") { + if (!this.gameOver && this.started) { + this.paused = !this.paused; + if (this.paused) { + this.showMessage("PAUSED — press P"); + } else { + this.hideMessage(); + this.lastDrop = performance.now(); + } + } + return; + } + + if (this.paused || this.gameOver || !this.started) return; + + switch (e.key) { + case "ArrowLeft": + case "a": + e.preventDefault(); + this.moveLeft(); + break; + case "ArrowRight": + case "d": + e.preventDefault(); + this.moveRight(); + break; + case "ArrowDown": + case "s": + e.preventDefault(); + this.softDrop(); + break; + case "ArrowUp": + case "x": + e.preventDefault(); + this.rotate(1); + break; + case "z": + case "Control": + e.preventDefault(); + this.rotate(-1); + break; + case " ": + e.preventDefault(); + this.hardDrop(); + break; + case "Shift": + case "c": + e.preventDefault(); + this.holdPiece(); + break; + } + }); + } + + // ── Game loop ───────────────────────────────────────────────────────── + + private reset(): void { + this.board = this.createBoard(); + this.score = 0; + this.level = 1; + this.lines = 0; + this.gameOver = false; + this.paused = false; + this.dropInterval = 1000; + this.lastDrop = performance.now(); + this.isLocking = false; + this.heldTetrominoIndex = -1; + this.canHold = true; + this.bag = []; + this.animatingRows = []; + + this.nextTetrominoIndex = this.nextFromBag(); + this.spawnPiece(); + this.updateScore(); + this.hideMessage(); + + if (!this.started) { + this.started = true; + requestAnimationFrame((t) => this.loop(t)); + } + } + + private loop(now: number): void { + if (!this.gameOver) { + requestAnimationFrame((t) => this.loop(t)); + } + + if (this.paused) { + this.drawBoard(); + this.drawPreview(); + this.drawHold(); + return; + } + + // Handle line-clear animation + if (this.animatingRows.length > 0) { + if (now - this.animationStart >= this.ANIMATION_DURATION) { + this.finishClear(); + } + this.drawBoard(); + this.drawPreview(); + this.drawHold(); + return; + } + + // Gravity + if (this.current) { + if (!this.collides(this.current.shape, this.current.x, this.current.y + 1)) { + if (now - this.lastDrop >= this.dropInterval) { + this.current.y++; + this.lastDrop = now; + this.isLocking = false; + } + } else { + // Piece is resting – start / check lock delay + if (!this.isLocking) { + this.isLocking = true; + this.lockTimer = now; + } else if (now - this.lockTimer >= this.lockDelay) { + this.lockPiece(); + this.lastDrop = now; + } + } + } + + this.drawBoard(); + this.drawPreview(); + this.drawHold(); + } +} + +// ── Bootstrap ────────────────────────────────────────────────────────────── + +window.addEventListener("DOMContentLoaded", () => { + new TetrisGame(); +}); diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/test-results/.last-run.json b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/test-results/.last-run.json @@ -0,0 +1,4 @@ +{ + "status": "passed", + "failedTests": [] +} +\ No newline at end of file diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/tests/tetris.spec.ts b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/tests/tetris.spec.ts @@ -0,0 +1,133 @@ +import { test, expect } from "@playwright/test"; + +test.describe("Tetris Game", () => { + test.beforeEach(async ({ page }) => { + await page.goto("/"); + }); + + test("renders the game canvas and UI elements", async ({ page }) => { + const canvas = page.locator("#game-canvas"); + await expect(canvas).toBeVisible(); + + // Side panels + await expect(page.locator("#score")).toHaveText("0"); + await expect(page.locator("#level")).toHaveText("1"); + await expect(page.locator("#lines")).toHaveText("0"); + + // Preview and hold canvases + await expect(page.locator("#preview-canvas")).toBeVisible(); + await expect(page.locator("#hold-canvas")).toBeVisible(); + }); + + test("shows start message before game begins", async ({ page }) => { + const msg = page.locator("#message"); + await expect(msg).toBeVisible(); + await expect(msg).toContainText("Press ENTER to start"); + }); + + test("starts the game when Enter is pressed", async ({ page }) => { + await page.keyboard.press("Enter"); + // Message should disappear + const msg = page.locator("#message"); + await expect(msg).toBeHidden(); + }); + + test("pauses and unpauses with P key", async ({ page }) => { + await page.keyboard.press("Enter"); + const msg = page.locator("#message"); + + await page.keyboard.press("p"); + await expect(msg).toBeVisible(); + await expect(msg).toContainText("PAUSED"); + + await page.keyboard.press("p"); + await expect(msg).toBeHidden(); + }); + + test("score increases on soft drop", async ({ page }) => { + await page.keyboard.press("Enter"); + // Soft-drop several times + for (let i = 0; i < 5; i++) { + await page.keyboard.press("ArrowDown"); + } + const score = await page.locator("#score").textContent(); + expect(Number(score)).toBeGreaterThanOrEqual(5); + }); + + test("score increases on hard drop", async ({ page }) => { + await page.keyboard.press("Enter"); + await page.keyboard.press(" "); // Space = hard drop + // The piece should have dropped 17+ rows → score ≥ 34 + const score = await page.locator("#score").textContent(); + expect(Number(score)).toBeGreaterThanOrEqual(10); + }); + + test("left and right arrow keys do not crash the game", async ({ page }) => { + await page.keyboard.press("Enter"); + await page.keyboard.press("ArrowLeft"); + await page.keyboard.press("ArrowLeft"); + await page.keyboard.press("ArrowRight"); + await page.keyboard.press("ArrowRight"); + await page.keyboard.press("ArrowRight"); + // Game should still be running (message hidden) + await expect(page.locator("#message")).toBeHidden(); + }); + + test("rotation keys do not crash the game", async ({ page }) => { + await page.keyboard.press("Enter"); + await page.keyboard.press("ArrowUp"); // CW + await page.keyboard.press("z"); // CCW + await page.keyboard.press("ArrowUp"); + await expect(page.locator("#message")).toBeHidden(); + }); + + test("hold piece works", async ({ page }) => { + await page.keyboard.press("Enter"); + // Hold the first piece + await page.keyboard.press("Shift"); + // Game should still be running + await expect(page.locator("#message")).toBeHidden(); + }); + + test("game over can be restarted with Enter", async ({ page }) => { + await page.keyboard.press("Enter"); + + // Stack pieces by hard-dropping many times to trigger game over + for (let i = 0; i < 100; i++) { + await page.keyboard.press(" ", { delay: 10 }); + } + + // Wait for game over message + const msg = page.locator("#message"); + await expect(msg).toContainText("GAME OVER", { timeout: 10000 }); + + // Restart + await page.keyboard.press("Enter"); + await expect(msg).toBeHidden(); + await expect(page.locator("#score")).toHaveText("0"); + await expect(page.locator("#level")).toHaveText("1"); + await expect(page.locator("#lines")).toHaveText("0"); + }); + + test("line clear increases lines count and score", async ({ page }) => { + await page.keyboard.press("Enter"); + + // We'll use a brute-force approach: drop many pieces and check that + // if any lines were cleared, the count is > 0. + // Since the game is random, we'll play for a while. + for (let i = 0; i < 40; i++) { + // Move piece to a somewhat random position and hard drop + const dir = i % 5 - 2; // -2, -1, 0, 1, 2 + for (let m = 0; m < Math.abs(dir); m++) { + await page.keyboard.press(dir < 0 ? "ArrowLeft" : "ArrowRight"); + } + await page.keyboard.press(" "); + // Small delay to let the game process + await page.waitForTimeout(50); + } + + // After 40 drops, score should have increased from hard drops at minimum + const score = await page.locator("#score").textContent(); + expect(Number(score)).toBeGreaterThan(0); + }); +}); diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/tsconfig.json b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ES2020", + "strict": true, + "outDir": "./dist", + "rootDir": "./src", + "sourceMap": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"] + }, + "include": ["src/**/*.ts"] +} diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/index.html b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/index.html @@ -0,0 +1,199 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <title>Tetris</title> + <style> + * { + margin: 0; + padding: 0; + box-sizing: border-box; + } + + body { + background: #0a0a1a; + color: #e0e0e0; + font-family: 'Segoe UI', 'Helvetica Neue', Arial, sans-serif; + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; + overflow: hidden; + } + + #game-container { + display: flex; + gap: 24px; + align-items: flex-start; + } + + .panel { + background: #12122a; + border: 2px solid #2a2a4a; + border-radius: 8px; + padding: 16px; + } + + #board-wrapper { + position: relative; + } + + #board-canvas { + display: block; + border: 2px solid #3a3a6a; + border-radius: 4px; + background: #0d0d20; + } + + .side-panel { + width: 160px; + display: flex; + flex-direction: column; + gap: 16px; + } + + .panel-title { + font-size: 12px; + text-transform: uppercase; + letter-spacing: 2px; + color: #8888aa; + margin-bottom: 8px; + text-align: center; + } + + #next-canvas { + display: block; + margin: 0 auto; + background: #0d0d20; + border: 1px solid #2a2a4a; + border-radius: 4px; + } + + .stat-row { + display: flex; + justify-content: space-between; + padding: 4px 0; + border-bottom: 1px solid #1a1a3a; + } + + .stat-label { + font-size: 11px; + text-transform: uppercase; + letter-spacing: 1px; + color: #6666aa; + } + + .stat-value { + font-size: 16px; + font-weight: bold; + color: #ffffff; + font-variant-numeric: tabular-nums; + } + + #overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + background: rgba(0, 0, 0, 0.75); + border-radius: 4px; + z-index: 10; + } + + #overlay.hidden { + display: none; + } + + #overlay-title { + font-size: 28px; + font-weight: bold; + margin-bottom: 8px; + color: #fff; + } + + #overlay-sub { + font-size: 14px; + color: #aaaacc; + } + + .controls-help { + font-size: 11px; + color: #555577; + line-height: 1.8; + text-align: center; + } + + .controls-help kbd { + background: #1a1a3a; + border: 1px solid #3a3a5a; + border-radius: 3px; + padding: 1px 6px; + font-family: inherit; + font-size: 11px; + color: #aaaacc; + } + </style> +</head> +<body> + <div id="game-container"> + <div class="side-panel"> + <div class="panel" id="hold-panel"> + <div class="panel-title">Hold</div> + <canvas id="hold-canvas" width="100" height="60"></canvas> + </div> + + <div class="panel"> + <div class="panel-title">Controls</div> + <div class="controls-help"> + <kbd>←</kbd> <kbd>→</kbd> Move<br/> + <kbd>↓</kbd> Soft Drop<br/> + <kbd>Space</kbd> Hard Drop<br/> + <kbd>↑</kbd> / <kbd>X</kbd> Rotate CW<br/> + <kbd>Z</kbd> Rotate CCW<br/> + <kbd>C</kbd> Hold<br/> + <kbd>P</kbd> Pause<br/> + </div> + </div> + </div> + + <div class="panel" id="board-wrapper"> + <canvas id="board-canvas" width="300" height="600"></canvas> + <div id="overlay"> + <div id="overlay-title">TETRIS</div> + <div id="overlay-sub">Press any key to start</div> + </div> + </div> + + <div class="side-panel"> + <div class="panel"> + <div class="panel-title">Next</div> + <canvas id="next-canvas" width="100" height="240"></canvas> + </div> + + <div class="panel"> + <div class="panel-title">Stats</div> + <div class="stat-row"> + <span class="stat-label">Score</span> + <span class="stat-value" id="score-value">0</span> + </div> + <div class="stat-row"> + <span class="stat-label">Level</span> + <span class="stat-value" id="level-value">1</span> + </div> + <div class="stat-row"> + <span class="stat-label">Lines</span> + <span class="stat-value" id="lines-value">0</span> + </div> + </div> + </div> + </div> + + <script type="module" src="./dist/game.js"></script> +</body> +</html> diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/package-lock.json b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/package-lock.json @@ -0,0 +1,2583 @@ +{ + "name": "loop-bench-gt1ikbl4", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "loop-bench-gt1ikbl4", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "@eslint/js": "^10.0.1", + "@playwright/test": "^1.59.1", + "@types/node": "^25.5.2", + "eslint": "^10.2.0", + "html-validate": "^10.11.3", + "jscpd": "^4.0.8", + "typescript": "^6.0.2" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.4", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.4.tgz", + "integrity": "sha512-lf19F24LSMfF8weXvW5QEtnLqW70u7kgit5e9PSx0MsHAFclGd1T9ynvWEMDT1w5J4Qt54tomGeAhdoAku1Xow==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.4", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.4.tgz", + "integrity": "sha512-jJhqiY3wPMlWWO3370M86CPJ7pt8GmEwSLglMfQhjXal07RCvhmU0as4IuUEW5SJeunfItiEetHmSxCCe9lDBg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.0.tgz", + "integrity": "sha512-8FTGbNzTvmSlc4cZBaShkC6YvFMG0riksYWRFKXztqVdXaQbcZLXlFbSpC05s70sGEsXAw0qwhx69JiW7hQS7A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.4.tgz", + "integrity": "sha512-55lO/7+Yp0ISKRP0PsPtNTeNGapXaO085aELZmWCVc5SH3jfrqpuU6YgOdIxMS99ZHkQN1cXKE+cdIqwww9ptw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.0.tgz", + "integrity": "sha512-ejvBr8MQCbVsWNZnCwDXjUKq40MDmHalq7cJ6e9s/qzTUFIIo/afzt1Vui9T97FM/V/pN4YsFVoed5NIa96RDg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@html-validate/stylish": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@html-validate/stylish/-/stylish-5.0.0.tgz", + "integrity": "sha512-xjhRV9k1mWfgsOcpYlwsjUOFy3w3EnCDrqUrEw+DWdvOStMK59ts2H7GLKWZtmLI5m6Npp3qMSNReafQy9K2sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^4.0.0" + }, + "engines": { + "node": "^20.11 || >= 22.16" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jscpd/badge-reporter": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@jscpd/badge-reporter/-/badge-reporter-4.0.4.tgz", + "integrity": "sha512-I9b4MmLXPM2vo0SxSUWnNGKcA4PjQlD3GzXvFK60z43cN/EIdLbOq3FVwCL+dg2obUqGXKIzAm7EsDFTg0D+mQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "badgen": "^3.2.3", + "colors": "^1.4.0", + "fs-extra": "^11.2.0" + } + }, + "node_modules/@jscpd/core": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@jscpd/core/-/core-4.0.4.tgz", + "integrity": "sha512-QGMT3iXEX1fI6lgjPH+x8eyJwhwr2KkpSF5uBpjC0Z5Xloj0yFTFLtwJT+RhxP/Ob4WYrtx2jvpKB269oIwgMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.1" + } + }, + "node_modules/@jscpd/finder": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@jscpd/finder/-/finder-4.0.4.tgz", + "integrity": "sha512-qVUWY7Nzuvfd5OIk+n7/5CM98LmFroLqblRXAI2gDABwZrc7qS+WH2SNr0qoUq0f4OqwM+piiwKvwL/VDNn/Cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jscpd/core": "4.0.4", + "@jscpd/tokenizer": "4.0.4", + "blamer": "^1.0.6", + "bytes": "^3.1.2", + "cli-table3": "^0.6.5", + "colors": "^1.4.0", + "fast-glob": "^3.3.2", + "fs-extra": "^11.2.0", + "markdown-table": "^2.0.0", + "pug": "^3.0.3" + } + }, + "node_modules/@jscpd/html-reporter": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@jscpd/html-reporter/-/html-reporter-4.0.4.tgz", + "integrity": "sha512-YiepyeYkeH74Kx59PJRdUdonznct0wHPFkf6FLQN+mCBoy6leAWCcOfHtcexnp+UsBFDlItG5nRdKrDSxSH+Kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "colors": "1.4.0", + "fs-extra": "^11.2.0", + "pug": "^3.0.3" + } + }, + "node_modules/@jscpd/tokenizer": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@jscpd/tokenizer/-/tokenizer-4.0.4.tgz", + "integrity": "sha512-xxYYY/qaLah/FlwogEbGIxx9CjDO+G9E6qawcy26WwrflzJb6wsnhjwdneN6Wb0RNCDsqvzY+bzG453jsin4UQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jscpd/core": "4.0.4", + "reprism": "^0.0.11", + "spark-md5": "^3.0.2" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@playwright/test": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz", + "integrity": "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.59.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.5.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.2.tgz", + "integrity": "sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/sarif": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@types/sarif/-/sarif-2.1.7.tgz", + "integrity": "sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/assert-never": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.4.0.tgz", + "integrity": "sha512-5oJg84os6NMQNl27T9LnZkvvqzvAnHu03ShCnoj6bsJwS7L8AO4lf+C/XjK/nvzEqQB744moC6V128RucQd1jA==", + "dev": true, + "license": "MIT" + }, + "node_modules/babel-walk": { + "version": "3.0.0-canary-5", + "resolved": "https://registry.npmjs.org/babel-walk/-/babel-walk-3.0.0-canary-5.tgz", + "integrity": "sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.9.6" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/badgen": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/badgen/-/badgen-3.2.3.tgz", + "integrity": "sha512-svDuwkc63E/z0ky3drpUppB83s/nlgDciH9m+STwwQoWyq7yCgew1qEfJ+9axkKdNq7MskByptWUN9j1PGMwFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/blamer": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/blamer/-/blamer-1.0.7.tgz", + "integrity": "sha512-GbBStl/EVlSWkiJQBZps3H1iARBrC7vt++Jb/TTmCNu/jZ04VW7tSN1nScbFXBUy1AN+jzeL7Zep9sbQxLhXKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^4.0.0", + "which": "^2.0.2" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/character-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", + "integrity": "sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-regex": "^1.0.3" + } + }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/constantinople": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz", + "integrity": "sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.6.0", + "@babel/types": "^7.6.1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/doctypes": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", + "integrity": "sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.2.0.tgz", + "integrity": "sha512-+L0vBFYGIpSNIt/KWTpFonPrqYvgKw1eUI5Vn7mEogrQcWtWYtNQ7dNqC+px/J0idT3BAkiWrhfS7k+Tum8TUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.4", + "@eslint/config-helpers": "^0.5.4", + "@eslint/core": "^1.2.0", + "@eslint/plugin-kit": "^0.7.0", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/fs-extra": { + "version": "11.3.4", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", + "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gitignore-to-glob": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/gitignore-to-glob/-/gitignore-to-glob-0.3.0.tgz", + "integrity": "sha512-mk74BdnK7lIwDHnotHddx1wsjMOFIThpLY3cPNniJ/2fA/tlLzHnFxIdR+4sLOu5KGgQJdij4kjJ2RoUNnCNMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.4 <5 || >=6.9" + } + }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-validate": { + "version": "10.11.3", + "resolved": "https://registry.npmjs.org/html-validate/-/html-validate-10.11.3.tgz", + "integrity": "sha512-wKUq9iR6bukMgiHhs/ORThZzEbQoFiiPNN7aZfQ8dlmhttPb2sM2Ji2p+Fy5Xj1aH7QHJ1biT2SUDw7A01P2oA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/html-validate" + } + ], + "license": "MIT", + "dependencies": { + "@html-validate/stylish": "^5.0.0", + "@sidvind/better-ajv-errors": "4.0.1", + "ajv": "^8.0.0", + "glob": "^13.0.0", + "kleur": "^4.1.0", + "minimist": "^1.2.0", + "prompts": "^2.0.0", + "semver": "^7.0.0" + }, + "bin": { + "html-validate": "bin/html-validate.mjs" + }, + "engines": { + "node": "^20.19.0 || >= 22.16.0" + }, + "peerDependencies": { + "jest": "^28.1.3 || ^29.0.3 || ^30.0.0", + "jest-diff": "^28.1.3 || ^29.0.3 || ^30.0.0", + "jest-snapshot": "^28.1.3 || ^29.0.3 || ^30.0.0", + "vitest": "^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.1" + }, + "peerDependenciesMeta": { + "jest": { + "optional": true + }, + "jest-diff": { + "optional": true + }, + "jest-snapshot": { + "optional": true + }, + "vitest": { + "optional": true + } + } + }, + "node_modules/html-validate/node_modules/@sidvind/better-ajv-errors": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sidvind/better-ajv-errors/-/better-ajv-errors-4.0.1.tgz", + "integrity": "sha512-6arF1ssKxItxgitPYXafUoLmsVBA6K7m9+ZGj6hLDoBl7nWpJ33EInwQUdHTle2METeWGxgQiqSex20KZRykew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "kleur": "^4.1.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "ajv": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/html-validate/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/html-validate/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-expression": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", + "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^7.1.1", + "object-assign": "^4.1.1" + } + }, + "node_modules/is-expression/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-stringify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", + "integrity": "sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==", + "dev": true, + "license": "MIT" + }, + "node_modules/jscpd": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/jscpd/-/jscpd-4.0.8.tgz", + "integrity": "sha512-d2VNT/2Hv4dxT2/59He8Lyda4DYOxPRyRG9zBaOpTZAqJCVf2xLrBlZkT8Va6Lo9u3X2qz8Bpq4HrDi4JsrQhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jscpd/badge-reporter": "4.0.4", + "@jscpd/core": "4.0.4", + "@jscpd/finder": "4.0.4", + "@jscpd/html-reporter": "4.0.4", + "@jscpd/tokenizer": "4.0.4", + "colors": "^1.4.0", + "commander": "^5.0.0", + "fs-extra": "^11.2.0", + "gitignore-to-glob": "^0.3.0", + "jscpd-sarif-reporter": "4.0.6" + }, + "bin": { + "jscpd": "bin/jscpd" + } + }, + "node_modules/jscpd-sarif-reporter": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/jscpd-sarif-reporter/-/jscpd-sarif-reporter-4.0.6.tgz", + "integrity": "sha512-b9Sm3IPZ3+m8Lwa4gZa+4/LhDhlc/ZLEsLXKSOy1DANQ6kx0ueqZT+fUHWEdQ6m0o3+RIVIa7DmvLSojQD05ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "colors": "^1.4.0", + "fs-extra": "^11.2.0", + "node-sarif-builder": "^3.4.0" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jstransformer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", + "integrity": "sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-promise": "^2.0.0", + "promise": "^7.0.1" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/markdown-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz", + "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "repeat-string": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-sarif-builder": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/node-sarif-builder/-/node-sarif-builder-3.4.0.tgz", + "integrity": "sha512-tGnJW6OKRii9u/b2WiUViTJS+h7Apxx17qsMUjsUeNDiMMX5ZFf8F8Fcz7PAQ6omvOxHZtvDTmOYKJQwmfpjeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/sarif": "^2.1.7", + "fs-extra": "^11.1.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/playwright": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz", + "integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.59.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz", + "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "asap": "~2.0.3" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prompts/node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pug": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.4.tgz", + "integrity": "sha512-kFfq5mMzrS7+wrl5pLJzZEzemx34OQ0w4SARfhy/3yxTlhbstsudDwJzhf1hP02yHzbjoVMSXUj/Sz6RNfMyXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pug-code-gen": "^3.0.4", + "pug-filters": "^4.0.0", + "pug-lexer": "^5.0.1", + "pug-linker": "^4.0.0", + "pug-load": "^3.0.0", + "pug-parser": "^6.0.0", + "pug-runtime": "^3.0.1", + "pug-strip-comments": "^2.0.0" + } + }, + "node_modules/pug-attrs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-3.0.0.tgz", + "integrity": "sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==", + "dev": true, + "license": "MIT", + "dependencies": { + "constantinople": "^4.0.1", + "js-stringify": "^1.0.2", + "pug-runtime": "^3.0.0" + } + }, + "node_modules/pug-code-gen": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-3.0.4.tgz", + "integrity": "sha512-6okWYIKdasTyXICyEtvobmTZAVX57JkzgzIi4iRJlin8kmhG+Xry2dsus+Mun/nGCn6F2U49haHI5mkELXB14g==", + "dev": true, + "license": "MIT", + "dependencies": { + "constantinople": "^4.0.1", + "doctypes": "^1.1.0", + "js-stringify": "^1.0.2", + "pug-attrs": "^3.0.0", + "pug-error": "^2.1.0", + "pug-runtime": "^3.0.1", + "void-elements": "^3.1.0", + "with": "^7.0.0" + } + }, + "node_modules/pug-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-2.1.0.tgz", + "integrity": "sha512-lv7sU9e5Jk8IeUheHata6/UThZ7RK2jnaaNztxfPYUY+VxZyk/ePVaNZ/vwmH8WqGvDz3LrNYt/+gA55NDg6Pg==", + "dev": true, + "license": "MIT" + }, + "node_modules/pug-filters": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-4.0.0.tgz", + "integrity": "sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "constantinople": "^4.0.1", + "jstransformer": "1.0.0", + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0", + "resolve": "^1.15.1" + } + }, + "node_modules/pug-lexer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-5.0.1.tgz", + "integrity": "sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-parser": "^2.2.0", + "is-expression": "^4.0.0", + "pug-error": "^2.0.0" + } + }, + "node_modules/pug-linker": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-4.0.0.tgz", + "integrity": "sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0" + } + }, + "node_modules/pug-load": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-3.0.0.tgz", + "integrity": "sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "object-assign": "^4.1.1", + "pug-walk": "^2.0.0" + } + }, + "node_modules/pug-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-6.0.0.tgz", + "integrity": "sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pug-error": "^2.0.0", + "token-stream": "1.0.0" + } + }, + "node_modules/pug-runtime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-3.0.1.tgz", + "integrity": "sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==", + "dev": true, + "license": "MIT" + }, + "node_modules/pug-strip-comments": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz", + "integrity": "sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "pug-error": "^2.0.0" + } + }, + "node_modules/pug-walk": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz", + "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/reprism": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/reprism/-/reprism-0.0.11.tgz", + "integrity": "sha512-VsxDR5QxZo08M/3nRypNlScw5r3rKeSOPdU/QhDmu3Ai3BJxHn/qgfXGWQp/tAxUtzwYNo9W6997JZR0tPLZsA==", + "dev": true, + "license": "MIT" + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/spark-md5": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/spark-md5/-/spark-md5-3.0.2.tgz", + "integrity": "sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw==", + "dev": true, + "license": "(WTFPL OR MIT)" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/token-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", + "integrity": "sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg==", + "dev": true, + "license": "MIT" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", + "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/with": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz", + "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.9.6", + "@babel/types": "^7.9.6", + "assert-never": "^1.2.1", + "babel-walk": "3.0.0-canary-5" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/package.json b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/package.json @@ -0,0 +1,23 @@ +{ + "name": "loop-bench-gt1ikbl4", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "build": "tsc", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "commonjs", + "devDependencies": { + "@eslint/js": "^10.0.1", + "@playwright/test": "^1.59.1", + "@types/node": "^25.5.2", + "eslint": "^10.2.0", + "html-validate": "^10.11.3", + "jscpd": "^4.0.8", + "typescript": "^6.0.2" + } +} diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/report/jscpd-report.json b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/report/jscpd-report.json @@ -0,0 +1,144 @@ +{ + "statistics": { + "detectionDate": "2026-04-04T21:01:57.648Z", + "formats": { + "typescript": { + "sources": { + "src/game.ts": { + "lines": 762, + "tokens": 8372, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "total": { + "lines": 762, + "tokens": 8372, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "javascript": { + "sources": { + "dist/game.js": { + "lines": 644, + "tokens": 8030, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "total": { + "lines": 644, + "tokens": 8030, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "json": { + "sources": { + "tsconfig.json": { + "lines": 15, + "tokens": 104, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + }, + "package.json": { + "lines": 22, + "tokens": 139, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "total": { + "lines": 37, + "tokens": 243, + "sources": 2, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "markup": { + "sources": { + "index.html": { + "lines": 198, + "tokens": 1337, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "total": { + "lines": 198, + "tokens": 1337, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + } + }, + "total": { + "lines": 1641, + "tokens": 17982, + "sources": 5, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "duplicates": [] +} +\ No newline at end of file diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/src/game.ts b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/src/game.ts @@ -0,0 +1,763 @@ +// ────────────────────────────────────────────── +// Tetris – full browser implementation +// ────────────────────────────────────────────── + +// ── Constants ──────────────────────────────── + +const COLS = 10; +const ROWS = 20; +const HIDDEN_ROWS = 2; // rows above visible area for spawning +const TOTAL_ROWS = ROWS + HIDDEN_ROWS; +const CELL = 30; // pixel size of each cell +const NEXT_CELL = 18; +const HOLD_CELL = 18; +const LOCK_DELAY = 500; // ms before piece locks after landing +const MAX_LOCK_RESETS = 15; + +// NES-inspired speed curve: frames per gravity tick at 60fps → converted to ms +const LEVEL_SPEEDS: number[] = [ + 800, 717, 633, 550, 467, 383, 300, 217, 133, 100, + 83, 83, 83, 67, 67, 67, 50, 50, 50, 33, +]; + +const SOFT_DROP_SPEED = 50; // ms + +// Scoring (guideline) +const LINE_SCORES = [0, 100, 300, 500, 800]; // 0,1,2,3,4 lines +const SOFT_DROP_SCORE = 1; // per cell +const HARD_DROP_SCORE = 2; // per cell + +// ── Piece Definitions (SRS) ───────────────── + +type PieceType = 'I' | 'O' | 'T' | 'S' | 'Z' | 'J' | 'L'; + +// Each rotation state is a list of [row, col] offsets from piece origin +const PIECE_SHAPES: Record<PieceType, number[][][]> = { + I: [ + [[0,-1],[0,0],[0,1],[0,2]], + [[-1,1],[0,1],[1,1],[2,1]], + [[1,-1],[1,0],[1,1],[1,2]], + [[-1,0],[0,0],[1,0],[2,0]], + ], + O: [ + [[0,0],[0,1],[1,0],[1,1]], + [[0,0],[0,1],[1,0],[1,1]], + [[0,0],[0,1],[1,0],[1,1]], + [[0,0],[0,1],[1,0],[1,1]], + ], + T: [ + [[0,-1],[0,0],[0,1],[-1,0]], + [[-1,0],[0,0],[1,0],[0,1]], + [[0,-1],[0,0],[0,1],[1,0]], + [[-1,0],[0,0],[1,0],[0,-1]], + ], + S: [ + [[0,-1],[0,0],[-1,0],[-1,1]], + [[-1,0],[0,0],[0,1],[1,1]], + [[1,-1],[1,0],[0,0],[0,1]], + [[-1,-1],[0,-1],[0,0],[1,0]], + ], + Z: [ + [[-1,-1],[-1,0],[0,0],[0,1]], + [[0,0],[1,0],[-1,1],[0,1]], + [[0,-1],[0,0],[1,0],[1,1]], + [[0,0],[0,-1],[1,-1],[-1,0]], + ], + J: [ + [[-1,-1],[0,-1],[0,0],[0,1]], + [[-1,0],[0,0],[1,0],[-1,1]], + [[0,-1],[0,0],[0,1],[1,1]], + [[1,0],[0,0],[-1,0],[1,-1]], + ], + L: [ + [[-1,1],[0,-1],[0,0],[0,1]], + [[-1,0],[0,0],[1,0],[1,1]], + [[0,-1],[0,0],[0,1],[1,-1]], + [[-1,-1],[-1,0],[0,0],[1,0]], + ], +}; + +// SRS wall kick data (non-I pieces) +const WALL_KICKS: Record<string, number[][]> = { + '0>1': [[0,0],[-1,0],[-1,-1],[0,2],[-1,2]], + '1>0': [[0,0],[1,0],[1,1],[0,-2],[1,-2]], + '1>2': [[0,0],[1,0],[1,1],[0,-2],[1,-2]], + '2>1': [[0,0],[-1,0],[-1,-1],[0,2],[-1,2]], + '2>3': [[0,0],[1,0],[1,-1],[0,2],[1,2]], + '3>2': [[0,0],[-1,0],[-1,1],[0,-2],[-1,-2]], + '3>0': [[0,0],[-1,0],[-1,1],[0,-2],[-1,-2]], + '0>3': [[0,0],[1,0],[1,-1],[0,2],[1,2]], +}; + +const I_WALL_KICKS: Record<string, number[][]> = { + '0>1': [[0,0],[-2,0],[1,0],[-2,1],[1,-2]], + '1>0': [[0,0],[2,0],[-1,0],[2,-1],[-1,2]], + '1>2': [[0,0],[-1,0],[2,0],[-1,-2],[2,1]], + '2>1': [[0,0],[1,0],[-2,0],[1,2],[-2,-1]], + '2>3': [[0,0],[2,0],[-1,0],[2,-1],[-1,2]], + '3>2': [[0,0],[-2,0],[1,0],[-2,1],[1,-2]], + '3>0': [[0,0],[1,0],[-2,0],[1,2],[-2,-1]], + '0>3': [[0,0],[-1,0],[2,0],[-1,-2],[2,1]], +}; + +const PIECE_COLORS: Record<PieceType, string> = { + I: '#00e5ff', + O: '#ffea00', + T: '#d500f9', + S: '#00e676', + Z: '#ff1744', + J: '#2979ff', + L: '#ff9100', +}; + +const GHOST_ALPHA = 0.25; + +const ALL_PIECES: PieceType[] = ['I', 'O', 'T', 'S', 'Z', 'J', 'L']; + +// ── Types ──────────────────────────────────── + +type Cell = PieceType | null; + +interface Piece { + type: PieceType; + row: number; // position on the board (top-left of bounding box concept → actually the origin) + col: number; + rotation: number; // 0-3 +} + +type GameState = 'idle' | 'playing' | 'paused' | 'gameover'; + +// ── Bag Randomizer (7-bag) ─────────────────── + +function shuffle<T>(arr: T[]): T[] { + const a = [...arr]; + for (let i = a.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [a[i], a[j]] = [a[j], a[i]]; + } + return a; +} + +class BagRandomizer { + private queue: PieceType[] = []; + + next(): PieceType { + if (this.queue.length < 7) { + this.queue.push(...shuffle(ALL_PIECES)); + } + return this.queue.shift()!; + } + + peek(count: number): PieceType[] { + while (this.queue.length < count + 7) { + this.queue.push(...shuffle(ALL_PIECES)); + } + return this.queue.slice(0, count); + } + + reset(): void { + this.queue = []; + } +} + +// ── Board helpers ──────────────────────────── + +function createBoard(): Cell[][] { + return Array.from({ length: TOTAL_ROWS }, () => Array<Cell>(COLS).fill(null)); +} + +function getBlocks(piece: Piece): [number, number][] { + return PIECE_SHAPES[piece.type][piece.rotation].map( + ([dr, dc]) => [piece.row + dr, piece.col + dc] as [number, number] + ); +} + +function isValid(board: Cell[][], piece: Piece): boolean { + for (const [r, c] of getBlocks(piece)) { + if (r < 0 || r >= TOTAL_ROWS || c < 0 || c >= COLS) return false; + if (board[r][c] !== null) return false; + } + return true; +} + +// ── Main Game Class ────────────────────────── + +class TetrisGame { + private board: Cell[][] = createBoard(); + private bag = new BagRandomizer(); + private current: Piece | null = null; + private holdPiece: PieceType | null = null; + private holdUsed = false; + private state: GameState = 'idle'; + + // Stats + private score = 0; + private level = 1; + private lines = 0; + + // Timing + private lastDrop = 0; + private lockTimer = 0; + private lockResets = 0; + private softDropping = false; + + // Line clear animation + private clearingRows: number[] = []; + private clearAnimStart = 0; + private readonly CLEAR_ANIM_MS = 300; + + // Canvas refs + private boardCtx: CanvasRenderingContext2D; + private nextCtx: CanvasRenderingContext2D; + private holdCtx: CanvasRenderingContext2D; + + // DOM refs + private scoreEl: HTMLElement; + private levelEl: HTMLElement; + private linesEl: HTMLElement; + private overlay: HTMLElement; + private overlayTitle: HTMLElement; + private overlaySub: HTMLElement; + + constructor() { + const boardCanvas = document.getElementById('board-canvas') as HTMLCanvasElement; + const nextCanvas = document.getElementById('next-canvas') as HTMLCanvasElement; + const holdCanvas = document.getElementById('hold-canvas') as HTMLCanvasElement; + + this.boardCtx = boardCanvas.getContext('2d')!; + this.nextCtx = nextCanvas.getContext('2d')!; + this.holdCtx = holdCanvas.getContext('2d')!; + + this.scoreEl = document.getElementById('score-value')!; + this.levelEl = document.getElementById('level-value')!; + this.linesEl = document.getElementById('lines-value')!; + this.overlay = document.getElementById('overlay')!; + this.overlayTitle = document.getElementById('overlay-title')!; + this.overlaySub = document.getElementById('overlay-sub')!; + + this.bindInput(); + this.showOverlay('TETRIS', 'Press any key to start'); + requestAnimationFrame(this.loop); + } + + // ── Input ──────────────────────────────── + + private bindInput(): void { + document.addEventListener('keydown', (e) => { + // Start game on any key from idle + if (this.state === 'idle') { + e.preventDefault(); + this.startGame(); + return; + } + + // Restart on Enter from gameover + if (this.state === 'gameover') { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + this.startGame(); + } + return; + } + + // Toggle pause + if (e.key === 'p' || e.key === 'P') { + e.preventDefault(); + this.togglePause(); + return; + } + + if (this.state !== 'playing' || !this.current) return; + if (this.clearingRows.length > 0) return; // animating + + switch (e.key) { + case 'ArrowLeft': + e.preventDefault(); + this.move(0, -1); + break; + case 'ArrowRight': + e.preventDefault(); + this.move(0, 1); + break; + case 'ArrowDown': + e.preventDefault(); + this.softDropping = true; + this.softDrop(); + break; + case 'ArrowUp': + case 'x': + case 'X': + e.preventDefault(); + this.rotate(1); + break; + case 'z': + case 'Z': + e.preventDefault(); + this.rotate(-1); + break; + case ' ': + e.preventDefault(); + this.hardDrop(); + break; + case 'c': + case 'C': + e.preventDefault(); + this.hold(); + break; + } + }); + + document.addEventListener('keyup', (e) => { + if (e.key === 'ArrowDown') { + this.softDropping = false; + } + }); + } + + // ── Game lifecycle ─────────────────────── + + private startGame(): void { + this.board = createBoard(); + this.bag.reset(); + this.current = null; + this.holdPiece = null; + this.holdUsed = false; + this.score = 0; + this.level = 1; + this.lines = 0; + this.clearingRows = []; + this.softDropping = false; + this.state = 'playing'; + this.hideOverlay(); + this.spawnPiece(); + this.lastDrop = performance.now(); + this.updateStats(); + } + + private gameOver(): void { + this.state = 'gameover'; + this.showOverlay('GAME OVER', `Score: ${this.score} — Press Enter to restart`); + } + + private togglePause(): void { + if (this.state === 'playing') { + this.state = 'paused'; + this.showOverlay('PAUSED', 'Press P to resume'); + } else if (this.state === 'paused') { + this.state = 'playing'; + this.lastDrop = performance.now(); + this.hideOverlay(); + } + } + + // ── Overlay helpers ────────────────────── + + private showOverlay(title: string, sub: string): void { + this.overlayTitle.textContent = title; + this.overlaySub.textContent = sub; + this.overlay.classList.remove('hidden'); + } + + private hideOverlay(): void { + this.overlay.classList.add('hidden'); + } + + // ── Piece spawning ────────────────────── + + private spawnPiece(): void { + const type = this.bag.next(); + this.current = { + type, + row: HIDDEN_ROWS - 1, // spawn near top of hidden area + col: Math.floor(COLS / 2) - 1, + rotation: 0, + }; + this.lockTimer = 0; + this.lockResets = 0; + this.holdUsed = false; + + // Try to push down into the visible area + if (isValid(this.board, { ...this.current, row: this.current.row + 1 })) { + this.current.row++; + } + + if (!isValid(this.board, this.current)) { + // Can't spawn → game over + this.placePiece(); + this.gameOver(); + } + } + + // ── Movement ───────────────────────────── + + private move(dr: number, dc: number): boolean { + if (!this.current) return false; + const test = { ...this.current, row: this.current.row + dr, col: this.current.col + dc }; + if (isValid(this.board, test)) { + this.current = test; + this.resetLockIfOnGround(); + return true; + } + return false; + } + + private rotate(dir: 1 | -1): void { + if (!this.current) return; + const from = this.current.rotation; + const to = (from + dir + 4) % 4; + const kicks = this.current.type === 'I' + ? I_WALL_KICKS[`${from}>${to}`] + : WALL_KICKS[`${from}>${to}`]; + + if (!kicks) return; + + for (const [dc, dr] of kicks) { + const test: Piece = { + ...this.current, + rotation: to, + row: this.current.row - dr, // SRS kick data: positive y = up, our row increases downward + col: this.current.col + dc, + }; + if (isValid(this.board, test)) { + this.current = test; + this.resetLockIfOnGround(); + return; + } + } + } + + private softDrop(): void { + if (this.move(1, 0)) { + this.score += SOFT_DROP_SCORE; + this.lastDrop = performance.now(); + this.updateStats(); + } + } + + private hardDrop(): void { + if (!this.current) return; + let dist = 0; + while (this.move(1, 0)) { + dist++; + } + this.score += dist * HARD_DROP_SCORE; + this.updateStats(); + this.placePiece(); + this.afterPlace(); + } + + private hold(): void { + if (!this.current || this.holdUsed) return; + const type = this.current.type; + if (this.holdPiece) { + // Spawn held piece + this.current = { + type: this.holdPiece, + row: HIDDEN_ROWS, + col: Math.floor(COLS / 2) - 1, + rotation: 0, + }; + this.holdPiece = type; + this.lockTimer = 0; + this.lockResets = 0; + } else { + this.holdPiece = type; + this.spawnPiece(); + } + this.holdUsed = true; + } + + // ── Lock delay ─────────────────────────── + + private isOnGround(): boolean { + if (!this.current) return false; + return !isValid(this.board, { ...this.current, row: this.current.row + 1 }); + } + + private resetLockIfOnGround(): void { + if (this.isOnGround() && this.lockResets < MAX_LOCK_RESETS) { + this.lockTimer = 0; + this.lockResets++; + } + } + + // ── Place piece & clear lines ──────────── + + private placePiece(): void { + if (!this.current) return; + for (const [r, c] of getBlocks(this.current)) { + if (r >= 0 && r < TOTAL_ROWS && c >= 0 && c < COLS) { + this.board[r][c] = this.current.type; + } + } + this.current = null; + } + + private afterPlace(): void { + // Find full rows + const fullRows: number[] = []; + for (let r = 0; r < TOTAL_ROWS; r++) { + if (this.board[r].every(cell => cell !== null)) { + fullRows.push(r); + } + } + + if (fullRows.length > 0) { + this.clearingRows = fullRows; + this.clearAnimStart = performance.now(); + } else { + this.spawnPiece(); + } + } + + private finishClear(): void { + const count = this.clearingRows.length; + + // Remove rows top-to-bottom (indices are sorted ascending) + for (const r of this.clearingRows.sort((a, b) => a - b)) { + this.board.splice(r, 1); + this.board.unshift(Array<Cell>(COLS).fill(null)); + } + + this.lines += count; + this.score += LINE_SCORES[count] * this.level; + this.level = Math.floor(this.lines / 10) + 1; + this.clearingRows = []; + this.updateStats(); + this.spawnPiece(); + } + + // ── Ghost piece ────────────────────────── + + private ghostRow(): number { + if (!this.current) return 0; + let test = { ...this.current }; + while (isValid(this.board, { ...test, row: test.row + 1 })) { + test.row++; + } + return test.row; + } + + // ── Stats DOM ──────────────────────────── + + private updateStats(): void { + this.scoreEl.textContent = this.score.toLocaleString(); + this.levelEl.textContent = String(this.level); + this.linesEl.textContent = String(this.lines); + } + + // ── Gravity speed ──────────────────────── + + private getSpeed(): number { + if (this.softDropping) return SOFT_DROP_SPEED; + const idx = Math.min(this.level - 1, LEVEL_SPEEDS.length - 1); + return LEVEL_SPEEDS[idx]; + } + + // ── Main loop ──────────────────────────── + + private loop = (now: number): void => { + requestAnimationFrame(this.loop); + + if (this.state !== 'playing') { + this.render(now); + return; + } + + // Line clear animation + if (this.clearingRows.length > 0) { + if (now - this.clearAnimStart >= this.CLEAR_ANIM_MS) { + this.finishClear(); + } + this.render(now); + return; + } + + if (!this.current) return; + + // Gravity + const speed = this.getSpeed(); + if (now - this.lastDrop >= speed) { + if (this.move(1, 0)) { + this.lastDrop = now; + } else { + // On ground + this.lastDrop = now; // prevent repeated triggers + } + } + + // Lock delay + if (this.isOnGround()) { + if (this.lockTimer === 0) { + this.lockTimer = now; + } else if (now - this.lockTimer >= LOCK_DELAY) { + this.placePiece(); + this.afterPlace(); + } + } else { + this.lockTimer = 0; + } + + this.render(now); + }; + + // ── Rendering ──────────────────────────── + + private render(now: number): void { + this.drawBoard(now); + this.drawNext(); + this.drawHold(); + } + + private drawBoard(now: number): void { + const ctx = this.boardCtx; + ctx.clearRect(0, 0, COLS * CELL, ROWS * CELL); + + // Grid lines + ctx.strokeStyle = '#1a1a30'; + ctx.lineWidth = 0.5; + for (let r = 0; r <= ROWS; r++) { + ctx.beginPath(); + ctx.moveTo(0, r * CELL); + ctx.lineTo(COLS * CELL, r * CELL); + ctx.stroke(); + } + for (let c = 0; c <= COLS; c++) { + ctx.beginPath(); + ctx.moveTo(c * CELL, 0); + ctx.lineTo(c * CELL, ROWS * CELL); + ctx.stroke(); + } + + // Board cells (skip hidden rows for drawing) + for (let r = HIDDEN_ROWS; r < TOTAL_ROWS; r++) { + for (let c = 0; c < COLS; c++) { + const cell = this.board[r][c]; + if (cell) { + const isClearing = this.clearingRows.includes(r); + if (isClearing) { + const progress = Math.min(1, (now - this.clearAnimStart) / this.CLEAR_ANIM_MS); + const flash = Math.sin(progress * Math.PI * 3) * 0.5 + 0.5; + ctx.globalAlpha = 1 - progress * 0.5; + this.drawCell(ctx, c, r - HIDDEN_ROWS, cell, flash); + ctx.globalAlpha = 1; + } else { + this.drawCell(ctx, c, r - HIDDEN_ROWS, cell); + } + } + } + } + + if (!this.current) return; + + // Ghost piece + const ghostR = this.ghostRow(); + if (ghostR !== this.current.row) { + const ghostPiece: Piece = { ...this.current, row: ghostR }; + for (const [r, c] of getBlocks(ghostPiece)) { + if (r >= HIDDEN_ROWS) { + ctx.globalAlpha = GHOST_ALPHA; + this.drawCell(ctx, c, r - HIDDEN_ROWS, this.current.type); + ctx.globalAlpha = 1; + } + } + } + + // Current piece + for (const [r, c] of getBlocks(this.current)) { + if (r >= HIDDEN_ROWS) { + this.drawCell(ctx, c, r - HIDDEN_ROWS, this.current.type); + } + } + } + + private drawCell( + ctx: CanvasRenderingContext2D, + col: number, + row: number, + type: PieceType, + flash = 0 + ): void { + const x = col * CELL; + const y = row * CELL; + const color = PIECE_COLORS[type]; + const inset = 1; + + // Main fill + ctx.fillStyle = color; + ctx.fillRect(x + inset, y + inset, CELL - inset * 2, CELL - inset * 2); + + // Highlight (top-left bevel) + ctx.fillStyle = `rgba(255,255,255,${0.25 + flash * 0.5})`; + ctx.fillRect(x + inset, y + inset, CELL - inset * 2, 2); + ctx.fillRect(x + inset, y + inset, 2, CELL - inset * 2); + + // Shadow (bottom-right bevel) + ctx.fillStyle = 'rgba(0,0,0,0.3)'; + ctx.fillRect(x + inset, y + CELL - inset - 2, CELL - inset * 2, 2); + ctx.fillRect(x + CELL - inset - 2, y + inset, 2, CELL - inset * 2); + } + + private drawPiecePreview( + ctx: CanvasRenderingContext2D, + type: PieceType, + offsetY: number, + cellSize: number, + canvasWidth: number + ): void { + const blocks = PIECE_SHAPES[type][0]; // rotation 0 + // Find bounding box + let minC = Infinity, maxC = -Infinity, minR = Infinity, maxR = -Infinity; + for (const [dr, dc] of blocks) { + minR = Math.min(minR, dr); + maxR = Math.max(maxR, dr); + minC = Math.min(minC, dc); + maxC = Math.max(maxC, dc); + } + const w = (maxC - minC + 1) * cellSize; + const h = (maxR - minR + 1) * cellSize; + const ox = (canvasWidth - w) / 2; + const oy = offsetY + (3 * cellSize - h) / 2; + + for (const [dr, dc] of blocks) { + const x = ox + (dc - minC) * cellSize; + const y = oy + (dr - minR) * cellSize; + const color = PIECE_COLORS[type]; + const inset = 1; + ctx.fillStyle = color; + ctx.fillRect(x + inset, y + inset, cellSize - inset * 2, cellSize - inset * 2); + ctx.fillStyle = 'rgba(255,255,255,0.2)'; + ctx.fillRect(x + inset, y + inset, cellSize - inset * 2, 1); + ctx.fillRect(x + inset, y + inset, 1, cellSize - inset * 2); + } + } + + private drawNext(): void { + const ctx = this.nextCtx; + const canvas = ctx.canvas; + ctx.clearRect(0, 0, canvas.width, canvas.height); + + const previews = this.bag.peek(4); + for (let i = 0; i < previews.length; i++) { + this.drawPiecePreview(ctx, previews[i], i * 60, NEXT_CELL, canvas.width); + } + } + + private drawHold(): void { + const ctx = this.holdCtx; + const canvas = ctx.canvas; + ctx.clearRect(0, 0, canvas.width, canvas.height); + + if (this.holdPiece) { + ctx.globalAlpha = this.holdUsed ? 0.4 : 1; + this.drawPiecePreview(ctx, this.holdPiece, 0, HOLD_CELL, canvas.width); + ctx.globalAlpha = 1; + } + } +} + +// ── Boot ───────────────────────────────────── + +document.addEventListener('DOMContentLoaded', () => { + new TetrisGame(); +}); diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/tsconfig.json b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "outDir": "./dist", + "rootDir": "./src", + "sourceMap": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"] + }, + "include": ["src/**/*"] +} diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/index.html b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/index.html @@ -0,0 +1,118 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <title>Tetris</title> + <style> + * { margin: 0; padding: 0; box-sizing: border-box; } + + body { + background: #0a0a1a; + color: #e0e0e0; + font-family: 'Segoe UI', 'Consolas', monospace; + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; + overflow: hidden; + } + + .game-wrapper { + display: flex; + gap: 24px; + align-items: flex-start; + } + + #game-canvas { + border: 3px solid #444; + border-radius: 4px; + box-shadow: 0 0 30px rgba(80, 80, 255, 0.15); + } + + .sidebar { + display: flex; + flex-direction: column; + gap: 20px; + min-width: 160px; + } + + .panel { + background: #1a1a2e; + border: 2px solid #333; + border-radius: 8px; + padding: 16px; + } + + .panel h3 { + font-size: 11px; + text-transform: uppercase; + letter-spacing: 2px; + color: #888; + margin-bottom: 10px; + } + + .panel .value { + font-size: 26px; + font-weight: bold; + color: #fff; + font-variant-numeric: tabular-nums; + } + + #preview-canvas { + display: block; + margin: 0 auto; + border-radius: 4px; + } + + .controls { + font-size: 12px; + line-height: 1.9; + color: #777; + } + + .controls kbd { + background: #2a2a3e; + border: 1px solid #444; + border-radius: 3px; + padding: 1px 6px; + font-size: 11px; + color: #ccc; + } + </style> +</head> +<body> + <div class="game-wrapper"> + <canvas id="game-canvas"></canvas> + <div class="sidebar"> + <div class="panel"> + <h3>Next</h3> + <canvas id="preview-canvas"></canvas> + </div> + <div class="panel"> + <h3>Score</h3> + <div class="value" id="score">0</div> + </div> + <div class="panel"> + <h3>Lines</h3> + <div class="value" id="lines">0</div> + </div> + <div class="panel"> + <h3>Level</h3> + <div class="value" id="level">0</div> + </div> + <div class="panel controls"> + <h3>Controls</h3> + <kbd>&larr;</kbd> <kbd>&rarr;</kbd> Move<br/> + <kbd>&darr;</kbd> Soft drop<br/> + <kbd>&uarr;</kbd> Rotate CW<br/> + <kbd>Z</kbd> Rotate CCW<br/> + <kbd>Space</kbd> Hard drop<br/> + <kbd>P</kbd> / <kbd>Esc</kbd> Pause<br/> + <kbd>R</kbd> Restart + </div> + </div> + </div> + <script type="module" src="dist/tetris.js"></script> +</body> +</html> diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/package-lock.json b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/package-lock.json @@ -0,0 +1,2583 @@ +{ + "name": "loop-bench-amzysv_v", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "loop-bench-amzysv_v", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "@eslint/js": "^10.0.1", + "@playwright/test": "^1.59.1", + "@types/node": "^25.5.2", + "eslint": "^10.2.0", + "html-validate": "^10.11.3", + "jscpd": "^4.0.8", + "typescript": "^6.0.2" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.4", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.4.tgz", + "integrity": "sha512-lf19F24LSMfF8weXvW5QEtnLqW70u7kgit5e9PSx0MsHAFclGd1T9ynvWEMDT1w5J4Qt54tomGeAhdoAku1Xow==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.4", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.4.tgz", + "integrity": "sha512-jJhqiY3wPMlWWO3370M86CPJ7pt8GmEwSLglMfQhjXal07RCvhmU0as4IuUEW5SJeunfItiEetHmSxCCe9lDBg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.0.tgz", + "integrity": "sha512-8FTGbNzTvmSlc4cZBaShkC6YvFMG0riksYWRFKXztqVdXaQbcZLXlFbSpC05s70sGEsXAw0qwhx69JiW7hQS7A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.4.tgz", + "integrity": "sha512-55lO/7+Yp0ISKRP0PsPtNTeNGapXaO085aELZmWCVc5SH3jfrqpuU6YgOdIxMS99ZHkQN1cXKE+cdIqwww9ptw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.0.tgz", + "integrity": "sha512-ejvBr8MQCbVsWNZnCwDXjUKq40MDmHalq7cJ6e9s/qzTUFIIo/afzt1Vui9T97FM/V/pN4YsFVoed5NIa96RDg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@html-validate/stylish": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@html-validate/stylish/-/stylish-5.0.0.tgz", + "integrity": "sha512-xjhRV9k1mWfgsOcpYlwsjUOFy3w3EnCDrqUrEw+DWdvOStMK59ts2H7GLKWZtmLI5m6Npp3qMSNReafQy9K2sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^4.0.0" + }, + "engines": { + "node": "^20.11 || >= 22.16" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jscpd/badge-reporter": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@jscpd/badge-reporter/-/badge-reporter-4.0.4.tgz", + "integrity": "sha512-I9b4MmLXPM2vo0SxSUWnNGKcA4PjQlD3GzXvFK60z43cN/EIdLbOq3FVwCL+dg2obUqGXKIzAm7EsDFTg0D+mQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "badgen": "^3.2.3", + "colors": "^1.4.0", + "fs-extra": "^11.2.0" + } + }, + "node_modules/@jscpd/core": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@jscpd/core/-/core-4.0.4.tgz", + "integrity": "sha512-QGMT3iXEX1fI6lgjPH+x8eyJwhwr2KkpSF5uBpjC0Z5Xloj0yFTFLtwJT+RhxP/Ob4WYrtx2jvpKB269oIwgMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.1" + } + }, + "node_modules/@jscpd/finder": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@jscpd/finder/-/finder-4.0.4.tgz", + "integrity": "sha512-qVUWY7Nzuvfd5OIk+n7/5CM98LmFroLqblRXAI2gDABwZrc7qS+WH2SNr0qoUq0f4OqwM+piiwKvwL/VDNn/Cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jscpd/core": "4.0.4", + "@jscpd/tokenizer": "4.0.4", + "blamer": "^1.0.6", + "bytes": "^3.1.2", + "cli-table3": "^0.6.5", + "colors": "^1.4.0", + "fast-glob": "^3.3.2", + "fs-extra": "^11.2.0", + "markdown-table": "^2.0.0", + "pug": "^3.0.3" + } + }, + "node_modules/@jscpd/html-reporter": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@jscpd/html-reporter/-/html-reporter-4.0.4.tgz", + "integrity": "sha512-YiepyeYkeH74Kx59PJRdUdonznct0wHPFkf6FLQN+mCBoy6leAWCcOfHtcexnp+UsBFDlItG5nRdKrDSxSH+Kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "colors": "1.4.0", + "fs-extra": "^11.2.0", + "pug": "^3.0.3" + } + }, + "node_modules/@jscpd/tokenizer": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@jscpd/tokenizer/-/tokenizer-4.0.4.tgz", + "integrity": "sha512-xxYYY/qaLah/FlwogEbGIxx9CjDO+G9E6qawcy26WwrflzJb6wsnhjwdneN6Wb0RNCDsqvzY+bzG453jsin4UQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jscpd/core": "4.0.4", + "reprism": "^0.0.11", + "spark-md5": "^3.0.2" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@playwright/test": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz", + "integrity": "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.59.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.5.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.2.tgz", + "integrity": "sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/sarif": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@types/sarif/-/sarif-2.1.7.tgz", + "integrity": "sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/assert-never": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.4.0.tgz", + "integrity": "sha512-5oJg84os6NMQNl27T9LnZkvvqzvAnHu03ShCnoj6bsJwS7L8AO4lf+C/XjK/nvzEqQB744moC6V128RucQd1jA==", + "dev": true, + "license": "MIT" + }, + "node_modules/babel-walk": { + "version": "3.0.0-canary-5", + "resolved": "https://registry.npmjs.org/babel-walk/-/babel-walk-3.0.0-canary-5.tgz", + "integrity": "sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.9.6" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/badgen": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/badgen/-/badgen-3.2.3.tgz", + "integrity": "sha512-svDuwkc63E/z0ky3drpUppB83s/nlgDciH9m+STwwQoWyq7yCgew1qEfJ+9axkKdNq7MskByptWUN9j1PGMwFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/blamer": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/blamer/-/blamer-1.0.7.tgz", + "integrity": "sha512-GbBStl/EVlSWkiJQBZps3H1iARBrC7vt++Jb/TTmCNu/jZ04VW7tSN1nScbFXBUy1AN+jzeL7Zep9sbQxLhXKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^4.0.0", + "which": "^2.0.2" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/character-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", + "integrity": "sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-regex": "^1.0.3" + } + }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/constantinople": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz", + "integrity": "sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.6.0", + "@babel/types": "^7.6.1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/doctypes": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", + "integrity": "sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.2.0.tgz", + "integrity": "sha512-+L0vBFYGIpSNIt/KWTpFonPrqYvgKw1eUI5Vn7mEogrQcWtWYtNQ7dNqC+px/J0idT3BAkiWrhfS7k+Tum8TUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.4", + "@eslint/config-helpers": "^0.5.4", + "@eslint/core": "^1.2.0", + "@eslint/plugin-kit": "^0.7.0", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/fs-extra": { + "version": "11.3.4", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", + "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gitignore-to-glob": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/gitignore-to-glob/-/gitignore-to-glob-0.3.0.tgz", + "integrity": "sha512-mk74BdnK7lIwDHnotHddx1wsjMOFIThpLY3cPNniJ/2fA/tlLzHnFxIdR+4sLOu5KGgQJdij4kjJ2RoUNnCNMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.4 <5 || >=6.9" + } + }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-validate": { + "version": "10.11.3", + "resolved": "https://registry.npmjs.org/html-validate/-/html-validate-10.11.3.tgz", + "integrity": "sha512-wKUq9iR6bukMgiHhs/ORThZzEbQoFiiPNN7aZfQ8dlmhttPb2sM2Ji2p+Fy5Xj1aH7QHJ1biT2SUDw7A01P2oA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/html-validate" + } + ], + "license": "MIT", + "dependencies": { + "@html-validate/stylish": "^5.0.0", + "@sidvind/better-ajv-errors": "4.0.1", + "ajv": "^8.0.0", + "glob": "^13.0.0", + "kleur": "^4.1.0", + "minimist": "^1.2.0", + "prompts": "^2.0.0", + "semver": "^7.0.0" + }, + "bin": { + "html-validate": "bin/html-validate.mjs" + }, + "engines": { + "node": "^20.19.0 || >= 22.16.0" + }, + "peerDependencies": { + "jest": "^28.1.3 || ^29.0.3 || ^30.0.0", + "jest-diff": "^28.1.3 || ^29.0.3 || ^30.0.0", + "jest-snapshot": "^28.1.3 || ^29.0.3 || ^30.0.0", + "vitest": "^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.1" + }, + "peerDependenciesMeta": { + "jest": { + "optional": true + }, + "jest-diff": { + "optional": true + }, + "jest-snapshot": { + "optional": true + }, + "vitest": { + "optional": true + } + } + }, + "node_modules/html-validate/node_modules/@sidvind/better-ajv-errors": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sidvind/better-ajv-errors/-/better-ajv-errors-4.0.1.tgz", + "integrity": "sha512-6arF1ssKxItxgitPYXafUoLmsVBA6K7m9+ZGj6hLDoBl7nWpJ33EInwQUdHTle2METeWGxgQiqSex20KZRykew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "kleur": "^4.1.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "ajv": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/html-validate/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/html-validate/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-expression": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", + "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^7.1.1", + "object-assign": "^4.1.1" + } + }, + "node_modules/is-expression/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-stringify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", + "integrity": "sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==", + "dev": true, + "license": "MIT" + }, + "node_modules/jscpd": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/jscpd/-/jscpd-4.0.8.tgz", + "integrity": "sha512-d2VNT/2Hv4dxT2/59He8Lyda4DYOxPRyRG9zBaOpTZAqJCVf2xLrBlZkT8Va6Lo9u3X2qz8Bpq4HrDi4JsrQhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jscpd/badge-reporter": "4.0.4", + "@jscpd/core": "4.0.4", + "@jscpd/finder": "4.0.4", + "@jscpd/html-reporter": "4.0.4", + "@jscpd/tokenizer": "4.0.4", + "colors": "^1.4.0", + "commander": "^5.0.0", + "fs-extra": "^11.2.0", + "gitignore-to-glob": "^0.3.0", + "jscpd-sarif-reporter": "4.0.6" + }, + "bin": { + "jscpd": "bin/jscpd" + } + }, + "node_modules/jscpd-sarif-reporter": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/jscpd-sarif-reporter/-/jscpd-sarif-reporter-4.0.6.tgz", + "integrity": "sha512-b9Sm3IPZ3+m8Lwa4gZa+4/LhDhlc/ZLEsLXKSOy1DANQ6kx0ueqZT+fUHWEdQ6m0o3+RIVIa7DmvLSojQD05ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "colors": "^1.4.0", + "fs-extra": "^11.2.0", + "node-sarif-builder": "^3.4.0" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jstransformer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", + "integrity": "sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-promise": "^2.0.0", + "promise": "^7.0.1" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/markdown-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz", + "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "repeat-string": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-sarif-builder": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/node-sarif-builder/-/node-sarif-builder-3.4.0.tgz", + "integrity": "sha512-tGnJW6OKRii9u/b2WiUViTJS+h7Apxx17qsMUjsUeNDiMMX5ZFf8F8Fcz7PAQ6omvOxHZtvDTmOYKJQwmfpjeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/sarif": "^2.1.7", + "fs-extra": "^11.1.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/playwright": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz", + "integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.59.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz", + "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "asap": "~2.0.3" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prompts/node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pug": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.4.tgz", + "integrity": "sha512-kFfq5mMzrS7+wrl5pLJzZEzemx34OQ0w4SARfhy/3yxTlhbstsudDwJzhf1hP02yHzbjoVMSXUj/Sz6RNfMyXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pug-code-gen": "^3.0.4", + "pug-filters": "^4.0.0", + "pug-lexer": "^5.0.1", + "pug-linker": "^4.0.0", + "pug-load": "^3.0.0", + "pug-parser": "^6.0.0", + "pug-runtime": "^3.0.1", + "pug-strip-comments": "^2.0.0" + } + }, + "node_modules/pug-attrs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-3.0.0.tgz", + "integrity": "sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==", + "dev": true, + "license": "MIT", + "dependencies": { + "constantinople": "^4.0.1", + "js-stringify": "^1.0.2", + "pug-runtime": "^3.0.0" + } + }, + "node_modules/pug-code-gen": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-3.0.4.tgz", + "integrity": "sha512-6okWYIKdasTyXICyEtvobmTZAVX57JkzgzIi4iRJlin8kmhG+Xry2dsus+Mun/nGCn6F2U49haHI5mkELXB14g==", + "dev": true, + "license": "MIT", + "dependencies": { + "constantinople": "^4.0.1", + "doctypes": "^1.1.0", + "js-stringify": "^1.0.2", + "pug-attrs": "^3.0.0", + "pug-error": "^2.1.0", + "pug-runtime": "^3.0.1", + "void-elements": "^3.1.0", + "with": "^7.0.0" + } + }, + "node_modules/pug-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-2.1.0.tgz", + "integrity": "sha512-lv7sU9e5Jk8IeUheHata6/UThZ7RK2jnaaNztxfPYUY+VxZyk/ePVaNZ/vwmH8WqGvDz3LrNYt/+gA55NDg6Pg==", + "dev": true, + "license": "MIT" + }, + "node_modules/pug-filters": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-4.0.0.tgz", + "integrity": "sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "constantinople": "^4.0.1", + "jstransformer": "1.0.0", + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0", + "resolve": "^1.15.1" + } + }, + "node_modules/pug-lexer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-5.0.1.tgz", + "integrity": "sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-parser": "^2.2.0", + "is-expression": "^4.0.0", + "pug-error": "^2.0.0" + } + }, + "node_modules/pug-linker": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-4.0.0.tgz", + "integrity": "sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0" + } + }, + "node_modules/pug-load": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-3.0.0.tgz", + "integrity": "sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "object-assign": "^4.1.1", + "pug-walk": "^2.0.0" + } + }, + "node_modules/pug-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-6.0.0.tgz", + "integrity": "sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pug-error": "^2.0.0", + "token-stream": "1.0.0" + } + }, + "node_modules/pug-runtime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-3.0.1.tgz", + "integrity": "sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==", + "dev": true, + "license": "MIT" + }, + "node_modules/pug-strip-comments": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz", + "integrity": "sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "pug-error": "^2.0.0" + } + }, + "node_modules/pug-walk": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz", + "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/reprism": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/reprism/-/reprism-0.0.11.tgz", + "integrity": "sha512-VsxDR5QxZo08M/3nRypNlScw5r3rKeSOPdU/QhDmu3Ai3BJxHn/qgfXGWQp/tAxUtzwYNo9W6997JZR0tPLZsA==", + "dev": true, + "license": "MIT" + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/spark-md5": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/spark-md5/-/spark-md5-3.0.2.tgz", + "integrity": "sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw==", + "dev": true, + "license": "(WTFPL OR MIT)" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/token-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", + "integrity": "sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg==", + "dev": true, + "license": "MIT" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", + "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/with": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz", + "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.9.6", + "@babel/types": "^7.9.6", + "assert-never": "^1.2.1", + "babel-walk": "3.0.0-canary-5" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/package.json b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/package.json @@ -0,0 +1,23 @@ +{ + "name": "loop-bench-amzysv_v", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "build": "tsc", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "commonjs", + "devDependencies": { + "@eslint/js": "^10.0.1", + "@playwright/test": "^1.59.1", + "@types/node": "^25.5.2", + "eslint": "^10.2.0", + "html-validate": "^10.11.3", + "jscpd": "^4.0.8", + "typescript": "^6.0.2" + } +} diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/report/jscpd-report.json b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/report/jscpd-report.json @@ -0,0 +1,217 @@ +{ + "statistics": { + "detectionDate": "2026-04-04T21:00:20.420Z", + "formats": { + "typescript": { + "sources": { + "src/tetris.ts": { + "lines": 542, + "tokens": 6509, + "sources": 1, + "clones": 2, + "duplicatedLines": 10, + "duplicatedTokens": 264, + "percentage": 1.85, + "percentageTokens": 4.06, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "total": { + "lines": 542, + "tokens": 6509, + "sources": 1, + "clones": 1, + "duplicatedLines": 5, + "duplicatedTokens": 132, + "percentage": 0.92, + "percentageTokens": 2.03, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "javascript": { + "sources": { + "dist/tetris.js": { + "lines": 472, + "tokens": 6087, + "sources": 1, + "clones": 2, + "duplicatedLines": 10, + "duplicatedTokens": 222, + "percentage": 2.12, + "percentageTokens": 3.65, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "total": { + "lines": 472, + "tokens": 6087, + "sources": 1, + "clones": 1, + "duplicatedLines": 5, + "duplicatedTokens": 111, + "percentage": 1.06, + "percentageTokens": 1.82, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "json": { + "sources": { + "tsconfig.json": { + "lines": 11, + "tokens": 68, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + }, + "package.json": { + "lines": 22, + "tokens": 139, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "total": { + "lines": 33, + "tokens": 207, + "sources": 2, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "markup": { + "sources": { + "index.html": { + "lines": 117, + "tokens": 832, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "total": { + "lines": 117, + "tokens": 832, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + } + }, + "total": { + "lines": 1164, + "tokens": 13635, + "sources": 5, + "clones": 2, + "duplicatedLines": 10, + "duplicatedTokens": 243, + "percentage": 0.86, + "percentageTokens": 1.78, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "duplicates": [ + { + "format": "typescript", + "lines": 6, + "fragment": "(matrix: number[][]): number[][] {\n const n = matrix.length;\n const result: number[][] = Array.from({ length: n }, () => Array(n).fill(0));\n for (let r = 0; r < n; r++) {\n for (let c = 0; c < n; c++) {\n result[n", + "tokens": 0, + "firstFile": { + "name": "src/tetris.ts", + "start": 157, + "end": 162, + "startLoc": { + "line": 157, + "column": 10, + "position": 2467 + }, + "endLoc": { + "line": 162, + "column": 2, + "position": 2599 + } + }, + "secondFile": { + "name": "src/tetris.ts", + "start": 146, + "end": 151, + "startLoc": { + "line": 146, + "column": 9, + "position": 2292 + }, + "endLoc": { + "line": 151, + "column": 2, + "position": 2424 + } + } + }, + { + "format": "javascript", + "lines": 6, + "fragment": "(matrix) {\n const n = matrix.length;\n const result = Array.from({ length: n }, () => Array(n).fill(0));\n for (let r = 0; r < n; r++) {\n for (let c = 0; c < n; c++) {\n result[n", + "tokens": 0, + "firstFile": { + "name": "dist/tetris.js", + "start": 125, + "end": 130, + "startLoc": { + "line": 125, + "column": 10, + "position": 2189 + }, + "endLoc": { + "line": 130, + "column": 2, + "position": 2300 + } + }, + "secondFile": { + "name": "dist/tetris.js", + "start": 115, + "end": 120, + "startLoc": { + "line": 115, + "column": 9, + "position": 2036 + }, + "endLoc": { + "line": 120, + "column": 2, + "position": 2147 + } + } + } + ] +} +\ No newline at end of file diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/src/tetris.ts b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/src/tetris.ts @@ -0,0 +1,543 @@ +// ── Types ────────────────────────────────────────────────────────────────── + +type Cell = string | null; +type Board = Cell[][]; + +interface Position { + x: number; + y: number; +} + +interface Piece { + shape: number[][]; + color: string; + pos: Position; +} + +// ── Constants ────────────────────────────────────────────────────────────── + +const COLS = 10; +const ROWS = 20; +const BLOCK_SIZE = 32; +const PREVIEW_BLOCK = 20; + +const COLORS: Record<string, string> = { + I: "#00f0f0", + O: "#f0f000", + T: "#a000f0", + S: "#00f000", + Z: "#f00000", + J: "#0000f0", + L: "#f0a000", +}; + +const SHAPES: Record<string, number[][]> = { + I: [ + [0, 0, 0, 0], + [1, 1, 1, 1], + [0, 0, 0, 0], + [0, 0, 0, 0], + ], + O: [ + [1, 1], + [1, 1], + ], + T: [ + [0, 1, 0], + [1, 1, 1], + [0, 0, 0], + ], + S: [ + [0, 1, 1], + [1, 1, 0], + [0, 0, 0], + ], + Z: [ + [1, 1, 0], + [0, 1, 1], + [0, 0, 0], + ], + J: [ + [1, 0, 0], + [1, 1, 1], + [0, 0, 0], + ], + L: [ + [0, 0, 1], + [1, 1, 1], + [0, 0, 0], + ], +}; + +const PIECE_NAMES = Object.keys(SHAPES); + +// SRS wall-kick data (non-I pieces) +const WALL_KICKS: Record<string, Position[]> = { + "0>1": [{ x: -1, y: 0 }, { x: -1, y: -1 }, { x: 0, y: 2 }, { x: -1, y: 2 }], + "1>0": [{ x: 1, y: 0 }, { x: 1, y: 1 }, { x: 0, y: -2 }, { x: 1, y: -2 }], + "1>2": [{ x: 1, y: 0 }, { x: 1, y: 1 }, { x: 0, y: -2 }, { x: 1, y: -2 }], + "2>1": [{ x: -1, y: 0 }, { x: -1, y: -1 }, { x: 0, y: 2 }, { x: -1, y: 2 }], + "2>3": [{ x: 1, y: 0 }, { x: 1, y: -1 }, { x: 0, y: 2 }, { x: 1, y: 2 }], + "3>2": [{ x: -1, y: 0 }, { x: -1, y: 1 }, { x: 0, y: -2 }, { x: -1, y: -2 }], + "3>0": [{ x: -1, y: 0 }, { x: -1, y: -1 }, { x: 0, y: 2 }, { x: -1, y: 2 }], + "0>3": [{ x: 1, y: 0 }, { x: 1, y: 1 }, { x: 0, y: -2 }, { x: 1, y: -2 }], +}; + +// SRS wall-kick data (I piece) +const WALL_KICKS_I: Record<string, Position[]> = { + "0>1": [{ x: -2, y: 0 }, { x: 1, y: 0 }, { x: -2, y: 1 }, { x: 1, y: -2 }], + "1>0": [{ x: 2, y: 0 }, { x: -1, y: 0 }, { x: 2, y: -1 }, { x: -1, y: 2 }], + "1>2": [{ x: -1, y: 0 }, { x: 2, y: 0 }, { x: -1, y: -2 }, { x: 2, y: 1 }], + "2>1": [{ x: 1, y: 0 }, { x: -2, y: 0 }, { x: 1, y: 2 }, { x: -2, y: -1 }], + "2>3": [{ x: 2, y: 0 }, { x: -1, y: 0 }, { x: 2, y: -1 }, { x: -1, y: 2 }], + "3>2": [{ x: -2, y: 0 }, { x: 1, y: 0 }, { x: -2, y: 1 }, { x: 1, y: -2 }], + "3>0": [{ x: 1, y: 0 }, { x: -2, y: 0 }, { x: 1, y: 2 }, { x: -2, y: -1 }], + "0>3": [{ x: -1, y: 0 }, { x: 2, y: 0 }, { x: -1, y: -2 }, { x: 2, y: 1 }], +}; + +const POINTS = [0, 100, 300, 500, 800]; + +// ── Game State ───────────────────────────────────────────────────────────── + +let board: Board; +let current: Piece; +let currentType: string; +let currentRotation: number; +let next: { type: string; shape: number[][]; color: string }; +let score: number; +let lines: number; +let level: number; +let gameOver: boolean; +let paused: boolean; +let dropInterval: number; +let lastDrop: number; +let animationId: number; +let lockDelay: number; +let lockTimer: number; +let softDropping: boolean; + +// ── Canvas Setup ─────────────────────────────────────────────────────────── + +const canvas = document.getElementById("game-canvas") as HTMLCanvasElement; +const ctx = canvas.getContext("2d")!; +canvas.width = COLS * BLOCK_SIZE; +canvas.height = ROWS * BLOCK_SIZE; + +const previewCanvas = document.getElementById("preview-canvas") as HTMLCanvasElement; +const previewCtx = previewCanvas.getContext("2d")!; +previewCanvas.width = 6 * PREVIEW_BLOCK; +previewCanvas.height = 6 * PREVIEW_BLOCK; + +const scoreEl = document.getElementById("score")!; +const linesEl = document.getElementById("lines")!; +const levelEl = document.getElementById("level")!; + +// ── Helper Functions ─────────────────────────────────────────────────────── + +function createBoard(): Board { + return Array.from({ length: ROWS }, () => Array(COLS).fill(null)); +} + +function randomPiece(): { type: string; shape: number[][]; color: string } { + const type = PIECE_NAMES[Math.floor(Math.random() * PIECE_NAMES.length)]; + return { type, shape: SHAPES[type].map((r) => [...r]), color: COLORS[type] }; +} + +function rotateCW(matrix: number[][]): number[][] { + const n = matrix.length; + const result: number[][] = Array.from({ length: n }, () => Array(n).fill(0)); + for (let r = 0; r < n; r++) { + for (let c = 0; c < n; c++) { + result[c][n - 1 - r] = matrix[r][c]; + } + } + return result; +} + +function rotateCCW(matrix: number[][]): number[][] { + const n = matrix.length; + const result: number[][] = Array.from({ length: n }, () => Array(n).fill(0)); + for (let r = 0; r < n; r++) { + for (let c = 0; c < n; c++) { + result[n - 1 - c][r] = matrix[r][c]; + } + } + return result; +} + +function collides(shape: number[][], pos: Position): boolean { + for (let r = 0; r < shape.length; r++) { + for (let c = 0; c < shape[r].length; c++) { + if (shape[r][c]) { + const nx = pos.x + c; + const ny = pos.y + r; + if (nx < 0 || nx >= COLS || ny >= ROWS) return true; + if (ny >= 0 && board[ny][nx] !== null) return true; + } + } + } + return false; +} + +function lock(): void { + for (let r = 0; r < current.shape.length; r++) { + for (let c = 0; c < current.shape[r].length; c++) { + if (current.shape[r][c]) { + const ny = current.pos.y + r; + const nx = current.pos.x + c; + if (ny < 0) { + gameOver = true; + return; + } + board[ny][nx] = current.color; + } + } + } + clearLines(); + spawn(); +} + +function clearLines(): void { + let cleared = 0; + for (let r = ROWS - 1; r >= 0; r--) { + if (board[r].every((cell) => cell !== null)) { + board.splice(r, 1); + board.unshift(Array(COLS).fill(null)); + cleared++; + r++; // recheck this row + } + } + if (cleared > 0) { + lines += cleared; + score += POINTS[cleared] * (level + 1); + const newLevel = Math.floor(lines / 10); + if (newLevel > level) { + level = newLevel; + dropInterval = Math.max(50, 1000 - level * 80); + } + updateHUD(); + } +} + +function spawn(): void { + currentType = next.type; + currentRotation = 0; + current = { + shape: next.shape, + color: next.color, + pos: { x: Math.floor((COLS - next.shape[0].length) / 2), y: -1 }, + }; + next = randomPiece(); + + // If spawn position collides, game over + if (collides(current.shape, current.pos)) { + // Try one row up + current.pos.y = -2; + if (collides(current.shape, current.pos)) { + gameOver = true; + } + } + lockTimer = 0; +} + +function ghostY(): number { + let gy = current.pos.y; + while (!collides(current.shape, { x: current.pos.x, y: gy + 1 })) { + gy++; + } + return gy; +} + +function tryRotate(direction: 1 | -1): void { + const oldShape = current.shape; + const oldRotation = currentRotation; + const newShape = direction === 1 ? rotateCW(oldShape) : rotateCCW(oldShape); + const newRotation = (oldRotation + direction + 4) % 4; + + // Try basic rotation first + if (!collides(newShape, current.pos)) { + current.shape = newShape; + currentRotation = newRotation; + resetLockTimer(); + return; + } + + // Wall kicks + const kickKey = `${oldRotation}>${newRotation}`; + const kicks = currentType === "I" ? WALL_KICKS_I[kickKey] : WALL_KICKS[kickKey]; + if (kicks) { + for (const kick of kicks) { + const testPos = { x: current.pos.x + kick.x, y: current.pos.y - kick.y }; + if (!collides(newShape, testPos)) { + current.shape = newShape; + current.pos = testPos; + currentRotation = newRotation; + resetLockTimer(); + return; + } + } + } +} + +function resetLockTimer(): void { + if (collides(current.shape, { x: current.pos.x, y: current.pos.y + 1 })) { + lockTimer = performance.now(); + } +} + +// ── Drawing ──────────────────────────────────────────────────────────────── + +function drawBlock( + context: CanvasRenderingContext2D, + x: number, + y: number, + color: string, + size: number, + alpha: number = 1 +): void { + context.globalAlpha = alpha; + context.fillStyle = color; + context.fillRect(x * size, y * size, size, size); + + // Highlight + context.fillStyle = "rgba(255,255,255,0.3)"; + context.fillRect(x * size, y * size, size, 2); + context.fillRect(x * size, y * size, 2, size); + + // Shadow + context.fillStyle = "rgba(0,0,0,0.3)"; + context.fillRect(x * size + size - 2, y * size, 2, size); + context.fillRect(x * size, y * size + size - 2, size, 2); + + // Outline + context.strokeStyle = "rgba(0,0,0,0.5)"; + context.strokeRect(x * size, y * size, size, size); + context.globalAlpha = 1; +} + +function drawBoard(): void { + // Background + ctx.fillStyle = "#111"; + ctx.fillRect(0, 0, canvas.width, canvas.height); + + // Grid lines + ctx.strokeStyle = "rgba(255,255,255,0.04)"; + ctx.lineWidth = 1; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + ctx.strokeRect(c * BLOCK_SIZE, r * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE); + } + } + + // Locked cells + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (board[r][c]) { + drawBlock(ctx, c, r, board[r][c]!, BLOCK_SIZE); + } + } + } + + // Ghost piece + if (!gameOver && !paused) { + const gy = ghostY(); + for (let r = 0; r < current.shape.length; r++) { + for (let c = 0; c < current.shape[r].length; c++) { + if (current.shape[r][c]) { + const py = gy + r; + const px = current.pos.x + c; + if (py >= 0) { + drawBlock(ctx, px, py, current.color, BLOCK_SIZE, 0.25); + } + } + } + } + } + + // Current piece + if (!gameOver && !paused) { + for (let r = 0; r < current.shape.length; r++) { + for (let c = 0; c < current.shape[r].length; c++) { + if (current.shape[r][c]) { + const py = current.pos.y + r; + const px = current.pos.x + c; + if (py >= 0) { + drawBlock(ctx, px, py, current.color, BLOCK_SIZE); + } + } + } + } + } + + // Overlay for pause / game over + if (paused && !gameOver) { + ctx.fillStyle = "rgba(0,0,0,0.6)"; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.fillStyle = "#fff"; + ctx.font = "bold 28px monospace"; + ctx.textAlign = "center"; + ctx.fillText("PAUSED", canvas.width / 2, canvas.height / 2); + } + if (gameOver) { + ctx.fillStyle = "rgba(0,0,0,0.7)"; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.fillStyle = "#f44"; + ctx.font = "bold 32px monospace"; + ctx.textAlign = "center"; + ctx.fillText("GAME OVER", canvas.width / 2, canvas.height / 2 - 20); + ctx.fillStyle = "#fff"; + ctx.font = "18px monospace"; + ctx.fillText("Press R to restart", canvas.width / 2, canvas.height / 2 + 20); + } +} + +function drawPreview(): void { + previewCtx.fillStyle = "#1a1a2e"; + previewCtx.fillRect(0, 0, previewCanvas.width, previewCanvas.height); + + const shape = next.shape; + const offsetX = (6 - shape[0].length) / 2; + const offsetY = (6 - shape.length) / 2; + for (let r = 0; r < shape.length; r++) { + for (let c = 0; c < shape[r].length; c++) { + if (shape[r][c]) { + drawBlock(previewCtx, offsetX + c, offsetY + r, next.color, PREVIEW_BLOCK); + } + } + } +} + +function updateHUD(): void { + scoreEl.textContent = score.toString(); + linesEl.textContent = lines.toString(); + levelEl.textContent = level.toString(); +} + +// ── Game Loop ────────────────────────────────────────────────────────────── + +function gameLoop(now: number): void { + animationId = requestAnimationFrame(gameLoop); + + if (gameOver || paused) { + drawBoard(); + drawPreview(); + return; + } + + // Gravity + const effectiveInterval = softDropping ? Math.max(30, dropInterval / 15) : dropInterval; + if (now - lastDrop > effectiveInterval) { + if (!collides(current.shape, { x: current.pos.x, y: current.pos.y + 1 })) { + current.pos.y++; + if (softDropping) score += 1; + lastDrop = now; + lockTimer = 0; + } else { + // Piece is resting on something — start lock delay + if (lockTimer === 0) { + lockTimer = now; + } else if (now - lockTimer > lockDelay) { + lock(); + lastDrop = now; + } + } + } + + drawBoard(); + drawPreview(); + updateHUD(); +} + +// ── Input ────────────────────────────────────────────────────────────────── + +document.addEventListener("keydown", (e: KeyboardEvent) => { + if (gameOver) { + if (e.key === "r" || e.key === "R") { + init(); + } + return; + } + + if (e.key === "p" || e.key === "P" || e.key === "Escape") { + paused = !paused; + if (!paused) lastDrop = performance.now(); + return; + } + + if (paused) return; + + switch (e.key) { + case "ArrowLeft": + case "a": + if (!collides(current.shape, { x: current.pos.x - 1, y: current.pos.y })) { + current.pos.x--; + resetLockTimer(); + } + e.preventDefault(); + break; + case "ArrowRight": + case "d": + if (!collides(current.shape, { x: current.pos.x + 1, y: current.pos.y })) { + current.pos.x++; + resetLockTimer(); + } + e.preventDefault(); + break; + case "ArrowDown": + case "s": + softDropping = true; + e.preventDefault(); + break; + case "ArrowUp": + case "w": + tryRotate(1); + e.preventDefault(); + break; + case "z": + case "Z": + tryRotate(-1); + e.preventDefault(); + break; + case " ": + // Hard drop + while (!collides(current.shape, { x: current.pos.x, y: current.pos.y + 1 })) { + current.pos.y++; + score += 2; + } + lock(); + lastDrop = performance.now(); + e.preventDefault(); + break; + } +}); + +document.addEventListener("keyup", (e: KeyboardEvent) => { + if (e.key === "ArrowDown" || e.key === "s") { + softDropping = false; + } +}); + +// ── Initialization ───────────────────────────────────────────────────────── + +function init(): void { + board = createBoard(); + score = 0; + lines = 0; + level = 0; + gameOver = false; + paused = false; + dropInterval = 1000; + lastDrop = performance.now(); + lockDelay = 500; + lockTimer = 0; + softDropping = false; + + next = randomPiece(); + spawn(); + updateHUD(); + + if (animationId) cancelAnimationFrame(animationId); + animationId = requestAnimationFrame(gameLoop); +} + +init(); diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/tsconfig.json b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ES2020", + "strict": true, + "outDir": "./dist", + "rootDir": "./src", + "sourceMap": true, + "esModuleInterop": true + }, + "include": ["src/**/*"] +} diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/index.html b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/index.html @@ -0,0 +1,77 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <title>Tetris</title> + <link rel="stylesheet" href="style.css" /> +</head> +<body> + + <h1>TETRIS</h1> + + <div class="game-wrapper"> + + <!-- ── Main board ── --> + <div class="board-container"> + <canvas id="game-canvas" + width="300" + height="600" + aria-label="Tetris game board"></canvas> + </div> + + <!-- ── Side panel ── --> + <div class="side-panel"> + + <!-- Next piece --> + <div class="panel-box"> + <h2>Next</h2> + <canvas id="next-canvas" width="125" height="100"></canvas> + </div> + + <!-- Score --> + <div class="panel-box stat-score"> + <h2>Score</h2> + <div class="stat-value" id="score">0</div> + </div> + + <!-- Lines --> + <div class="panel-box stat-lines"> + <h2>Lines</h2> + <div class="stat-value" id="lines">0</div> + </div> + + <!-- Level --> + <div class="panel-box stat-level"> + <h2>Level</h2> + <div class="stat-value" id="level">1</div> + </div> + + <!-- Buttons --> + <div class="panel-box"> + <div class="btn-row"> + <button id="btn-pause">Pause</button> + <button id="btn-restart">Restart</button> + </div> + </div> + + <!-- Controls --> + <div class="panel-box"> + <h2>Controls</h2> + <ul class="controls-list"> + <li><kbd>◀▶</kbd> Move</li> + <li><kbd>▼</kbd> Soft drop</li> + <li><kbd>Space</kbd> Hard drop</li> + <li><kbd>▲</kbd> Rotate CW</li> + <li><kbd>Z</kbd> Rotate CCW</li> + <li><kbd>P</kbd> Pause</li> + <li><kbd>R</kbd> Restart</li> + </ul> + </div> + + </div><!-- /side-panel --> + </div><!-- /game-wrapper --> + + <script src="tetris.js"></script> +</body> +</html> diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/package-lock.json b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/package-lock.json @@ -0,0 +1,2583 @@ +{ + "name": "loop-bench-6vr8i9xe", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "loop-bench-6vr8i9xe", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "@eslint/js": "^10.0.1", + "@playwright/test": "^1.59.1", + "@types/node": "^25.5.2", + "eslint": "^10.2.0", + "html-validate": "^10.11.3", + "jscpd": "^4.0.8", + "typescript": "^6.0.2" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.4", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.4.tgz", + "integrity": "sha512-lf19F24LSMfF8weXvW5QEtnLqW70u7kgit5e9PSx0MsHAFclGd1T9ynvWEMDT1w5J4Qt54tomGeAhdoAku1Xow==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.4", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.4.tgz", + "integrity": "sha512-jJhqiY3wPMlWWO3370M86CPJ7pt8GmEwSLglMfQhjXal07RCvhmU0as4IuUEW5SJeunfItiEetHmSxCCe9lDBg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.0.tgz", + "integrity": "sha512-8FTGbNzTvmSlc4cZBaShkC6YvFMG0riksYWRFKXztqVdXaQbcZLXlFbSpC05s70sGEsXAw0qwhx69JiW7hQS7A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.4.tgz", + "integrity": "sha512-55lO/7+Yp0ISKRP0PsPtNTeNGapXaO085aELZmWCVc5SH3jfrqpuU6YgOdIxMS99ZHkQN1cXKE+cdIqwww9ptw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.0.tgz", + "integrity": "sha512-ejvBr8MQCbVsWNZnCwDXjUKq40MDmHalq7cJ6e9s/qzTUFIIo/afzt1Vui9T97FM/V/pN4YsFVoed5NIa96RDg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@html-validate/stylish": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@html-validate/stylish/-/stylish-5.0.0.tgz", + "integrity": "sha512-xjhRV9k1mWfgsOcpYlwsjUOFy3w3EnCDrqUrEw+DWdvOStMK59ts2H7GLKWZtmLI5m6Npp3qMSNReafQy9K2sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^4.0.0" + }, + "engines": { + "node": "^20.11 || >= 22.16" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jscpd/badge-reporter": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@jscpd/badge-reporter/-/badge-reporter-4.0.4.tgz", + "integrity": "sha512-I9b4MmLXPM2vo0SxSUWnNGKcA4PjQlD3GzXvFK60z43cN/EIdLbOq3FVwCL+dg2obUqGXKIzAm7EsDFTg0D+mQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "badgen": "^3.2.3", + "colors": "^1.4.0", + "fs-extra": "^11.2.0" + } + }, + "node_modules/@jscpd/core": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@jscpd/core/-/core-4.0.4.tgz", + "integrity": "sha512-QGMT3iXEX1fI6lgjPH+x8eyJwhwr2KkpSF5uBpjC0Z5Xloj0yFTFLtwJT+RhxP/Ob4WYrtx2jvpKB269oIwgMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.1" + } + }, + "node_modules/@jscpd/finder": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@jscpd/finder/-/finder-4.0.4.tgz", + "integrity": "sha512-qVUWY7Nzuvfd5OIk+n7/5CM98LmFroLqblRXAI2gDABwZrc7qS+WH2SNr0qoUq0f4OqwM+piiwKvwL/VDNn/Cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jscpd/core": "4.0.4", + "@jscpd/tokenizer": "4.0.4", + "blamer": "^1.0.6", + "bytes": "^3.1.2", + "cli-table3": "^0.6.5", + "colors": "^1.4.0", + "fast-glob": "^3.3.2", + "fs-extra": "^11.2.0", + "markdown-table": "^2.0.0", + "pug": "^3.0.3" + } + }, + "node_modules/@jscpd/html-reporter": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@jscpd/html-reporter/-/html-reporter-4.0.4.tgz", + "integrity": "sha512-YiepyeYkeH74Kx59PJRdUdonznct0wHPFkf6FLQN+mCBoy6leAWCcOfHtcexnp+UsBFDlItG5nRdKrDSxSH+Kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "colors": "1.4.0", + "fs-extra": "^11.2.0", + "pug": "^3.0.3" + } + }, + "node_modules/@jscpd/tokenizer": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@jscpd/tokenizer/-/tokenizer-4.0.4.tgz", + "integrity": "sha512-xxYYY/qaLah/FlwogEbGIxx9CjDO+G9E6qawcy26WwrflzJb6wsnhjwdneN6Wb0RNCDsqvzY+bzG453jsin4UQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jscpd/core": "4.0.4", + "reprism": "^0.0.11", + "spark-md5": "^3.0.2" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@playwright/test": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz", + "integrity": "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.59.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.5.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.2.tgz", + "integrity": "sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/sarif": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@types/sarif/-/sarif-2.1.7.tgz", + "integrity": "sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/assert-never": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.4.0.tgz", + "integrity": "sha512-5oJg84os6NMQNl27T9LnZkvvqzvAnHu03ShCnoj6bsJwS7L8AO4lf+C/XjK/nvzEqQB744moC6V128RucQd1jA==", + "dev": true, + "license": "MIT" + }, + "node_modules/babel-walk": { + "version": "3.0.0-canary-5", + "resolved": "https://registry.npmjs.org/babel-walk/-/babel-walk-3.0.0-canary-5.tgz", + "integrity": "sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.9.6" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/badgen": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/badgen/-/badgen-3.2.3.tgz", + "integrity": "sha512-svDuwkc63E/z0ky3drpUppB83s/nlgDciH9m+STwwQoWyq7yCgew1qEfJ+9axkKdNq7MskByptWUN9j1PGMwFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/blamer": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/blamer/-/blamer-1.0.7.tgz", + "integrity": "sha512-GbBStl/EVlSWkiJQBZps3H1iARBrC7vt++Jb/TTmCNu/jZ04VW7tSN1nScbFXBUy1AN+jzeL7Zep9sbQxLhXKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^4.0.0", + "which": "^2.0.2" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/character-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", + "integrity": "sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-regex": "^1.0.3" + } + }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/constantinople": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz", + "integrity": "sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.6.0", + "@babel/types": "^7.6.1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/doctypes": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", + "integrity": "sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.2.0.tgz", + "integrity": "sha512-+L0vBFYGIpSNIt/KWTpFonPrqYvgKw1eUI5Vn7mEogrQcWtWYtNQ7dNqC+px/J0idT3BAkiWrhfS7k+Tum8TUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.4", + "@eslint/config-helpers": "^0.5.4", + "@eslint/core": "^1.2.0", + "@eslint/plugin-kit": "^0.7.0", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/fs-extra": { + "version": "11.3.4", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", + "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gitignore-to-glob": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/gitignore-to-glob/-/gitignore-to-glob-0.3.0.tgz", + "integrity": "sha512-mk74BdnK7lIwDHnotHddx1wsjMOFIThpLY3cPNniJ/2fA/tlLzHnFxIdR+4sLOu5KGgQJdij4kjJ2RoUNnCNMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.4 <5 || >=6.9" + } + }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-validate": { + "version": "10.11.3", + "resolved": "https://registry.npmjs.org/html-validate/-/html-validate-10.11.3.tgz", + "integrity": "sha512-wKUq9iR6bukMgiHhs/ORThZzEbQoFiiPNN7aZfQ8dlmhttPb2sM2Ji2p+Fy5Xj1aH7QHJ1biT2SUDw7A01P2oA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/html-validate" + } + ], + "license": "MIT", + "dependencies": { + "@html-validate/stylish": "^5.0.0", + "@sidvind/better-ajv-errors": "4.0.1", + "ajv": "^8.0.0", + "glob": "^13.0.0", + "kleur": "^4.1.0", + "minimist": "^1.2.0", + "prompts": "^2.0.0", + "semver": "^7.0.0" + }, + "bin": { + "html-validate": "bin/html-validate.mjs" + }, + "engines": { + "node": "^20.19.0 || >= 22.16.0" + }, + "peerDependencies": { + "jest": "^28.1.3 || ^29.0.3 || ^30.0.0", + "jest-diff": "^28.1.3 || ^29.0.3 || ^30.0.0", + "jest-snapshot": "^28.1.3 || ^29.0.3 || ^30.0.0", + "vitest": "^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.1" + }, + "peerDependenciesMeta": { + "jest": { + "optional": true + }, + "jest-diff": { + "optional": true + }, + "jest-snapshot": { + "optional": true + }, + "vitest": { + "optional": true + } + } + }, + "node_modules/html-validate/node_modules/@sidvind/better-ajv-errors": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sidvind/better-ajv-errors/-/better-ajv-errors-4.0.1.tgz", + "integrity": "sha512-6arF1ssKxItxgitPYXafUoLmsVBA6K7m9+ZGj6hLDoBl7nWpJ33EInwQUdHTle2METeWGxgQiqSex20KZRykew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "kleur": "^4.1.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "ajv": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/html-validate/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/html-validate/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-expression": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", + "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^7.1.1", + "object-assign": "^4.1.1" + } + }, + "node_modules/is-expression/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-stringify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", + "integrity": "sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==", + "dev": true, + "license": "MIT" + }, + "node_modules/jscpd": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/jscpd/-/jscpd-4.0.8.tgz", + "integrity": "sha512-d2VNT/2Hv4dxT2/59He8Lyda4DYOxPRyRG9zBaOpTZAqJCVf2xLrBlZkT8Va6Lo9u3X2qz8Bpq4HrDi4JsrQhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jscpd/badge-reporter": "4.0.4", + "@jscpd/core": "4.0.4", + "@jscpd/finder": "4.0.4", + "@jscpd/html-reporter": "4.0.4", + "@jscpd/tokenizer": "4.0.4", + "colors": "^1.4.0", + "commander": "^5.0.0", + "fs-extra": "^11.2.0", + "gitignore-to-glob": "^0.3.0", + "jscpd-sarif-reporter": "4.0.6" + }, + "bin": { + "jscpd": "bin/jscpd" + } + }, + "node_modules/jscpd-sarif-reporter": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/jscpd-sarif-reporter/-/jscpd-sarif-reporter-4.0.6.tgz", + "integrity": "sha512-b9Sm3IPZ3+m8Lwa4gZa+4/LhDhlc/ZLEsLXKSOy1DANQ6kx0ueqZT+fUHWEdQ6m0o3+RIVIa7DmvLSojQD05ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "colors": "^1.4.0", + "fs-extra": "^11.2.0", + "node-sarif-builder": "^3.4.0" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jstransformer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", + "integrity": "sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-promise": "^2.0.0", + "promise": "^7.0.1" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/markdown-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz", + "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "repeat-string": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-sarif-builder": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/node-sarif-builder/-/node-sarif-builder-3.4.0.tgz", + "integrity": "sha512-tGnJW6OKRii9u/b2WiUViTJS+h7Apxx17qsMUjsUeNDiMMX5ZFf8F8Fcz7PAQ6omvOxHZtvDTmOYKJQwmfpjeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/sarif": "^2.1.7", + "fs-extra": "^11.1.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/playwright": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz", + "integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.59.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz", + "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "asap": "~2.0.3" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prompts/node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pug": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.4.tgz", + "integrity": "sha512-kFfq5mMzrS7+wrl5pLJzZEzemx34OQ0w4SARfhy/3yxTlhbstsudDwJzhf1hP02yHzbjoVMSXUj/Sz6RNfMyXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pug-code-gen": "^3.0.4", + "pug-filters": "^4.0.0", + "pug-lexer": "^5.0.1", + "pug-linker": "^4.0.0", + "pug-load": "^3.0.0", + "pug-parser": "^6.0.0", + "pug-runtime": "^3.0.1", + "pug-strip-comments": "^2.0.0" + } + }, + "node_modules/pug-attrs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-3.0.0.tgz", + "integrity": "sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==", + "dev": true, + "license": "MIT", + "dependencies": { + "constantinople": "^4.0.1", + "js-stringify": "^1.0.2", + "pug-runtime": "^3.0.0" + } + }, + "node_modules/pug-code-gen": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-3.0.4.tgz", + "integrity": "sha512-6okWYIKdasTyXICyEtvobmTZAVX57JkzgzIi4iRJlin8kmhG+Xry2dsus+Mun/nGCn6F2U49haHI5mkELXB14g==", + "dev": true, + "license": "MIT", + "dependencies": { + "constantinople": "^4.0.1", + "doctypes": "^1.1.0", + "js-stringify": "^1.0.2", + "pug-attrs": "^3.0.0", + "pug-error": "^2.1.0", + "pug-runtime": "^3.0.1", + "void-elements": "^3.1.0", + "with": "^7.0.0" + } + }, + "node_modules/pug-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-2.1.0.tgz", + "integrity": "sha512-lv7sU9e5Jk8IeUheHata6/UThZ7RK2jnaaNztxfPYUY+VxZyk/ePVaNZ/vwmH8WqGvDz3LrNYt/+gA55NDg6Pg==", + "dev": true, + "license": "MIT" + }, + "node_modules/pug-filters": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-4.0.0.tgz", + "integrity": "sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "constantinople": "^4.0.1", + "jstransformer": "1.0.0", + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0", + "resolve": "^1.15.1" + } + }, + "node_modules/pug-lexer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-5.0.1.tgz", + "integrity": "sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-parser": "^2.2.0", + "is-expression": "^4.0.0", + "pug-error": "^2.0.0" + } + }, + "node_modules/pug-linker": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-4.0.0.tgz", + "integrity": "sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0" + } + }, + "node_modules/pug-load": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-3.0.0.tgz", + "integrity": "sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "object-assign": "^4.1.1", + "pug-walk": "^2.0.0" + } + }, + "node_modules/pug-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-6.0.0.tgz", + "integrity": "sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pug-error": "^2.0.0", + "token-stream": "1.0.0" + } + }, + "node_modules/pug-runtime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-3.0.1.tgz", + "integrity": "sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==", + "dev": true, + "license": "MIT" + }, + "node_modules/pug-strip-comments": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz", + "integrity": "sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "pug-error": "^2.0.0" + } + }, + "node_modules/pug-walk": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz", + "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/reprism": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/reprism/-/reprism-0.0.11.tgz", + "integrity": "sha512-VsxDR5QxZo08M/3nRypNlScw5r3rKeSOPdU/QhDmu3Ai3BJxHn/qgfXGWQp/tAxUtzwYNo9W6997JZR0tPLZsA==", + "dev": true, + "license": "MIT" + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/spark-md5": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/spark-md5/-/spark-md5-3.0.2.tgz", + "integrity": "sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw==", + "dev": true, + "license": "(WTFPL OR MIT)" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/token-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", + "integrity": "sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg==", + "dev": true, + "license": "MIT" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", + "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/with": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz", + "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.9.6", + "@babel/types": "^7.9.6", + "assert-never": "^1.2.1", + "babel-walk": "3.0.0-canary-5" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/package.json b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/package.json @@ -0,0 +1,22 @@ +{ + "name": "loop-bench-6vr8i9xe", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "commonjs", + "devDependencies": { + "@eslint/js": "^10.0.1", + "@playwright/test": "^1.59.1", + "@types/node": "^25.5.2", + "eslint": "^10.2.0", + "html-validate": "^10.11.3", + "jscpd": "^4.0.8", + "typescript": "^6.0.2" + } +} diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/report/jscpd-report.json b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/report/jscpd-report.json @@ -0,0 +1,172 @@ +{ + "statistics": { + "detectionDate": "2026-04-04T21:04:39.218Z", + "formats": { + "json": { + "sources": { + "tsconfig.json": { + "lines": 13, + "tokens": 90, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + }, + "package.json": { + "lines": 21, + "tokens": 132, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "total": { + "lines": 34, + "tokens": 222, + "sources": 2, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "typescript": { + "sources": { + "tetris.ts": { + "lines": 692, + "tokens": 7216, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "total": { + "lines": 692, + "tokens": 7216, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "javascript": { + "sources": { + "tetris.js": { + "lines": 610, + "tokens": 6842, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "total": { + "lines": 610, + "tokens": 6842, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "css": { + "sources": { + "style.css": { + "lines": 160, + "tokens": 1020, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "total": { + "lines": 160, + "tokens": 1020, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "markup": { + "sources": { + "index.html": { + "lines": 76, + "tokens": 647, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "total": { + "lines": 76, + "tokens": 647, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + } + }, + "total": { + "lines": 1572, + "tokens": 15947, + "sources": 6, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "duplicates": [] +} +\ No newline at end of file diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/style.css b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/style.css @@ -0,0 +1,161 @@ +/* ── Reset & base ── */ +*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } + +body { + background: #0d0d1a; + color: #e0e0ff; + font-family: 'Courier New', Courier, monospace; + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + user-select: none; +} + +/* ── Title ── */ +h1 { + font-size: 2.4rem; + letter-spacing: 0.25em; + text-transform: uppercase; + background: linear-gradient(90deg, #00f0f0, #a000f0, #f0a000, #00f000, #f00000); + background-size: 300% 100%; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + animation: rainbow 4s linear infinite; + margin-bottom: 20px; + text-shadow: none; +} + +@keyframes rainbow { + 0% { background-position: 0% 50%; } + 100% { background-position: 300% 50%; } +} + +/* ── Main layout ── */ +.game-wrapper { + display: flex; + gap: 24px; + align-items: flex-start; +} + +/* ── Board ── */ +.board-container { + position: relative; +} + +#game-canvas { + display: block; + border: 2px solid rgba(255,255,255,0.15); + border-radius: 4px; + box-shadow: + 0 0 0 1px rgba(255,255,255,0.05), + 0 0 20px rgba(100,100,255,0.25), + 0 0 60px rgba(80, 80,200,0.10); +} + +/* ── Side panel ── */ +.side-panel { + display: flex; + flex-direction: column; + gap: 18px; + width: 150px; +} + +.panel-box { + background: rgba(255,255,255,0.04); + border: 1px solid rgba(255,255,255,0.1); + border-radius: 8px; + padding: 12px 14px; +} + +.panel-box h2 { + font-size: 0.65rem; + letter-spacing: 0.18em; + text-transform: uppercase; + color: rgba(200,200,255,0.5); + margin-bottom: 8px; +} + +/* ── Next piece canvas ── */ +#next-canvas { + display: block; + border-radius: 4px; + margin: 0 auto; +} + +/* ── Stat values ── */ +.stat-value { + font-size: 1.5rem; + font-weight: bold; + color: #fff; + letter-spacing: 0.05em; + line-height: 1.1; +} + +/* ── Controls list ── */ +.controls-list { + list-style: none; + display: flex; + flex-direction: column; + gap: 5px; +} + +.controls-list li { + display: flex; + align-items: center; + gap: 8px; + font-size: 0.72rem; + color: rgba(200,200,255,0.7); +} + +kbd { + display: inline-block; + background: rgba(255,255,255,0.1); + border: 1px solid rgba(255,255,255,0.25); + border-radius: 4px; + padding: 1px 5px; + font-family: inherit; + font-size: 0.7rem; + color: #fff; + min-width: 22px; + text-align: center; + flex-shrink: 0; +} + +/* ── Buttons ── */ +.btn-row { + display: flex; + gap: 8px; +} + +button { + flex: 1; + background: rgba(255,255,255,0.07); + border: 1px solid rgba(255,255,255,0.18); + border-radius: 6px; + color: #e0e0ff; + font-family: inherit; + font-size: 0.78rem; + letter-spacing: 0.08em; + padding: 7px 4px; + cursor: pointer; + transition: background 0.15s, border-color 0.15s; + text-transform: uppercase; +} + +button:hover { background: rgba(255,255,255,0.14); border-color: rgba(255,255,255,0.35); } +button:active { background: rgba(255,255,255,0.22); } + +/* ── Level colour accent ── */ +.stat-level .stat-value { color: #f0a000; } +.stat-lines .stat-value { color: #00f0f0; } +.stat-score .stat-value { color: #a000f0; } + +/* ── Responsive shrink for small screens ── */ +@media (max-width: 600px) { + h1 { font-size: 1.6rem; } + .game-wrapper { gap: 14px; } + .side-panel { width: 120px; } +} diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/tetris.js b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/tetris.js @@ -0,0 +1,611 @@ +"use strict"; +// ───────────────────────────────────────────── +// Tetris — TypeScript browser implementation +// ───────────────────────────────────────────── +const COLS = 10; +const ROWS = 20; +const CELL = 30; // px per cell on main board +const NEXT_CELL = 25; // px per cell in next-piece preview +// ── Colours ──────────────────────────────────── +const PIECE_COLORS = { + I: '#00f0f0', + O: '#f0f000', + T: '#a000f0', + S: '#00e000', + Z: '#f00000', + J: '#0050f0', + L: '#f0a000', +}; +// ── SRS shape tables ─────────────────────────── +// Each entry is an array of rotation states. +// 0 = spawn orientation, then CW 1, 180, CCW. +// 1 inside the matrix means a filled cell. +const SHAPES = { + I: [ + [[0, 0, 0, 0], + [1, 1, 1, 1], + [0, 0, 0, 0], + [0, 0, 0, 0]], + [[0, 0, 1, 0], + [0, 0, 1, 0], + [0, 0, 1, 0], + [0, 0, 1, 0]], + [[0, 0, 0, 0], + [0, 0, 0, 0], + [1, 1, 1, 1], + [0, 0, 0, 0]], + [[0, 1, 0, 0], + [0, 1, 0, 0], + [0, 1, 0, 0], + [0, 1, 0, 0]], + ], + O: [ + [[0, 1, 1, 0], + [0, 1, 1, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]], + ], + T: [ + [[0, 1, 0], + [1, 1, 1], + [0, 0, 0]], + [[0, 1, 0], + [0, 1, 1], + [0, 1, 0]], + [[0, 0, 0], + [1, 1, 1], + [0, 1, 0]], + [[0, 1, 0], + [1, 1, 0], + [0, 1, 0]], + ], + S: [ + [[0, 1, 1], + [1, 1, 0], + [0, 0, 0]], + [[0, 1, 0], + [0, 1, 1], + [0, 0, 1]], + [[0, 0, 0], + [0, 1, 1], + [1, 1, 0]], + [[1, 0, 0], + [1, 1, 0], + [0, 1, 0]], + ], + Z: [ + [[1, 1, 0], + [0, 1, 1], + [0, 0, 0]], + [[0, 0, 1], + [0, 1, 1], + [0, 1, 0]], + [[0, 0, 0], + [1, 1, 0], + [0, 1, 1]], + [[0, 1, 0], + [1, 1, 0], + [1, 0, 0]], + ], + J: [ + [[1, 0, 0], + [1, 1, 1], + [0, 0, 0]], + [[0, 1, 1], + [0, 1, 0], + [0, 1, 0]], + [[0, 0, 0], + [1, 1, 1], + [0, 0, 1]], + [[0, 1, 0], + [0, 1, 0], + [1, 1, 0]], + ], + L: [ + [[0, 0, 1], + [1, 1, 1], + [0, 0, 0]], + [[0, 1, 0], + [0, 1, 0], + [0, 1, 1]], + [[0, 0, 0], + [1, 1, 1], + [1, 0, 0]], + [[1, 1, 0], + [0, 1, 0], + [0, 1, 0]], + ], +}; +const KICKS_JLSTZ = [ + // state 0 → 1 (CW) + [[0, 0], [-1, 0], [-1, -1], [0, 2], [-1, 2]], + // state 1 → 2 (CW) + [[0, 0], [1, 0], [1, 1], [0, -2], [1, -2]], + // state 2 → 3 (CW) + [[0, 0], [1, 0], [1, -1], [0, 2], [1, 2]], + // state 3 → 0 (CW) + [[0, 0], [-1, 0], [-1, 1], [0, -2], [-1, -2]], +]; +const KICKS_I = [ + // state 0 → 1 (CW) + [[0, 0], [-2, 0], [1, 0], [-2, 1], [1, -2]], + // state 1 → 2 (CW) + [[0, 0], [-1, 0], [2, 0], [-1, -2], [2, 1]], + // state 2 → 3 (CW) + [[0, 0], [2, 0], [-1, 0], [2, -1], [-1, 2]], + // state 3 → 0 (CW) + [[0, 0], [1, 0], [-2, 0], [1, 2], [-2, -1]], +]; +// ── Scoring per lines cleared (× level) ──────── +const LINE_POINTS = [0, 100, 300, 500, 800]; +// ───────────────────────────────────────────── +// TetrisGame class +// ───────────────────────────────────────────── +class TetrisGame { + constructor() { + this.current = null; + this.next = null; + this.score = 0; + this.lines = 0; + this.level = 1; + this.gameOver = false; + this.paused = false; + // Timing + this.rafId = 0; + this.lastStamp = 0; + this.dropAccum = 0; // ms accumulated towards next auto-drop + // DAS (Delayed Auto-Shift) state for smooth key repeat + this.dasKey = null; + this.dasTimer = 0; + this.DAS_DELAY = 170; // ms before repeat starts + this.DAS_PERIOD = 50; // ms between repeats + // ── Bag randomiser (7-bag) ───────────────── + this.bag = []; + this.boardCanvas = document.getElementById('game-canvas'); + this.boardCtx = this.boardCanvas.getContext('2d'); + this.nextCanvas = document.getElementById('next-canvas'); + this.nextCtx = this.nextCanvas.getContext('2d'); + this.elScore = document.getElementById('score'); + this.elLines = document.getElementById('lines'); + this.elLevel = document.getElementById('level'); + this.board = this.emptyBoard(); + // Wire up keyboard + window.addEventListener('keydown', this.onKeyDown.bind(this)); + window.addEventListener('keyup', this.onKeyUp.bind(this)); + // Wire up buttons + document.getElementById('btn-restart') + .addEventListener('click', () => this.restart()); + document.getElementById('btn-pause') + .addEventListener('click', () => this.togglePause()); + this.spawnNext(); // prepares this.next + this.spawnPiece(); // moves next → current, prepares new next + this.loop(0); + } + // ── Board helpers ────────────────────────── + emptyBoard() { + return Array.from({ length: ROWS }, () => Array(COLS).fill(null)); + } + // ── Bag randomiser ───────────────────────── + pullFromBag() { + if (this.bag.length === 0) { + const types = Object.keys(SHAPES); + // Fisher-Yates shuffle + for (let i = types.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [types[i], types[j]] = [types[j], types[i]]; + } + this.bag = types; + } + return this.bag.pop(); + } + makePiece(type) { + // Centre-spawn: for 3-wide pieces col=3, for 4-wide pieces col=3 too + return { type, rot: 0, col: 3, row: 0 }; + } + spawnNext() { + this.next = this.makePiece(this.pullFromBag()); + this.drawNext(); + } + spawnPiece() { + var _a; + this.current = (_a = this.next) !== null && _a !== void 0 ? _a : this.makePiece(this.pullFromBag()); + this.spawnNext(); + // Nudge piece so it isn't partially hidden above board + // (the I-piece row-0 is blank in spawn orientation — push it up by 1) + if (this.current.type === 'I') { + this.current.row = -1; + } + if (!this.fits(this.current, 0, 0)) { + this.gameOver = true; + } + } + // ── Shape helpers ────────────────────────── + shape(p) { + const states = SHAPES[p.type]; + return states[p.rot % states.length]; + } + /** Returns the list of {r, c} filled cells for a piece. */ + cells(p) { + const out = []; + const s = this.shape(p); + for (let r = 0; r < s.length; r++) { + for (let c = 0; c < s[r].length; c++) { + if (s[r][c]) + out.push({ r: p.row + r, c: p.col + c }); + } + } + return out; + } + /** True when the piece (plus offset) fits on the board without collision. */ + fits(p, dc, dr, rot) { + const test = Object.assign(Object.assign({}, p), { col: p.col + dc, row: p.row + dr }); + if (rot !== undefined) + test.rot = rot; + for (const { r, c } of this.cells(test)) { + if (c < 0 || c >= COLS || r >= ROWS) + return false; + if (r >= 0 && this.board[r][c] !== null) + return false; + } + return true; + } + // ── Ghost piece ──────────────────────────── + ghostRow() { + if (!this.current) + return 0; + let dr = 0; + while (this.fits(this.current, 0, dr + 1)) + dr++; + return this.current.row + dr; + } + // ── Movement ────────────────────────────── + moveLeft() { if (this.current && this.fits(this.current, -1, 0)) + this.current.col--; } + moveRight() { if (this.current && this.fits(this.current, 1, 0)) + this.current.col++; } + softDrop() { + if (!this.current) + return; + if (this.fits(this.current, 0, 1)) { + this.current.row++; + this.score += 1; + this.dropAccum = 0; + this.updateStats(); + } + else { + this.lock(); + } + } + hardDrop() { + if (!this.current) + return; + let dropped = 0; + while (this.fits(this.current, 0, 1)) { + this.current.row++; + dropped++; + } + this.score += dropped * 2; + this.updateStats(); + this.lock(); + } + rotate(dir) { + if (!this.current) + return; + const states = SHAPES[this.current.type].length; + const oldRot = this.current.rot; + const newRot = ((oldRot + dir) % states + states) % states; + const kickTable = this.current.type === 'I' ? KICKS_I : KICKS_JLSTZ; + // For CW use kicks[oldRot]; for CCW negate kicks[newRot] + let kicks; + if (dir === 1) { + kicks = kickTable[oldRot % kickTable.length]; + } + else { + kicks = kickTable[newRot % kickTable.length].map(([dx, dy]) => [-dx, -dy]); + } + for (const [dx, dy] of kicks) { + if (this.fits(this.current, dx, dy, newRot)) { + this.current.col += dx; + this.current.row += dy; + this.current.rot = newRot; + return; + } + } + // No kick succeeded — rotation blocked + } + // ── Locking ─────────────────────────────── + lock() { + if (!this.current) + return; + for (const { r, c } of this.cells(this.current)) { + if (r >= 0) + this.board[r][c] = PIECE_COLORS[this.current.type]; + } + this.clearLines(); + this.spawnPiece(); + this.dropAccum = 0; + } + clearLines() { + let cleared = 0; + for (let r = ROWS - 1; r >= 0; r--) { + if (this.board[r].every(c => c !== null)) { + this.board.splice(r, 1); + this.board.unshift(Array(COLS).fill(null)); + cleared++; + r++; // re-check same index after splice + } + } + if (cleared === 0) + return; + this.score += LINE_POINTS[cleared] * this.level; + this.lines += cleared; + this.level = Math.floor(this.lines / 10) + 1; + this.updateStats(); + } + // ── Drop interval (ms) by level ─────────── + dropInterval() { + // Levels 1–20, each level ~12% faster, floor at 50 ms + return Math.max(50, Math.round(1000 * Math.pow(0.85, this.level - 1))); + } + // ── Stats DOM update ────────────────────── + updateStats() { + this.elScore.textContent = this.score.toString(); + this.elLines.textContent = this.lines.toString(); + this.elLevel.textContent = this.level.toString(); + } + // ── Public controls ─────────────────────── + togglePause() { + if (this.gameOver) + return; + this.paused = !this.paused; + const btn = document.getElementById('btn-pause'); + if (btn) + btn.textContent = this.paused ? 'Resume' : 'Pause'; + } + restart() { + this.board = this.emptyBoard(); + this.current = null; + this.next = null; + this.bag = []; + this.score = 0; + this.lines = 0; + this.level = 1; + this.dropAccum = 0; + this.gameOver = false; + this.paused = false; + this.dasKey = null; + this.updateStats(); + const btn = document.getElementById('btn-pause'); + if (btn) + btn.textContent = 'Pause'; + this.spawnNext(); + this.spawnPiece(); + } + // ── Keyboard handling ───────────────────── + onKeyDown(e) { + // Always allow restart / pause + if (e.key === 'r' || e.key === 'R') { + this.restart(); + return; + } + if (e.key === 'p' || e.key === 'P') { + this.togglePause(); + return; + } + if (e.key === 'Escape') { + this.togglePause(); + return; + } + if (this.gameOver || this.paused) + return; + switch (e.key) { + case 'ArrowLeft': + e.preventDefault(); + this.moveLeft(); + this.startDAS('ArrowLeft'); + break; + case 'ArrowRight': + e.preventDefault(); + this.moveRight(); + this.startDAS('ArrowRight'); + break; + case 'ArrowDown': + e.preventDefault(); + this.softDrop(); + this.startDAS('ArrowDown'); + break; + case 'ArrowUp': + e.preventDefault(); + this.rotate(1); + break; + case 'x': + case 'X': + this.rotate(1); // CW + break; + case 'z': + case 'Z': + this.rotate(-1); // CCW + break; + case 'Control': + this.rotate(-1); // CCW (alternative) + break; + case ' ': + e.preventDefault(); + this.hardDrop(); + break; + } + } + onKeyUp(e) { + if (this.dasKey === e.key) + this.dasKey = null; + } + startDAS(key) { + if (this.dasKey === key) + return; + this.dasKey = key; + this.dasTimer = this.DAS_DELAY; + } + tickDAS(dt) { + if (!this.dasKey || this.paused || this.gameOver) + return; + this.dasTimer -= dt; + if (this.dasTimer <= 0) { + this.dasTimer += this.DAS_PERIOD; + switch (this.dasKey) { + case 'ArrowLeft': + this.moveLeft(); + break; + case 'ArrowRight': + this.moveRight(); + break; + case 'ArrowDown': + this.softDrop(); + break; + } + } + } + // ── Main loop ───────────────────────────── + loop(stamp) { + const dt = Math.min(stamp - this.lastStamp, 100); // cap at 100ms + this.lastStamp = stamp; + if (!this.paused && !this.gameOver) { + this.dropAccum += dt; + if (this.dropAccum >= this.dropInterval()) { + this.dropAccum -= this.dropInterval(); + if (this.current) { + if (this.fits(this.current, 0, 1)) { + this.current.row++; + } + else { + this.lock(); + } + } + } + this.tickDAS(dt); + } + this.render(); + this.rafId = requestAnimationFrame(this.loop.bind(this)); + } + // ───────────────────────────────────────── + // Rendering + // ───────────────────────────────────────── + render() { + const ctx = this.boardCtx; + const W = this.boardCanvas.width; + const H = this.boardCanvas.height; + // Background + ctx.fillStyle = '#0a0a1a'; + ctx.fillRect(0, 0, W, H); + // Subtle grid + ctx.strokeStyle = 'rgba(255,255,255,0.04)'; + ctx.lineWidth = 1; + for (let c = 0; c <= COLS; c++) { + ctx.beginPath(); + ctx.moveTo(c * CELL, 0); + ctx.lineTo(c * CELL, H); + ctx.stroke(); + } + for (let r = 0; r <= ROWS; r++) { + ctx.beginPath(); + ctx.moveTo(0, r * CELL); + ctx.lineTo(W, r * CELL); + ctx.stroke(); + } + // Locked cells + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + const col = this.board[r][c]; + if (col) + this.drawCell(ctx, c, r, col, CELL); + } + } + // Ghost piece + if (this.current && !this.gameOver) { + const gr = this.ghostRow(); + const ghost = Object.assign(Object.assign({}, this.current), { row: gr }); + for (const { r, c } of this.cells(ghost)) { + if (r < 0) + continue; + ctx.strokeStyle = PIECE_COLORS[this.current.type]; + ctx.lineWidth = 1.5; + ctx.globalAlpha = 0.3; + ctx.strokeRect(c * CELL + 2, r * CELL + 2, CELL - 4, CELL - 4); + ctx.globalAlpha = 1; + } + } + // Active piece + if (this.current && !this.gameOver) { + const col = PIECE_COLORS[this.current.type]; + for (const { r, c } of this.cells(this.current)) { + if (r < 0) + continue; + this.drawCell(ctx, c, r, col, CELL); + } + } + // Overlays + if (this.paused) { + this.drawOverlay(ctx, W, H, 'PAUSED', '#ffffff', 'Press P to resume'); + } + else if (this.gameOver) { + this.drawOverlay(ctx, W, H, 'GAME OVER', '#ff4455', `Score: ${this.score} · Press R to restart`); + } + } + drawCell(ctx, col, row, color, size) { + const x = col * size; + const y = row * size; + // Main fill + ctx.fillStyle = color; + ctx.fillRect(x + 1, y + 1, size - 2, size - 2); + // Bright top-left bevel + ctx.fillStyle = 'rgba(255,255,255,0.30)'; + ctx.fillRect(x + 1, y + 1, size - 2, 4); + ctx.fillRect(x + 1, y + 1, 4, size - 2); + // Dark bottom-right bevel + ctx.fillStyle = 'rgba(0,0,0,0.35)'; + ctx.fillRect(x + 1, y + size - 5, size - 2, 4); + ctx.fillRect(x + size - 5, y + 1, 4, size - 2); + // Inner highlight + ctx.fillStyle = 'rgba(255,255,255,0.12)'; + ctx.fillRect(x + 5, y + 5, size - 10, size - 10); + } + drawOverlay(ctx, W, H, title, titleColor, subtitle) { + ctx.fillStyle = 'rgba(0,0,0,0.72)'; + ctx.fillRect(0, 0, W, H); + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.fillStyle = titleColor; + ctx.font = `bold ${Math.round(CELL * 1.2)}px "Courier New", monospace`; + ctx.fillText(title, W / 2, H / 2 - CELL); + ctx.fillStyle = 'rgba(220,220,255,0.80)'; + ctx.font = `${Math.round(CELL * 0.5)}px "Courier New", monospace`; + ctx.fillText(subtitle, W / 2, H / 2 + CELL * 0.4); + } + // ── Next-piece preview ──────────────────── + drawNext() { + const ctx = this.nextCtx; + const W = this.nextCanvas.width; + const H = this.nextCanvas.height; + ctx.fillStyle = '#0a0a1a'; + ctx.fillRect(0, 0, W, H); + if (!this.next) + return; + const s = this.shape(this.next); + const rows = s.length; + const cols = s[0].length; + const color = PIECE_COLORS[this.next.type]; + const ox = Math.floor((W / NEXT_CELL - cols) / 2); + const oy = Math.floor((H / NEXT_CELL - rows) / 2); + for (let r = 0; r < rows; r++) { + for (let c = 0; c < cols; c++) { + if (s[r][c]) { + this.drawCell(ctx, ox + c, oy + r, color, NEXT_CELL); + } + } + } + } +} +// ── Bootstrap ──────────────────────────────── +window.addEventListener('DOMContentLoaded', () => { + new TetrisGame(); +}); +//# sourceMappingURL=tetris.js.map +\ No newline at end of file diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/tetris.js.map b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/tetris.js.map @@ -0,0 +1 @@ +{"version":3,"file":"tetris.js","sourceRoot":"","sources":["tetris.ts"],"names":[],"mappings":"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P,CAAC,CAAC,EAAC,CAAC,EAAC,CAAC,CAAC;YACP,CAAC,CAAC,EAAC,CAAC,EAAC,CAAC,CAAC,CAAC;QAET,CAAC,CAAC,CAAC,EAAC,CAAC,EAAC,CAAC,CAAC;YACP,CAAC,CAAC,EAAC,CAAC,EAAC,CAAC,CAAC;YACP,CAAC,CAAC,EAAC,CAAC,EAAC,CAAC,CAAC,CAAC;KACV;IACD,CAAC,EAAE;QACD,CAAC,CAAC,CAAC,EAAC,CAAC,EAAC,CAAC,CAAC;YACP,CAAC,CAAC,EAAC,CAAC,EAAC,CAAC,CAAC;YACP,CAAC,CAAC,EAAC,CAAC,EAAC,CAAC,CAAC,CAAC;QAET,CAAC,CAAC,CAAC,EAAC,CAAC,EAAC,CAAC,CAAC;YACP,CAAC,CAAC,EAAC,CAAC,EAAC,CAAC,CAAC;YACP,CAAC,CAAC,EAAC,CAAC,EAAC,CAAC,CAAC,CAAC;QAET,CAAC,CAAC,CAAC,EAAC,CAAC,EAAC,CAAC,CAAC;YACP,CAAC,CAAC,EAAC,CAAC,EAAC,CAAC,CAAC;YACP,CAAC,CAAC,EAAC,CAAC,EAAC,CAAC,CAAC,CAAC;QAET,CAAC,CAAC,CAAC,EAAC,CAAC,EAAC,CAAC,CAAC;YACP,CAAC,CAAC,EAAC,CAAC,EAAC,CAAC,CAAC;YACP,CAAC,CAAC,EAAC,CAAC,EAAC,CAAC,CAAC,CAAC;KACV;IACD,CAAC,EAAE;QACD,CAAC,CAAC,CAAC,EAAC,CAAC,EAAC,CAAC,CAAC;YACP,CAAC,CAAC,EAAC,CAAC,EAAC,CAAC,CAAC;YACP,CAAC,CAAC,EAAC,CAAC,EAAC,CAAC,CAAC,CAAC;QAET,CAAC,CAAC,CAAC,EAAC,CAAC,EAAC,CAAC,CAAC;YACP,CAAC,CAAC,EAAC,CAAC,EAAC,CAAC,CAAC;YACP,CAAC,CAAC,EAAC,CAAC,EAAC,CAAC,CAAC,CAAC;QAET,CAAC,CAAC,CAAC,EAAC,CAAC,EAAC,CAAC,CAAC;YACP,CAAC,CAAC,EAAC,CAAC,EAAC,CAAC,CAAC;YACP,CAAC,CAAC,EAAC,CAAC,EAAC,CAAC,CAAC,CAAC;QAET,CAAC,CAAC,CAAC,EAAC,CAAC,EAAC,CAA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"} +\ No newline at end of file diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/tetris.ts b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/tetris.ts @@ -0,0 +1,693 @@ +// ───────────────────────────────────────────── +// Tetris — TypeScript browser implementation +// ───────────────────────────────────────────── + +const COLS = 10; +const ROWS = 20; +const CELL = 30; // px per cell on main board +const NEXT_CELL = 25; // px per cell in next-piece preview + +// ── Colours ──────────────────────────────────── +const PIECE_COLORS: Record<string, string> = { + I: '#00f0f0', + O: '#f0f000', + T: '#a000f0', + S: '#00e000', + Z: '#f00000', + J: '#0050f0', + L: '#f0a000', +}; + +// ── SRS shape tables ─────────────────────────── +// Each entry is an array of rotation states. +// 0 = spawn orientation, then CW 1, 180, CCW. +// 1 inside the matrix means a filled cell. +const SHAPES: Record<string, number[][][]> = { + I: [ + [[0,0,0,0], + [1,1,1,1], + [0,0,0,0], + [0,0,0,0]], + + [[0,0,1,0], + [0,0,1,0], + [0,0,1,0], + [0,0,1,0]], + + [[0,0,0,0], + [0,0,0,0], + [1,1,1,1], + [0,0,0,0]], + + [[0,1,0,0], + [0,1,0,0], + [0,1,0,0], + [0,1,0,0]], + ], + O: [ + [[0,1,1,0], + [0,1,1,0], + [0,0,0,0], + [0,0,0,0]], + ], + T: [ + [[0,1,0], + [1,1,1], + [0,0,0]], + + [[0,1,0], + [0,1,1], + [0,1,0]], + + [[0,0,0], + [1,1,1], + [0,1,0]], + + [[0,1,0], + [1,1,0], + [0,1,0]], + ], + S: [ + [[0,1,1], + [1,1,0], + [0,0,0]], + + [[0,1,0], + [0,1,1], + [0,0,1]], + + [[0,0,0], + [0,1,1], + [1,1,0]], + + [[1,0,0], + [1,1,0], + [0,1,0]], + ], + Z: [ + [[1,1,0], + [0,1,1], + [0,0,0]], + + [[0,0,1], + [0,1,1], + [0,1,0]], + + [[0,0,0], + [1,1,0], + [0,1,1]], + + [[0,1,0], + [1,1,0], + [1,0,0]], + ], + J: [ + [[1,0,0], + [1,1,1], + [0,0,0]], + + [[0,1,1], + [0,1,0], + [0,1,0]], + + [[0,0,0], + [1,1,1], + [0,0,1]], + + [[0,1,0], + [0,1,0], + [1,1,0]], + ], + L: [ + [[0,0,1], + [1,1,1], + [0,0,0]], + + [[0,1,0], + [0,1,0], + [0,1,1]], + + [[0,0,0], + [1,1,1], + [1,0,0]], + + [[1,1,0], + [0,1,0], + [0,1,0]], + ], +}; + +// ── SRS wall-kick tables ──────────────────────── +// Each row is the list of (dx, dy) offsets to try for a CW rotation +// starting from that state index. Screen coords: +y = down. +// Source: Tetris Wiki / SRS (y-axis flipped from wiki math coords). +type Kick = [number, number]; + +const KICKS_JLSTZ: Kick[][] = [ + // state 0 → 1 (CW) + [[0,0],[-1,0],[-1,-1],[0,2],[-1,2]], + // state 1 → 2 (CW) + [[0,0],[1,0],[1,1],[0,-2],[1,-2]], + // state 2 → 3 (CW) + [[0,0],[1,0],[1,-1],[0,2],[1,2]], + // state 3 → 0 (CW) + [[0,0],[-1,0],[-1,1],[0,-2],[-1,-2]], +]; + +const KICKS_I: Kick[][] = [ + // state 0 → 1 (CW) + [[0,0],[-2,0],[1,0],[-2,1],[1,-2]], + // state 1 → 2 (CW) + [[0,0],[-1,0],[2,0],[-1,-2],[2,1]], + // state 2 → 3 (CW) + [[0,0],[2,0],[-1,0],[2,-1],[-1,2]], + // state 3 → 0 (CW) + [[0,0],[1,0],[-2,0],[1,2],[-2,-1]], +]; + +// ── Types ────────────────────────────────────── +type PieceType = keyof typeof SHAPES; + +interface ActivePiece { + type : PieceType; + rot : number; // 0–3 + col : number; // board column of top-left of bounding box + row : number; // board row of top-left of bounding box +} + +type Cell = string | null; // null = empty, string = colour + +// ── Scoring per lines cleared (× level) ──────── +const LINE_POINTS = [0, 100, 300, 500, 800]; + +// ───────────────────────────────────────────── +// TetrisGame class +// ───────────────────────────────────────────── +class TetrisGame { + // Canvases + private readonly boardCanvas : HTMLCanvasElement; + private readonly boardCtx : CanvasRenderingContext2D; + private readonly nextCanvas : HTMLCanvasElement; + private readonly nextCtx : CanvasRenderingContext2D; + + // DOM stat elements + private readonly elScore : HTMLElement; + private readonly elLines : HTMLElement; + private readonly elLevel : HTMLElement; + + // Game state + private board : Cell[][]; + private current: ActivePiece | null = null; + private next : ActivePiece | null = null; + + private score : number = 0; + private lines : number = 0; + private level : number = 1; + + private gameOver: boolean = false; + private paused : boolean = false; + + // Timing + private rafId : number = 0; + private lastStamp : number = 0; + private dropAccum : number = 0; // ms accumulated towards next auto-drop + + // DAS (Delayed Auto-Shift) state for smooth key repeat + private dasKey : string | null = null; + private dasTimer : number = 0; + private readonly DAS_DELAY = 170; // ms before repeat starts + private readonly DAS_PERIOD = 50; // ms between repeats + + // ── Bag randomiser (7-bag) ───────────────── + private bag: PieceType[] = []; + + constructor() { + this.boardCanvas = document.getElementById('game-canvas') as HTMLCanvasElement; + this.boardCtx = this.boardCanvas.getContext('2d')!; + this.nextCanvas = document.getElementById('next-canvas') as HTMLCanvasElement; + this.nextCtx = this.nextCanvas.getContext('2d')!; + + this.elScore = document.getElementById('score')!; + this.elLines = document.getElementById('lines')!; + this.elLevel = document.getElementById('level')!; + + this.board = this.emptyBoard(); + + // Wire up keyboard + window.addEventListener('keydown', this.onKeyDown.bind(this)); + window.addEventListener('keyup', this.onKeyUp.bind(this)); + + // Wire up buttons + document.getElementById('btn-restart')! + .addEventListener('click', () => this.restart()); + document.getElementById('btn-pause')! + .addEventListener('click', () => this.togglePause()); + + this.spawnNext(); // prepares this.next + this.spawnPiece(); // moves next → current, prepares new next + this.loop(0); + } + + // ── Board helpers ────────────────────────── + private emptyBoard(): Cell[][] { + return Array.from({ length: ROWS }, () => Array<Cell>(COLS).fill(null)); + } + + // ── Bag randomiser ───────────────────────── + private pullFromBag(): PieceType { + if (this.bag.length === 0) { + const types = Object.keys(SHAPES) as PieceType[]; + // Fisher-Yates shuffle + for (let i = types.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [types[i], types[j]] = [types[j], types[i]]; + } + this.bag = types; + } + return this.bag.pop()!; + } + + private makePiece(type: PieceType): ActivePiece { + // Centre-spawn: for 3-wide pieces col=3, for 4-wide pieces col=3 too + return { type, rot: 0, col: 3, row: 0 }; + } + + private spawnNext(): void { + this.next = this.makePiece(this.pullFromBag()); + this.drawNext(); + } + + private spawnPiece(): void { + this.current = this.next ?? this.makePiece(this.pullFromBag()); + this.spawnNext(); + + // Nudge piece so it isn't partially hidden above board + // (the I-piece row-0 is blank in spawn orientation — push it up by 1) + if (this.current.type === 'I') { + this.current.row = -1; + } + + if (!this.fits(this.current, 0, 0)) { + this.gameOver = true; + } + } + + // ── Shape helpers ────────────────────────── + private shape(p: ActivePiece): number[][] { + const states = SHAPES[p.type]; + return states[p.rot % states.length]; + } + + /** Returns the list of {r, c} filled cells for a piece. */ + private cells(p: ActivePiece): Array<{r: number; c: number}> { + const out: Array<{r: number; c: number}> = []; + const s = this.shape(p); + for (let r = 0; r < s.length; r++) { + for (let c = 0; c < s[r].length; c++) { + if (s[r][c]) out.push({ r: p.row + r, c: p.col + c }); + } + } + return out; + } + + /** True when the piece (plus offset) fits on the board without collision. */ + private fits(p: ActivePiece, dc: number, dr: number, rot?: number): boolean { + const test: ActivePiece = { ...p, col: p.col + dc, row: p.row + dr }; + if (rot !== undefined) test.rot = rot; + for (const { r, c } of this.cells(test)) { + if (c < 0 || c >= COLS || r >= ROWS) return false; + if (r >= 0 && this.board[r][c] !== null) return false; + } + return true; + } + + // ── Ghost piece ──────────────────────────── + private ghostRow(): number { + if (!this.current) return 0; + let dr = 0; + while (this.fits(this.current, 0, dr + 1)) dr++; + return this.current.row + dr; + } + + // ── Movement ────────────────────────────── + private moveLeft () : void { if (this.current && this.fits(this.current, -1, 0)) this.current.col--; } + private moveRight() : void { if (this.current && this.fits(this.current, 1, 0)) this.current.col++; } + + private softDrop(): void { + if (!this.current) return; + if (this.fits(this.current, 0, 1)) { + this.current.row++; + this.score += 1; + this.dropAccum = 0; + this.updateStats(); + } else { + this.lock(); + } + } + + private hardDrop(): void { + if (!this.current) return; + let dropped = 0; + while (this.fits(this.current, 0, 1)) { + this.current.row++; + dropped++; + } + this.score += dropped * 2; + this.updateStats(); + this.lock(); + } + + private rotate(dir: 1 | -1): void { + if (!this.current) return; + const states = SHAPES[this.current.type].length; + const oldRot = this.current.rot; + const newRot = ((oldRot + dir) % states + states) % states; + + const kickTable = this.current.type === 'I' ? KICKS_I : KICKS_JLSTZ; + + // For CW use kicks[oldRot]; for CCW negate kicks[newRot] + let kicks: Kick[]; + if (dir === 1) { + kicks = kickTable[oldRot % kickTable.length]; + } else { + kicks = kickTable[newRot % kickTable.length].map(([dx, dy]) => [-dx, -dy] as Kick); + } + + for (const [dx, dy] of kicks) { + if (this.fits(this.current, dx, dy, newRot)) { + this.current.col += dx; + this.current.row += dy; + this.current.rot = newRot; + return; + } + } + // No kick succeeded — rotation blocked + } + + // ── Locking ─────────────────────────────── + private lock(): void { + if (!this.current) return; + + for (const { r, c } of this.cells(this.current)) { + if (r >= 0) this.board[r][c] = PIECE_COLORS[this.current.type]; + } + + this.clearLines(); + this.spawnPiece(); + this.dropAccum = 0; + } + + private clearLines(): void { + let cleared = 0; + for (let r = ROWS - 1; r >= 0; r--) { + if (this.board[r].every(c => c !== null)) { + this.board.splice(r, 1); + this.board.unshift(Array<Cell>(COLS).fill(null)); + cleared++; + r++; // re-check same index after splice + } + } + if (cleared === 0) return; + + this.score += LINE_POINTS[cleared] * this.level; + this.lines += cleared; + this.level = Math.floor(this.lines / 10) + 1; + this.updateStats(); + } + + // ── Drop interval (ms) by level ─────────── + private dropInterval(): number { + // Levels 1–20, each level ~12% faster, floor at 50 ms + return Math.max(50, Math.round(1000 * Math.pow(0.85, this.level - 1))); + } + + // ── Stats DOM update ────────────────────── + private updateStats(): void { + this.elScore.textContent = this.score.toString(); + this.elLines.textContent = this.lines.toString(); + this.elLevel.textContent = this.level.toString(); + } + + // ── Public controls ─────────────────────── + togglePause(): void { + if (this.gameOver) return; + this.paused = !this.paused; + const btn = document.getElementById('btn-pause'); + if (btn) btn.textContent = this.paused ? 'Resume' : 'Pause'; + } + + restart(): void { + this.board = this.emptyBoard(); + this.current = null; + this.next = null; + this.bag = []; + this.score = 0; + this.lines = 0; + this.level = 1; + this.dropAccum = 0; + this.gameOver = false; + this.paused = false; + this.dasKey = null; + this.updateStats(); + const btn = document.getElementById('btn-pause'); + if (btn) btn.textContent = 'Pause'; + this.spawnNext(); + this.spawnPiece(); + } + + // ── Keyboard handling ───────────────────── + private onKeyDown(e: KeyboardEvent): void { + // Always allow restart / pause + if (e.key === 'r' || e.key === 'R') { this.restart(); return; } + if (e.key === 'p' || e.key === 'P') { this.togglePause(); return; } + if (e.key === 'Escape') { this.togglePause(); return; } + + if (this.gameOver || this.paused) return; + + switch (e.key) { + case 'ArrowLeft': + e.preventDefault(); + this.moveLeft(); + this.startDAS('ArrowLeft'); + break; + case 'ArrowRight': + e.preventDefault(); + this.moveRight(); + this.startDAS('ArrowRight'); + break; + case 'ArrowDown': + e.preventDefault(); + this.softDrop(); + this.startDAS('ArrowDown'); + break; + case 'ArrowUp': + e.preventDefault(); + this.rotate(1); + break; + case 'x': case 'X': + this.rotate(1); // CW + break; + case 'z': case 'Z': + this.rotate(-1); // CCW + break; + case 'Control': + this.rotate(-1); // CCW (alternative) + break; + case ' ': + e.preventDefault(); + this.hardDrop(); + break; + } + } + + private onKeyUp(e: KeyboardEvent): void { + if (this.dasKey === e.key) this.dasKey = null; + } + + private startDAS(key: string): void { + if (this.dasKey === key) return; + this.dasKey = key; + this.dasTimer = this.DAS_DELAY; + } + + private tickDAS(dt: number): void { + if (!this.dasKey || this.paused || this.gameOver) return; + this.dasTimer -= dt; + if (this.dasTimer <= 0) { + this.dasTimer += this.DAS_PERIOD; + switch (this.dasKey) { + case 'ArrowLeft': this.moveLeft(); break; + case 'ArrowRight': this.moveRight(); break; + case 'ArrowDown': this.softDrop(); break; + } + } + } + + // ── Main loop ───────────────────────────── + private loop(stamp: number): void { + const dt = Math.min(stamp - this.lastStamp, 100); // cap at 100ms + this.lastStamp = stamp; + + if (!this.paused && !this.gameOver) { + this.dropAccum += dt; + if (this.dropAccum >= this.dropInterval()) { + this.dropAccum -= this.dropInterval(); + if (this.current) { + if (this.fits(this.current, 0, 1)) { + this.current.row++; + } else { + this.lock(); + } + } + } + this.tickDAS(dt); + } + + this.render(); + this.rafId = requestAnimationFrame(this.loop.bind(this)); + } + + // ───────────────────────────────────────── + // Rendering + // ───────────────────────────────────────── + private render(): void { + const ctx = this.boardCtx; + const W = this.boardCanvas.width; + const H = this.boardCanvas.height; + + // Background + ctx.fillStyle = '#0a0a1a'; + ctx.fillRect(0, 0, W, H); + + // Subtle grid + ctx.strokeStyle = 'rgba(255,255,255,0.04)'; + ctx.lineWidth = 1; + for (let c = 0; c <= COLS; c++) { + ctx.beginPath(); ctx.moveTo(c * CELL, 0); ctx.lineTo(c * CELL, H); ctx.stroke(); + } + for (let r = 0; r <= ROWS; r++) { + ctx.beginPath(); ctx.moveTo(0, r * CELL); ctx.lineTo(W, r * CELL); ctx.stroke(); + } + + // Locked cells + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + const col = this.board[r][c]; + if (col) this.drawCell(ctx, c, r, col, CELL); + } + } + + // Ghost piece + if (this.current && !this.gameOver) { + const gr = this.ghostRow(); + const ghost: ActivePiece = { ...this.current, row: gr }; + for (const { r, c } of this.cells(ghost)) { + if (r < 0) continue; + ctx.strokeStyle = PIECE_COLORS[this.current.type]; + ctx.lineWidth = 1.5; + ctx.globalAlpha = 0.3; + ctx.strokeRect(c * CELL + 2, r * CELL + 2, CELL - 4, CELL - 4); + ctx.globalAlpha = 1; + } + } + + // Active piece + if (this.current && !this.gameOver) { + const col = PIECE_COLORS[this.current.type]; + for (const { r, c } of this.cells(this.current)) { + if (r < 0) continue; + this.drawCell(ctx, c, r, col, CELL); + } + } + + // Overlays + if (this.paused) { + this.drawOverlay(ctx, W, H, 'PAUSED', '#ffffff', 'Press P to resume'); + } else if (this.gameOver) { + this.drawOverlay(ctx, W, H, 'GAME OVER', '#ff4455', `Score: ${this.score} · Press R to restart`); + } + } + + private drawCell( + ctx : CanvasRenderingContext2D, + col : number, row: number, + color: string, + size : number, + ): void { + const x = col * size; + const y = row * size; + + // Main fill + ctx.fillStyle = color; + ctx.fillRect(x + 1, y + 1, size - 2, size - 2); + + // Bright top-left bevel + ctx.fillStyle = 'rgba(255,255,255,0.30)'; + ctx.fillRect(x + 1, y + 1, size - 2, 4); + ctx.fillRect(x + 1, y + 1, 4, size - 2); + + // Dark bottom-right bevel + ctx.fillStyle = 'rgba(0,0,0,0.35)'; + ctx.fillRect(x + 1, y + size - 5, size - 2, 4); + ctx.fillRect(x + size - 5, y + 1, 4, size - 2); + + // Inner highlight + ctx.fillStyle = 'rgba(255,255,255,0.12)'; + ctx.fillRect(x + 5, y + 5, size - 10, size - 10); + } + + private drawOverlay( + ctx : CanvasRenderingContext2D, + W : number, H: number, + title : string, + titleColor: string, + subtitle: string, + ): void { + ctx.fillStyle = 'rgba(0,0,0,0.72)'; + ctx.fillRect(0, 0, W, H); + + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + + ctx.fillStyle = titleColor; + ctx.font = `bold ${Math.round(CELL * 1.2)}px "Courier New", monospace`; + ctx.fillText(title, W / 2, H / 2 - CELL); + + ctx.fillStyle = 'rgba(220,220,255,0.80)'; + ctx.font = `${Math.round(CELL * 0.5)}px "Courier New", monospace`; + ctx.fillText(subtitle, W / 2, H / 2 + CELL * 0.4); + } + + // ── Next-piece preview ──────────────────── + private drawNext(): void { + const ctx = this.nextCtx; + const W = this.nextCanvas.width; + const H = this.nextCanvas.height; + + ctx.fillStyle = '#0a0a1a'; + ctx.fillRect(0, 0, W, H); + + if (!this.next) return; + + const s = this.shape(this.next); + const rows = s.length; + const cols = s[0].length; + const color = PIECE_COLORS[this.next.type]; + + const ox = Math.floor((W / NEXT_CELL - cols) / 2); + const oy = Math.floor((H / NEXT_CELL - rows) / 2); + + for (let r = 0; r < rows; r++) { + for (let c = 0; c < cols; c++) { + if (s[r][c]) { + this.drawCell(ctx, ox + c, oy + r, color, NEXT_CELL); + } + } + } + } +} + +// ── Bootstrap ──────────────────────────────── +window.addEventListener('DOMContentLoaded', () => { + new TetrisGame(); +}); diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/tsconfig.json b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["ES2017", "DOM"], + "module": "None", + "outFile": "tetris.js", + "strict": true, + "noImplicitAny": true, + "sourceMap": true, + "types": [] + }, + "include": ["tetris.ts"], + "exclude": ["node_modules"] +} diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/index.html b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/index.html @@ -0,0 +1,203 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <title>Tetris</title> + <style> + *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } + + body { + background: #0a0a0f; + color: #e0e0e0; + font-family: "Segoe UI", system-ui, sans-serif; + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; + gap: 0; + } + + /* ── Layout wrapper ── */ + #game-wrapper { + display: flex; + align-items: flex-start; + gap: 20px; + } + + /* ── Board ── */ + #board-container { + position: relative; + } + + #board { + display: block; + border: 2px solid #333; + border-radius: 4px; + box-shadow: 0 0 40px rgba(0, 240, 240, 0.08); + } + + /* ── Overlays ── */ + .overlay { + display: none; + position: absolute; + inset: 0; + background: rgba(0,0,0,0.82); + flex-direction: column; + align-items: center; + justify-content: center; + gap: 16px; + border-radius: 4px; + backdrop-filter: blur(4px); + } + + .overlay h2 { + font-size: 2rem; + letter-spacing: 4px; + text-transform: uppercase; + } + + .overlay p { + color: #aaa; + font-size: 0.9rem; + letter-spacing: 1px; + } + + #gameover-overlay h2 { color: #f04040; } + #pause-overlay h2 { color: #f0f000; } + + .final-score-label { color: #aaa; font-size: 0.85rem; letter-spacing: 2px; text-transform: uppercase; } + #final-score { font-size: 2.5rem; font-weight: 700; color: #fff; } + + /* ── Side panel ── */ + #side-panel { + display: flex; + flex-direction: column; + gap: 20px; + width: 130px; + } + + .panel-box { + background: #141418; + border: 1px solid #2a2a35; + border-radius: 6px; + padding: 12px 14px; + } + + .panel-box .label { + font-size: 0.68rem; + letter-spacing: 2px; + text-transform: uppercase; + color: #666; + margin-bottom: 4px; + } + + .panel-box .value { + font-size: 1.5rem; + font-weight: 700; + color: #fff; + letter-spacing: 1px; + } + + #preview-box .label { margin-bottom: 8px; } + + #preview { + display: block; + border-radius: 3px; + } + + /* ── Controls help ── */ + #controls { + background: #141418; + border: 1px solid #2a2a35; + border-radius: 6px; + padding: 12px 14px; + font-size: 0.7rem; + line-height: 1.9; + color: #888; + } + + #controls .ctrl-title { + font-size: 0.68rem; + letter-spacing: 2px; + text-transform: uppercase; + color: #555; + margin-bottom: 6px; + } + + kbd { + display: inline-block; + background: #222; + border: 1px solid #444; + border-radius: 3px; + padding: 0 4px; + font-family: inherit; + font-size: 0.68rem; + color: #ccc; + } + </style> +</head> +<body> + <div id="game-wrapper"> + + <!-- Board + overlays --> + <div id="board-container"> + <canvas id="board"></canvas> + + <div id="pause-overlay" class="overlay"> + <h2>Paused</h2> + <p>Press <kbd>P</kbd> or <kbd>Esc</kbd> to resume</p> + </div> + + <div id="gameover-overlay" class="overlay"> + <h2>Game Over</h2> + <p class="final-score-label">Score</p> + <div id="final-score">0</div> + <p>Press <kbd>Enter</kbd> or <kbd>Space</kbd> to restart</p> + </div> + </div> + + <!-- Side panel --> + <div id="side-panel"> + + <div class="panel-box" id="preview-box"> + <div class="label">Next</div> + <canvas id="preview"></canvas> + </div> + + <div class="panel-box"> + <div class="label">Score</div> + <div class="value" id="score">0</div> + </div> + + <div class="panel-box"> + <div class="label">Lines</div> + <div class="value" id="lines">0</div> + </div> + + <div class="panel-box"> + <div class="label">Level</div> + <div class="value" id="level">0</div> + </div> + + <div class="panel-box"> + <div class="label">Best</div> + <div class="value" id="highscore">0</div> + </div> + + <div id="controls"> + <div class="ctrl-title">Controls</div> + <div><kbd>←</kbd><kbd>→</kbd> Move</div> + <div><kbd>↑</kbd> / <kbd>X</kbd> Rotate CW</div> + <div><kbd>Z</kbd> Rotate CCW</div> + <div><kbd>↓</kbd> Soft drop</div> + <div><kbd>Space</kbd> Hard drop</div> + <div><kbd>P</kbd> Pause</div> + </div> + + </div> + </div> + + <script src="dist/tetris.js"></script> +</body> +</html> diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/package-lock.json b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/package-lock.json @@ -0,0 +1,2583 @@ +{ + "name": "loop-bench-q82ugmwv", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "loop-bench-q82ugmwv", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "@eslint/js": "^10.0.1", + "@playwright/test": "^1.59.1", + "@types/node": "^25.5.2", + "eslint": "^10.2.0", + "html-validate": "^10.11.3", + "jscpd": "^4.0.8", + "typescript": "^6.0.2" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.4", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.4.tgz", + "integrity": "sha512-lf19F24LSMfF8weXvW5QEtnLqW70u7kgit5e9PSx0MsHAFclGd1T9ynvWEMDT1w5J4Qt54tomGeAhdoAku1Xow==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.4", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.4.tgz", + "integrity": "sha512-jJhqiY3wPMlWWO3370M86CPJ7pt8GmEwSLglMfQhjXal07RCvhmU0as4IuUEW5SJeunfItiEetHmSxCCe9lDBg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.0.tgz", + "integrity": "sha512-8FTGbNzTvmSlc4cZBaShkC6YvFMG0riksYWRFKXztqVdXaQbcZLXlFbSpC05s70sGEsXAw0qwhx69JiW7hQS7A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.4.tgz", + "integrity": "sha512-55lO/7+Yp0ISKRP0PsPtNTeNGapXaO085aELZmWCVc5SH3jfrqpuU6YgOdIxMS99ZHkQN1cXKE+cdIqwww9ptw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.0.tgz", + "integrity": "sha512-ejvBr8MQCbVsWNZnCwDXjUKq40MDmHalq7cJ6e9s/qzTUFIIo/afzt1Vui9T97FM/V/pN4YsFVoed5NIa96RDg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@html-validate/stylish": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@html-validate/stylish/-/stylish-5.0.0.tgz", + "integrity": "sha512-xjhRV9k1mWfgsOcpYlwsjUOFy3w3EnCDrqUrEw+DWdvOStMK59ts2H7GLKWZtmLI5m6Npp3qMSNReafQy9K2sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^4.0.0" + }, + "engines": { + "node": "^20.11 || >= 22.16" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jscpd/badge-reporter": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@jscpd/badge-reporter/-/badge-reporter-4.0.4.tgz", + "integrity": "sha512-I9b4MmLXPM2vo0SxSUWnNGKcA4PjQlD3GzXvFK60z43cN/EIdLbOq3FVwCL+dg2obUqGXKIzAm7EsDFTg0D+mQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "badgen": "^3.2.3", + "colors": "^1.4.0", + "fs-extra": "^11.2.0" + } + }, + "node_modules/@jscpd/core": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@jscpd/core/-/core-4.0.4.tgz", + "integrity": "sha512-QGMT3iXEX1fI6lgjPH+x8eyJwhwr2KkpSF5uBpjC0Z5Xloj0yFTFLtwJT+RhxP/Ob4WYrtx2jvpKB269oIwgMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.1" + } + }, + "node_modules/@jscpd/finder": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@jscpd/finder/-/finder-4.0.4.tgz", + "integrity": "sha512-qVUWY7Nzuvfd5OIk+n7/5CM98LmFroLqblRXAI2gDABwZrc7qS+WH2SNr0qoUq0f4OqwM+piiwKvwL/VDNn/Cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jscpd/core": "4.0.4", + "@jscpd/tokenizer": "4.0.4", + "blamer": "^1.0.6", + "bytes": "^3.1.2", + "cli-table3": "^0.6.5", + "colors": "^1.4.0", + "fast-glob": "^3.3.2", + "fs-extra": "^11.2.0", + "markdown-table": "^2.0.0", + "pug": "^3.0.3" + } + }, + "node_modules/@jscpd/html-reporter": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@jscpd/html-reporter/-/html-reporter-4.0.4.tgz", + "integrity": "sha512-YiepyeYkeH74Kx59PJRdUdonznct0wHPFkf6FLQN+mCBoy6leAWCcOfHtcexnp+UsBFDlItG5nRdKrDSxSH+Kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "colors": "1.4.0", + "fs-extra": "^11.2.0", + "pug": "^3.0.3" + } + }, + "node_modules/@jscpd/tokenizer": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@jscpd/tokenizer/-/tokenizer-4.0.4.tgz", + "integrity": "sha512-xxYYY/qaLah/FlwogEbGIxx9CjDO+G9E6qawcy26WwrflzJb6wsnhjwdneN6Wb0RNCDsqvzY+bzG453jsin4UQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jscpd/core": "4.0.4", + "reprism": "^0.0.11", + "spark-md5": "^3.0.2" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@playwright/test": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz", + "integrity": "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.59.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.5.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.2.tgz", + "integrity": "sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/sarif": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@types/sarif/-/sarif-2.1.7.tgz", + "integrity": "sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/assert-never": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.4.0.tgz", + "integrity": "sha512-5oJg84os6NMQNl27T9LnZkvvqzvAnHu03ShCnoj6bsJwS7L8AO4lf+C/XjK/nvzEqQB744moC6V128RucQd1jA==", + "dev": true, + "license": "MIT" + }, + "node_modules/babel-walk": { + "version": "3.0.0-canary-5", + "resolved": "https://registry.npmjs.org/babel-walk/-/babel-walk-3.0.0-canary-5.tgz", + "integrity": "sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.9.6" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/badgen": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/badgen/-/badgen-3.2.3.tgz", + "integrity": "sha512-svDuwkc63E/z0ky3drpUppB83s/nlgDciH9m+STwwQoWyq7yCgew1qEfJ+9axkKdNq7MskByptWUN9j1PGMwFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/blamer": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/blamer/-/blamer-1.0.7.tgz", + "integrity": "sha512-GbBStl/EVlSWkiJQBZps3H1iARBrC7vt++Jb/TTmCNu/jZ04VW7tSN1nScbFXBUy1AN+jzeL7Zep9sbQxLhXKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^4.0.0", + "which": "^2.0.2" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/character-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", + "integrity": "sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-regex": "^1.0.3" + } + }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/constantinople": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz", + "integrity": "sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.6.0", + "@babel/types": "^7.6.1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/doctypes": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", + "integrity": "sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.2.0.tgz", + "integrity": "sha512-+L0vBFYGIpSNIt/KWTpFonPrqYvgKw1eUI5Vn7mEogrQcWtWYtNQ7dNqC+px/J0idT3BAkiWrhfS7k+Tum8TUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.4", + "@eslint/config-helpers": "^0.5.4", + "@eslint/core": "^1.2.0", + "@eslint/plugin-kit": "^0.7.0", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/fs-extra": { + "version": "11.3.4", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", + "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gitignore-to-glob": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/gitignore-to-glob/-/gitignore-to-glob-0.3.0.tgz", + "integrity": "sha512-mk74BdnK7lIwDHnotHddx1wsjMOFIThpLY3cPNniJ/2fA/tlLzHnFxIdR+4sLOu5KGgQJdij4kjJ2RoUNnCNMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.4 <5 || >=6.9" + } + }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-validate": { + "version": "10.11.3", + "resolved": "https://registry.npmjs.org/html-validate/-/html-validate-10.11.3.tgz", + "integrity": "sha512-wKUq9iR6bukMgiHhs/ORThZzEbQoFiiPNN7aZfQ8dlmhttPb2sM2Ji2p+Fy5Xj1aH7QHJ1biT2SUDw7A01P2oA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/html-validate" + } + ], + "license": "MIT", + "dependencies": { + "@html-validate/stylish": "^5.0.0", + "@sidvind/better-ajv-errors": "4.0.1", + "ajv": "^8.0.0", + "glob": "^13.0.0", + "kleur": "^4.1.0", + "minimist": "^1.2.0", + "prompts": "^2.0.0", + "semver": "^7.0.0" + }, + "bin": { + "html-validate": "bin/html-validate.mjs" + }, + "engines": { + "node": "^20.19.0 || >= 22.16.0" + }, + "peerDependencies": { + "jest": "^28.1.3 || ^29.0.3 || ^30.0.0", + "jest-diff": "^28.1.3 || ^29.0.3 || ^30.0.0", + "jest-snapshot": "^28.1.3 || ^29.0.3 || ^30.0.0", + "vitest": "^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.1" + }, + "peerDependenciesMeta": { + "jest": { + "optional": true + }, + "jest-diff": { + "optional": true + }, + "jest-snapshot": { + "optional": true + }, + "vitest": { + "optional": true + } + } + }, + "node_modules/html-validate/node_modules/@sidvind/better-ajv-errors": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sidvind/better-ajv-errors/-/better-ajv-errors-4.0.1.tgz", + "integrity": "sha512-6arF1ssKxItxgitPYXafUoLmsVBA6K7m9+ZGj6hLDoBl7nWpJ33EInwQUdHTle2METeWGxgQiqSex20KZRykew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "kleur": "^4.1.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "ajv": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/html-validate/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/html-validate/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-expression": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", + "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^7.1.1", + "object-assign": "^4.1.1" + } + }, + "node_modules/is-expression/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-stringify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", + "integrity": "sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==", + "dev": true, + "license": "MIT" + }, + "node_modules/jscpd": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/jscpd/-/jscpd-4.0.8.tgz", + "integrity": "sha512-d2VNT/2Hv4dxT2/59He8Lyda4DYOxPRyRG9zBaOpTZAqJCVf2xLrBlZkT8Va6Lo9u3X2qz8Bpq4HrDi4JsrQhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jscpd/badge-reporter": "4.0.4", + "@jscpd/core": "4.0.4", + "@jscpd/finder": "4.0.4", + "@jscpd/html-reporter": "4.0.4", + "@jscpd/tokenizer": "4.0.4", + "colors": "^1.4.0", + "commander": "^5.0.0", + "fs-extra": "^11.2.0", + "gitignore-to-glob": "^0.3.0", + "jscpd-sarif-reporter": "4.0.6" + }, + "bin": { + "jscpd": "bin/jscpd" + } + }, + "node_modules/jscpd-sarif-reporter": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/jscpd-sarif-reporter/-/jscpd-sarif-reporter-4.0.6.tgz", + "integrity": "sha512-b9Sm3IPZ3+m8Lwa4gZa+4/LhDhlc/ZLEsLXKSOy1DANQ6kx0ueqZT+fUHWEdQ6m0o3+RIVIa7DmvLSojQD05ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "colors": "^1.4.0", + "fs-extra": "^11.2.0", + "node-sarif-builder": "^3.4.0" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jstransformer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", + "integrity": "sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-promise": "^2.0.0", + "promise": "^7.0.1" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/markdown-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz", + "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "repeat-string": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-sarif-builder": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/node-sarif-builder/-/node-sarif-builder-3.4.0.tgz", + "integrity": "sha512-tGnJW6OKRii9u/b2WiUViTJS+h7Apxx17qsMUjsUeNDiMMX5ZFf8F8Fcz7PAQ6omvOxHZtvDTmOYKJQwmfpjeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/sarif": "^2.1.7", + "fs-extra": "^11.1.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/playwright": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz", + "integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.59.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz", + "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "asap": "~2.0.3" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prompts/node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pug": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.4.tgz", + "integrity": "sha512-kFfq5mMzrS7+wrl5pLJzZEzemx34OQ0w4SARfhy/3yxTlhbstsudDwJzhf1hP02yHzbjoVMSXUj/Sz6RNfMyXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pug-code-gen": "^3.0.4", + "pug-filters": "^4.0.0", + "pug-lexer": "^5.0.1", + "pug-linker": "^4.0.0", + "pug-load": "^3.0.0", + "pug-parser": "^6.0.0", + "pug-runtime": "^3.0.1", + "pug-strip-comments": "^2.0.0" + } + }, + "node_modules/pug-attrs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-3.0.0.tgz", + "integrity": "sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==", + "dev": true, + "license": "MIT", + "dependencies": { + "constantinople": "^4.0.1", + "js-stringify": "^1.0.2", + "pug-runtime": "^3.0.0" + } + }, + "node_modules/pug-code-gen": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-3.0.4.tgz", + "integrity": "sha512-6okWYIKdasTyXICyEtvobmTZAVX57JkzgzIi4iRJlin8kmhG+Xry2dsus+Mun/nGCn6F2U49haHI5mkELXB14g==", + "dev": true, + "license": "MIT", + "dependencies": { + "constantinople": "^4.0.1", + "doctypes": "^1.1.0", + "js-stringify": "^1.0.2", + "pug-attrs": "^3.0.0", + "pug-error": "^2.1.0", + "pug-runtime": "^3.0.1", + "void-elements": "^3.1.0", + "with": "^7.0.0" + } + }, + "node_modules/pug-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-2.1.0.tgz", + "integrity": "sha512-lv7sU9e5Jk8IeUheHata6/UThZ7RK2jnaaNztxfPYUY+VxZyk/ePVaNZ/vwmH8WqGvDz3LrNYt/+gA55NDg6Pg==", + "dev": true, + "license": "MIT" + }, + "node_modules/pug-filters": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-4.0.0.tgz", + "integrity": "sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "constantinople": "^4.0.1", + "jstransformer": "1.0.0", + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0", + "resolve": "^1.15.1" + } + }, + "node_modules/pug-lexer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-5.0.1.tgz", + "integrity": "sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-parser": "^2.2.0", + "is-expression": "^4.0.0", + "pug-error": "^2.0.0" + } + }, + "node_modules/pug-linker": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-4.0.0.tgz", + "integrity": "sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0" + } + }, + "node_modules/pug-load": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-3.0.0.tgz", + "integrity": "sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "object-assign": "^4.1.1", + "pug-walk": "^2.0.0" + } + }, + "node_modules/pug-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-6.0.0.tgz", + "integrity": "sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pug-error": "^2.0.0", + "token-stream": "1.0.0" + } + }, + "node_modules/pug-runtime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-3.0.1.tgz", + "integrity": "sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==", + "dev": true, + "license": "MIT" + }, + "node_modules/pug-strip-comments": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz", + "integrity": "sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "pug-error": "^2.0.0" + } + }, + "node_modules/pug-walk": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz", + "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/reprism": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/reprism/-/reprism-0.0.11.tgz", + "integrity": "sha512-VsxDR5QxZo08M/3nRypNlScw5r3rKeSOPdU/QhDmu3Ai3BJxHn/qgfXGWQp/tAxUtzwYNo9W6997JZR0tPLZsA==", + "dev": true, + "license": "MIT" + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/spark-md5": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/spark-md5/-/spark-md5-3.0.2.tgz", + "integrity": "sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw==", + "dev": true, + "license": "(WTFPL OR MIT)" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/token-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", + "integrity": "sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg==", + "dev": true, + "license": "MIT" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", + "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/with": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz", + "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.9.6", + "@babel/types": "^7.9.6", + "assert-never": "^1.2.1", + "babel-walk": "3.0.0-canary-5" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/package.json b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/package.json @@ -0,0 +1,22 @@ +{ + "name": "loop-bench-q82ugmwv", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "commonjs", + "devDependencies": { + "@eslint/js": "^10.0.1", + "@playwright/test": "^1.59.1", + "@types/node": "^25.5.2", + "eslint": "^10.2.0", + "html-validate": "^10.11.3", + "jscpd": "^4.0.8", + "typescript": "^6.0.2" + } +} diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/report/jscpd-report.json b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/report/jscpd-report.json @@ -0,0 +1,156 @@ +{ + "statistics": { + "detectionDate": "2026-04-04T21:17:44.759Z", + "formats": { + "typescript": { + "sources": { + "src/tetris.ts": { + "lines": 555, + "tokens": 5867, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "total": { + "lines": 555, + "tokens": 5867, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "javascript": { + "sources": { + "dist/tetris.js": { + "lines": 465, + "tokens": 5330, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + }, + "smoke-test.mjs": { + "lines": 49, + "tokens": 594, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "total": { + "lines": 514, + "tokens": 5924, + "sources": 2, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "json": { + "sources": { + "tsconfig.json": { + "lines": 10, + "tokens": 66, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + }, + "package.json": { + "lines": 21, + "tokens": 132, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "total": { + "lines": 31, + "tokens": 198, + "sources": 2, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "markup": { + "sources": { + "index.html": { + "lines": 202, + "tokens": 1454, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "total": { + "lines": 202, + "tokens": 1454, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + } + }, + "total": { + "lines": 1302, + "tokens": 13443, + "sources": 6, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "duplicates": [] +} +\ No newline at end of file diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/smoke-test.mjs b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/smoke-test.mjs @@ -0,0 +1,50 @@ +import { chromium } from './node_modules/playwright-core/index.js'; +import path from 'path'; + +(async () => { + const browser = await chromium.launch(); + const page = await browser.newPage(); + + const errors = []; + page.on('pageerror', e => errors.push(e.message)); + page.on('console', msg => { + if (msg.type() === 'error') errors.push(msg.text()); + }); + + const file = path.resolve('/tmp/loop-bench-q82ugmwv/index.html'); + await page.goto(`file://${file}`); + await page.waitForTimeout(1200); + + const boardSize = await page.evaluate(() => { + const c = document.getElementById('board'); + return { w: c.width, h: c.height }; + }); + console.log('Board size:', boardSize); + + const hudValues = await page.evaluate(() => ({ + score: document.getElementById('score').textContent, + lines: document.getElementById('lines').textContent, + level: document.getElementById('level').textContent, + })); + console.log('HUD:', hudValues); + + await page.keyboard.press('ArrowLeft'); + await page.keyboard.press('ArrowRight'); + await page.keyboard.press('ArrowUp'); + await page.keyboard.press('Space'); // hard drop + await page.waitForTimeout(300); + + const scoreAfter = await page.evaluate(() => document.getElementById('score').textContent); + console.log('Score after hard drop:', scoreAfter); + + const pauseVisible = await page.evaluate(() => { + document.dispatchEvent(new KeyboardEvent('keydown', { code: 'KeyP', bubbles: true })); + return document.getElementById('pause-overlay').style.display; + }); + console.log('Pause overlay after P:', pauseVisible); + + await browser.close(); + + if (errors.length) { console.error('JS Errors:', errors); process.exit(1); } + console.log('\n✅ All checks passed'); +})(); diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/src/tetris.ts b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/src/tetris.ts @@ -0,0 +1,556 @@ +// ============================================================ +// Tetris – complete implementation +// ============================================================ + +// ── Types ──────────────────────────────────────────────────── + +type Color = string; +type Grid = (Color | null)[][]; +type TetrominoShape = number[][]; + +interface Tetromino { + shape: TetrominoShape; + color: Color; + x: number; + y: number; +} + +// ── Constants ──────────────────────────────────────────────── + +const COLS = 10; +const ROWS = 20; +const BLOCK = 30; // px per cell +const PREVIEW_BLOCK = 24; + +const COLORS: Record<string, Color> = { + I: "#00f0f0", + O: "#f0f000", + T: "#a000f0", + S: "#00f000", + Z: "#f00000", + J: "#0000f0", + L: "#f0a000", +}; + +const SHAPES: Record<string, TetrominoShape> = { + I: [[1, 1, 1, 1]], + O: [ + [1, 1], + [1, 1], + ], + T: [ + [0, 1, 0], + [1, 1, 1], + ], + S: [ + [0, 1, 1], + [1, 1, 0], + ], + Z: [ + [1, 1, 0], + [0, 1, 1], + ], + J: [ + [1, 0, 0], + [1, 1, 1], + ], + L: [ + [0, 0, 1], + [1, 1, 1], + ], +}; + +const PIECE_KEYS = Object.keys(SHAPES); + +// Points awarded per number of lines cleared simultaneously +const LINE_POINTS = [0, 100, 300, 500, 800]; + +// Drop interval in ms at each level (level 0 … 29) +function dropInterval(level: number): number { + // Classic NES-style speed curve (clamped at level 29) + const speeds = [ + 800, 716, 633, 550, 466, 383, 300, 216, 133, 100, 83, 83, 83, 66, 66, 66, + 50, 50, 50, 33, 33, 33, 33, 33, 33, 16, 16, 16, 16, 16, + ]; + return speeds[Math.min(level, speeds.length - 1)]; +} + +// ── Piece factory ──────────────────────────────────────────── + +function randomPiece(): Tetromino { + const key = PIECE_KEYS[Math.floor(Math.random() * PIECE_KEYS.length)]; + const shape = SHAPES[key].map((row) => [...row]); + return { + shape, + color: COLORS[key], + x: Math.floor((COLS - shape[0].length) / 2), + y: 0, + }; +} + +// ── Rotation ───────────────────────────────────────────────── + +function rotate(shape: TetrominoShape): TetrominoShape { + const rows = shape.length; + const cols = shape[0].length; + const result: TetrominoShape = Array.from({ length: cols }, () => + new Array(rows).fill(0) + ); + for (let r = 0; r < rows; r++) { + for (let c = 0; c < cols; c++) { + result[c][rows - 1 - r] = shape[r][c]; + } + } + return result; +} + +// ── Board helpers ──────────────────────────────────────────── + +function emptyGrid(): Grid { + return Array.from({ length: ROWS }, () => new Array<Color | null>(COLS).fill(null)); +} + +function isValid(grid: Grid, piece: Tetromino, dx = 0, dy = 0, shape?: TetrominoShape): boolean { + const s = shape ?? piece.shape; + for (let r = 0; r < s.length; r++) { + for (let c = 0; c < s[r].length; c++) { + if (!s[r][c]) continue; + const nx = piece.x + c + dx; + const ny = piece.y + r + dy; + if (nx < 0 || nx >= COLS || ny >= ROWS) return false; + if (ny < 0) continue; // allow above board + if (grid[ny][nx] !== null) return false; + } + } + return true; +} + +function lock(grid: Grid, piece: Tetromino): Grid { + const next = grid.map((row) => [...row]); + for (let r = 0; r < piece.shape.length; r++) { + for (let c = 0; c < piece.shape[r].length; c++) { + if (!piece.shape[r][c]) continue; + const ny = piece.y + r; + const nx = piece.x + c; + if (ny >= 0) next[ny][nx] = piece.color; + } + } + return next; +} + +function clearLines(grid: Grid): { grid: Grid; linesCleared: number } { + const kept = grid.filter((row) => row.some((cell) => cell === null)); + const linesCleared = ROWS - kept.length; + const newRows: Grid = Array.from({ length: linesCleared }, () => + new Array<Color | null>(COLS).fill(null) + ); + return { grid: [...newRows, ...kept], linesCleared }; +} + +// ── Wall‑kick data (SRS) ────────────────────────────────────── +// Offsets to try when a rotation is blocked +const KICKS: [number, number][] = [ + [0, 0], + [-1, 0], + [1, 0], + [-1, -1], + [1, -1], + [0, -2], +]; + +function tryRotate(grid: Grid, piece: Tetromino): Tetromino | null { + const newShape = rotate(piece.shape); + for (const [dx, dy] of KICKS) { + if (isValid(grid, piece, dx, dy, newShape)) { + return { ...piece, shape: newShape, x: piece.x + dx, y: piece.y + dy }; + } + } + return null; +} + +// ── Ghost piece ─────────────────────────────────────────────── + +function ghostY(grid: Grid, piece: Tetromino): number { + let dy = 0; + while (isValid(grid, piece, 0, dy + 1)) dy++; + return piece.y + dy; +} + +// ── Canvas rendering ────────────────────────────────────────── + +function drawBlock( + ctx: CanvasRenderingContext2D, + x: number, + y: number, + color: Color, + size: number, + alpha = 1 +): void { + ctx.save(); + ctx.globalAlpha = alpha; + // Main fill + ctx.fillStyle = color; + ctx.fillRect(x * size + 1, y * size + 1, size - 2, size - 2); + // Highlight top-left edge + ctx.fillStyle = "rgba(255,255,255,0.35)"; + ctx.fillRect(x * size + 1, y * size + 1, size - 2, 3); + ctx.fillRect(x * size + 1, y * size + 1, 3, size - 2); + // Shadow bottom-right edge + ctx.fillStyle = "rgba(0,0,0,0.25)"; + ctx.fillRect(x * size + 1, y * size + size - 4, size - 2, 3); + ctx.fillRect(x * size + size - 4, y * size + 1, 3, size - 2); + ctx.restore(); +} + +function drawGrid(ctx: CanvasRenderingContext2D, grid: Grid): void { + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + const cell = grid[r][c]; + if (cell) { + drawBlock(ctx, c, r, cell, BLOCK); + } else { + ctx.fillStyle = "rgba(255,255,255,0.03)"; + ctx.fillRect(c * BLOCK, r * BLOCK, BLOCK, BLOCK); + } + } + } +} + +function drawPiece(ctx: CanvasRenderingContext2D, piece: Tetromino, alpha = 1): void { + for (let r = 0; r < piece.shape.length; r++) { + for (let c = 0; c < piece.shape[r].length; c++) { + if (piece.shape[r][c]) { + drawBlock(ctx, piece.x + c, piece.y + r, piece.color, BLOCK, alpha); + } + } + } +} + +function drawGhost(ctx: CanvasRenderingContext2D, grid: Grid, piece: Tetromino): void { + const gy = ghostY(grid, piece); + if (gy === piece.y) return; + const ghost: Tetromino = { ...piece, y: gy }; + ctx.save(); + ctx.globalAlpha = 0.18; + for (let r = 0; r < ghost.shape.length; r++) { + for (let c = 0; c < ghost.shape[r].length; c++) { + if (ghost.shape[r][c]) { + ctx.fillStyle = piece.color; + ctx.fillRect( + (ghost.x + c) * BLOCK + 1, + (ghost.y + r) * BLOCK + 1, + BLOCK - 2, + BLOCK - 2 + ); + ctx.strokeStyle = piece.color; + ctx.lineWidth = 1.5; + ctx.strokeRect( + (ghost.x + c) * BLOCK + 2, + (ghost.y + r) * BLOCK + 2, + BLOCK - 4, + BLOCK - 4 + ); + } + } + } + ctx.restore(); +} + +function drawPreview(ctx: CanvasRenderingContext2D, piece: Tetromino): void { + const pw = 4 * PREVIEW_BLOCK; + const ph = 4 * PREVIEW_BLOCK; + ctx.clearRect(0, 0, pw, ph); + ctx.fillStyle = "#111"; + ctx.fillRect(0, 0, pw, ph); + + const cols = piece.shape[0].length; + const rows = piece.shape.length; + const offsetX = Math.floor((4 - cols) / 2); + const offsetY = Math.floor((4 - rows) / 2); + + for (let r = 0; r < rows; r++) { + for (let c = 0; c < cols; c++) { + if (piece.shape[r][c]) { + drawBlock(ctx, offsetX + c, offsetY + r, piece.color, PREVIEW_BLOCK); + } + } + } +} + +// ── Game class ──────────────────────────────────────────────── + +class Tetris { + private canvas: HTMLCanvasElement; + private ctx: CanvasRenderingContext2D; + private previewCanvas: HTMLCanvasElement; + private previewCtx: CanvasRenderingContext2D; + + private grid: Grid = emptyGrid(); + private current: Tetromino = randomPiece(); + private next: Tetromino = randomPiece(); + + private score = 0; + private lines = 0; + private level = 0; + private highScore = 0; + + private paused = false; + private gameOver = false; + + private lastTime = 0; + private dropAccum = 0; + private rafId = 0; + + // soft-drop tracking + private softDropping = false; + + constructor() { + this.canvas = document.getElementById("board") as HTMLCanvasElement; + this.ctx = this.canvas.getContext("2d")!; + this.previewCanvas = document.getElementById("preview") as HTMLCanvasElement; + this.previewCtx = this.previewCanvas.getContext("2d")!; + + this.canvas.width = COLS * BLOCK; + this.canvas.height = ROWS * BLOCK; + this.previewCanvas.width = 4 * PREVIEW_BLOCK; + this.previewCanvas.height = 4 * PREVIEW_BLOCK; + + this.loadHighScore(); + this.bindKeys(); + this.render(0); + } + + // ── Persistence ────────────────────────────────────────────── + + private loadHighScore(): void { + const stored = localStorage.getItem("tetris-highscore"); + if (stored) this.highScore = parseInt(stored, 10); + this.updateHUD(); + } + + private saveHighScore(): void { + if (this.score > this.highScore) { + this.highScore = this.score; + localStorage.setItem("tetris-highscore", String(this.highScore)); + } + } + + // ── HUD ─────────────────────────────────────────────────────── + + private updateHUD(): void { + (document.getElementById("score") as HTMLElement).textContent = String(this.score); + (document.getElementById("lines") as HTMLElement).textContent = String(this.lines); + (document.getElementById("level") as HTMLElement).textContent = String(this.level); + (document.getElementById("highscore") as HTMLElement).textContent = String(this.highScore); + } + + // ── Input ───────────────────────────────────────────────────── + + private bindKeys(): void { + document.addEventListener("keydown", (e) => { + if (this.gameOver) { + if (e.code === "Enter" || e.code === "Space") this.restart(); + return; + } + if (e.code === "KeyP" || e.code === "Escape") { + this.togglePause(); + return; + } + if (this.paused) return; + + switch (e.code) { + case "ArrowLeft": + e.preventDefault(); + this.move(-1, 0); + break; + case "ArrowRight": + e.preventDefault(); + this.move(1, 0); + break; + case "ArrowDown": + e.preventDefault(); + this.softDropping = true; + this.move(0, 1); + break; + case "ArrowUp": + case "KeyX": + e.preventDefault(); + this.rotatePiece(); + break; + case "KeyZ": + e.preventDefault(); + this.rotateCCW(); + break; + case "Space": + e.preventDefault(); + this.hardDrop(); + break; + case "KeyC": + // hold – not implemented but key reserved + break; + } + }); + + document.addEventListener("keyup", (e) => { + if (e.code === "ArrowDown") this.softDropping = false; + }); + } + + // ── Actions ─────────────────────────────────────────────────── + + private move(dx: number, dy: number): boolean { + if (isValid(this.grid, this.current, dx, dy)) { + this.current = { ...this.current, x: this.current.x + dx, y: this.current.y + dy }; + if (dy > 0 && this.softDropping) { + // award 1 point per soft-drop row + this.score += 1; + this.updateHUD(); + } + return true; + } + return false; + } + + private rotatePiece(): void { + const rotated = tryRotate(this.grid, this.current); + if (rotated) this.current = rotated; + } + + private rotateCCW(): void { + // Three CW rotations = one CCW rotation + let piece = this.current; + for (let i = 0; i < 3; i++) { + const r = tryRotate(this.grid, piece); + if (!r) return; + piece = r; + } + this.current = piece; + } + + private hardDrop(): void { + let dropped = 0; + while (this.move(0, 1)) dropped++; + this.score += dropped * 2; + this.updateHUD(); + this.lockPiece(); + } + + private lockPiece(): void { + this.grid = lock(this.grid, this.current); + const { grid, linesCleared } = clearLines(this.grid); + this.grid = grid; + + if (linesCleared > 0) { + this.score += LINE_POINTS[linesCleared] * (this.level + 1); + this.lines += linesCleared; + this.level = Math.floor(this.lines / 10); + this.updateHUD(); + } + + this.current = this.next; + this.next = randomPiece(); + this.dropAccum = 0; + + if (!isValid(this.grid, this.current)) { + this.endGame(); + } + } + + private togglePause(): void { + if (this.gameOver) return; + this.paused = !this.paused; + (document.getElementById("pause-overlay") as HTMLElement).style.display = this.paused + ? "flex" + : "none"; + if (!this.paused) { + this.lastTime = performance.now(); + this.rafId = requestAnimationFrame((t) => this.render(t)); + } + } + + private endGame(): void { + this.gameOver = true; + this.saveHighScore(); + this.updateHUD(); + (document.getElementById("gameover-overlay") as HTMLElement).style.display = "flex"; + (document.getElementById("final-score") as HTMLElement).textContent = String(this.score); + cancelAnimationFrame(this.rafId); + } + + private restart(): void { + this.grid = emptyGrid(); + this.current = randomPiece(); + this.next = randomPiece(); + this.score = 0; + this.lines = 0; + this.level = 0; + this.paused = false; + this.gameOver = false; + this.dropAccum = 0; + this.softDropping = false; + this.updateHUD(); + (document.getElementById("gameover-overlay") as HTMLElement).style.display = "none"; + (document.getElementById("pause-overlay") as HTMLElement).style.display = "none"; + this.lastTime = performance.now(); + this.rafId = requestAnimationFrame((t) => this.render(t)); + } + + // ── Game loop ───────────────────────────────────────────────── + + private render(timestamp: number): void { + if (this.paused || this.gameOver) return; + + const delta = timestamp - this.lastTime; + this.lastTime = timestamp; + this.dropAccum += delta; + + const interval = this.softDropping + ? Math.min(dropInterval(this.level) / 20, 50) + : dropInterval(this.level); + + if (this.dropAccum >= interval) { + this.dropAccum = 0; + if (!this.move(0, 1)) { + this.lockPiece(); + } + } + + this.draw(); + this.rafId = requestAnimationFrame((t) => this.render(t)); + } + + private draw(): void { + const ctx = this.ctx; + // Background + ctx.fillStyle = "#111"; + ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); + + drawGrid(ctx, this.grid); + drawGhost(ctx, this.grid, this.current); + drawPiece(ctx, this.current); + + // Grid lines + ctx.strokeStyle = "rgba(255,255,255,0.04)"; + ctx.lineWidth = 0.5; + for (let c = 0; c <= COLS; c++) { + ctx.beginPath(); + ctx.moveTo(c * BLOCK, 0); + ctx.lineTo(c * BLOCK, this.canvas.height); + ctx.stroke(); + } + for (let r = 0; r <= ROWS; r++) { + ctx.beginPath(); + ctx.moveTo(0, r * BLOCK); + ctx.lineTo(this.canvas.width, r * BLOCK); + ctx.stroke(); + } + + drawPreview(this.previewCtx, this.next); + } +} + +// ── Boot ────────────────────────────────────────────────────── + +window.addEventListener("DOMContentLoaded", () => { + new Tetris(); +}); diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/tsconfig.json b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "strict": true, + "outDir": "./dist", + "rootDir": "./src", + "lib": ["ES2020", "DOM"] + }, + "include": ["src/**/*"] +} diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/index.html b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/index.html @@ -0,0 +1,198 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <title>Tetris</title> + <style> + /* ── Reset ──────────────────────────────────────────────────────────── */ + *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } + + body { + background: #060a12; + color: #d0d8f0; + font-family: 'Courier New', Courier, monospace; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + min-height: 100vh; + overflow: hidden; + user-select: none; + } + + /* ── Title ──────────────────────────────────────────────────────────── */ + .game-title { + font-size: 28px; + font-weight: bold; + letter-spacing: 10px; + margin-bottom: 18px; + background: linear-gradient(120deg, #00c8ff 0%, #c040f0 55%, #ff8800 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + text-shadow: none; + } + + /* ── Layout ─────────────────────────────────────────────────────────── */ + .game-wrapper { + display: flex; + gap: 14px; + align-items: flex-start; + } + + .panel { + width: 138px; + display: flex; + flex-direction: column; + gap: 10px; + } + + /* ── Card ───────────────────────────────────────────────────────────── */ + .card { + background: #0d1420; + border: 1px solid #1e2a40; + border-radius: 8px; + padding: 10px 14px 12px; + } + + .card-label { + font-size: 9px; + letter-spacing: 3px; + text-transform: uppercase; + color: #445566; + margin-bottom: 5px; + } + + .card-value { + font-size: 26px; + font-weight: bold; + color: #c8d8f8; + line-height: 1; + } + + .card-value.md { + font-size: 18px; + } + + /* ── Board canvas ───────────────────────────────────────────────────── */ + #board { + display: block; + border: 2px solid #1e2a40; + border-radius: 4px; + box-shadow: + 0 0 0 1px #060a12, + 0 0 50px rgba(0, 160, 255, 0.09), + 0 8px 40px rgba(0, 0, 0, 0.7); + } + + /* ── Preview canvases ───────────────────────────────────────────────── */ + #next, #hold { + display: block; + border-radius: 4px; + } + + /* ── Controls list ──────────────────────────────────────────────────── */ + .controls { + font-size: 11px; + line-height: 1.95; + color: #3a4a5a; + } + + .controls kbd { + display: inline-block; + min-width: 22px; + text-align: center; + color: #7090b0; + background: #111c2a; + border: 1px solid #1e2a40; + border-radius: 3px; + padding: 0 3px; + font-family: inherit; + font-size: 10px; + } + + .ctrl-row { + display: flex; + align-items: center; + gap: 5px; + } + + .ctrl-desc { + color: #4a5a6a; + } + + /* ── Divider ─────────────────────────────────────────────────────────── */ + .divider { + border: none; + border-top: 1px solid #1a2030; + margin: 2px 0; + } + </style> +</head> +<body> + + <div class="game-title">TETRIS</div> + + <div class="game-wrapper"> + + <!-- ── Left Panel ─────────────────────────────────────────────────── --> + <div class="panel"> + + <div class="card"> + <div class="card-label">Hold <kbd>C</kbd></div> + <canvas id="hold"></canvas> + </div> + + <div class="card"> + <div class="card-label">Level</div> + <div class="card-value" id="level">1</div> + </div> + + <div class="card"> + <div class="card-label">Lines</div> + <div class="card-value" id="lines">0</div> + </div> + + </div> + + <!-- ── Board ──────────────────────────────────────────────────────── --> + <canvas id="board"></canvas> + + <!-- ── Right Panel ────────────────────────────────────────────────── --> + <div class="panel"> + + <div class="card"> + <div class="card-label">Score</div> + <div class="card-value md" id="score">0</div> + </div> + + <div class="card"> + <div class="card-label">Best</div> + <div class="card-value md" id="high-score">0</div> + </div> + + <div class="card"> + <div class="card-label">Next</div> + <canvas id="next"></canvas> + </div> + + <div class="card controls"> + <div class="ctrl-row"><kbd>←</kbd><kbd>→</kbd><span class="ctrl-desc">Move</span></div> + <div class="ctrl-row"><kbd>↑</kbd><kbd>X</kbd><span class="ctrl-desc">Rotate CW</span></div> + <div class="ctrl-row"><kbd>Z</kbd><span class="ctrl-desc">Rotate CCW</span></div> + <div class="ctrl-row"><kbd>↓</kbd><span class="ctrl-desc">Soft drop</span></div> + <div class="ctrl-row"><kbd>Spc</kbd><span class="ctrl-desc">Hard drop</span></div> + <div class="ctrl-row"><kbd>C</kbd><kbd>⇧</kbd><span class="ctrl-desc">Hold</span></div> + <hr class="divider" /> + <div class="ctrl-row"><kbd>P</kbd><span class="ctrl-desc">Pause</span></div> + <div class="ctrl-row"><kbd>R</kbd><span class="ctrl-desc">Restart</span></div> + </div> + + </div> + + </div> + + <script src="dist/tetris.js"></script> +</body> +</html> diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/package-lock.json b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/package-lock.json @@ -0,0 +1,2583 @@ +{ + "name": "loop-bench-iuvnueix", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "loop-bench-iuvnueix", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "@eslint/js": "^10.0.1", + "@playwright/test": "^1.59.1", + "@types/node": "^25.5.2", + "eslint": "^10.2.0", + "html-validate": "^10.11.3", + "jscpd": "^4.0.8", + "typescript": "^6.0.2" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.4", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.4.tgz", + "integrity": "sha512-lf19F24LSMfF8weXvW5QEtnLqW70u7kgit5e9PSx0MsHAFclGd1T9ynvWEMDT1w5J4Qt54tomGeAhdoAku1Xow==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.4", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.4.tgz", + "integrity": "sha512-jJhqiY3wPMlWWO3370M86CPJ7pt8GmEwSLglMfQhjXal07RCvhmU0as4IuUEW5SJeunfItiEetHmSxCCe9lDBg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.0.tgz", + "integrity": "sha512-8FTGbNzTvmSlc4cZBaShkC6YvFMG0riksYWRFKXztqVdXaQbcZLXlFbSpC05s70sGEsXAw0qwhx69JiW7hQS7A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.4.tgz", + "integrity": "sha512-55lO/7+Yp0ISKRP0PsPtNTeNGapXaO085aELZmWCVc5SH3jfrqpuU6YgOdIxMS99ZHkQN1cXKE+cdIqwww9ptw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.0.tgz", + "integrity": "sha512-ejvBr8MQCbVsWNZnCwDXjUKq40MDmHalq7cJ6e9s/qzTUFIIo/afzt1Vui9T97FM/V/pN4YsFVoed5NIa96RDg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@html-validate/stylish": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@html-validate/stylish/-/stylish-5.0.0.tgz", + "integrity": "sha512-xjhRV9k1mWfgsOcpYlwsjUOFy3w3EnCDrqUrEw+DWdvOStMK59ts2H7GLKWZtmLI5m6Npp3qMSNReafQy9K2sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^4.0.0" + }, + "engines": { + "node": "^20.11 || >= 22.16" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jscpd/badge-reporter": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@jscpd/badge-reporter/-/badge-reporter-4.0.4.tgz", + "integrity": "sha512-I9b4MmLXPM2vo0SxSUWnNGKcA4PjQlD3GzXvFK60z43cN/EIdLbOq3FVwCL+dg2obUqGXKIzAm7EsDFTg0D+mQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "badgen": "^3.2.3", + "colors": "^1.4.0", + "fs-extra": "^11.2.0" + } + }, + "node_modules/@jscpd/core": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@jscpd/core/-/core-4.0.4.tgz", + "integrity": "sha512-QGMT3iXEX1fI6lgjPH+x8eyJwhwr2KkpSF5uBpjC0Z5Xloj0yFTFLtwJT+RhxP/Ob4WYrtx2jvpKB269oIwgMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.1" + } + }, + "node_modules/@jscpd/finder": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@jscpd/finder/-/finder-4.0.4.tgz", + "integrity": "sha512-qVUWY7Nzuvfd5OIk+n7/5CM98LmFroLqblRXAI2gDABwZrc7qS+WH2SNr0qoUq0f4OqwM+piiwKvwL/VDNn/Cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jscpd/core": "4.0.4", + "@jscpd/tokenizer": "4.0.4", + "blamer": "^1.0.6", + "bytes": "^3.1.2", + "cli-table3": "^0.6.5", + "colors": "^1.4.0", + "fast-glob": "^3.3.2", + "fs-extra": "^11.2.0", + "markdown-table": "^2.0.0", + "pug": "^3.0.3" + } + }, + "node_modules/@jscpd/html-reporter": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@jscpd/html-reporter/-/html-reporter-4.0.4.tgz", + "integrity": "sha512-YiepyeYkeH74Kx59PJRdUdonznct0wHPFkf6FLQN+mCBoy6leAWCcOfHtcexnp+UsBFDlItG5nRdKrDSxSH+Kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "colors": "1.4.0", + "fs-extra": "^11.2.0", + "pug": "^3.0.3" + } + }, + "node_modules/@jscpd/tokenizer": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@jscpd/tokenizer/-/tokenizer-4.0.4.tgz", + "integrity": "sha512-xxYYY/qaLah/FlwogEbGIxx9CjDO+G9E6qawcy26WwrflzJb6wsnhjwdneN6Wb0RNCDsqvzY+bzG453jsin4UQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jscpd/core": "4.0.4", + "reprism": "^0.0.11", + "spark-md5": "^3.0.2" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@playwright/test": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz", + "integrity": "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.59.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.5.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.2.tgz", + "integrity": "sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/sarif": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@types/sarif/-/sarif-2.1.7.tgz", + "integrity": "sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/assert-never": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.4.0.tgz", + "integrity": "sha512-5oJg84os6NMQNl27T9LnZkvvqzvAnHu03ShCnoj6bsJwS7L8AO4lf+C/XjK/nvzEqQB744moC6V128RucQd1jA==", + "dev": true, + "license": "MIT" + }, + "node_modules/babel-walk": { + "version": "3.0.0-canary-5", + "resolved": "https://registry.npmjs.org/babel-walk/-/babel-walk-3.0.0-canary-5.tgz", + "integrity": "sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.9.6" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/badgen": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/badgen/-/badgen-3.2.3.tgz", + "integrity": "sha512-svDuwkc63E/z0ky3drpUppB83s/nlgDciH9m+STwwQoWyq7yCgew1qEfJ+9axkKdNq7MskByptWUN9j1PGMwFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/blamer": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/blamer/-/blamer-1.0.7.tgz", + "integrity": "sha512-GbBStl/EVlSWkiJQBZps3H1iARBrC7vt++Jb/TTmCNu/jZ04VW7tSN1nScbFXBUy1AN+jzeL7Zep9sbQxLhXKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^4.0.0", + "which": "^2.0.2" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/character-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", + "integrity": "sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-regex": "^1.0.3" + } + }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/constantinople": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz", + "integrity": "sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.6.0", + "@babel/types": "^7.6.1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/doctypes": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", + "integrity": "sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.2.0.tgz", + "integrity": "sha512-+L0vBFYGIpSNIt/KWTpFonPrqYvgKw1eUI5Vn7mEogrQcWtWYtNQ7dNqC+px/J0idT3BAkiWrhfS7k+Tum8TUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.4", + "@eslint/config-helpers": "^0.5.4", + "@eslint/core": "^1.2.0", + "@eslint/plugin-kit": "^0.7.0", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/fs-extra": { + "version": "11.3.4", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", + "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gitignore-to-glob": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/gitignore-to-glob/-/gitignore-to-glob-0.3.0.tgz", + "integrity": "sha512-mk74BdnK7lIwDHnotHddx1wsjMOFIThpLY3cPNniJ/2fA/tlLzHnFxIdR+4sLOu5KGgQJdij4kjJ2RoUNnCNMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.4 <5 || >=6.9" + } + }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-validate": { + "version": "10.11.3", + "resolved": "https://registry.npmjs.org/html-validate/-/html-validate-10.11.3.tgz", + "integrity": "sha512-wKUq9iR6bukMgiHhs/ORThZzEbQoFiiPNN7aZfQ8dlmhttPb2sM2Ji2p+Fy5Xj1aH7QHJ1biT2SUDw7A01P2oA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/html-validate" + } + ], + "license": "MIT", + "dependencies": { + "@html-validate/stylish": "^5.0.0", + "@sidvind/better-ajv-errors": "4.0.1", + "ajv": "^8.0.0", + "glob": "^13.0.0", + "kleur": "^4.1.0", + "minimist": "^1.2.0", + "prompts": "^2.0.0", + "semver": "^7.0.0" + }, + "bin": { + "html-validate": "bin/html-validate.mjs" + }, + "engines": { + "node": "^20.19.0 || >= 22.16.0" + }, + "peerDependencies": { + "jest": "^28.1.3 || ^29.0.3 || ^30.0.0", + "jest-diff": "^28.1.3 || ^29.0.3 || ^30.0.0", + "jest-snapshot": "^28.1.3 || ^29.0.3 || ^30.0.0", + "vitest": "^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.1" + }, + "peerDependenciesMeta": { + "jest": { + "optional": true + }, + "jest-diff": { + "optional": true + }, + "jest-snapshot": { + "optional": true + }, + "vitest": { + "optional": true + } + } + }, + "node_modules/html-validate/node_modules/@sidvind/better-ajv-errors": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sidvind/better-ajv-errors/-/better-ajv-errors-4.0.1.tgz", + "integrity": "sha512-6arF1ssKxItxgitPYXafUoLmsVBA6K7m9+ZGj6hLDoBl7nWpJ33EInwQUdHTle2METeWGxgQiqSex20KZRykew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "kleur": "^4.1.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "ajv": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/html-validate/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/html-validate/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-expression": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", + "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^7.1.1", + "object-assign": "^4.1.1" + } + }, + "node_modules/is-expression/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-stringify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", + "integrity": "sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==", + "dev": true, + "license": "MIT" + }, + "node_modules/jscpd": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/jscpd/-/jscpd-4.0.8.tgz", + "integrity": "sha512-d2VNT/2Hv4dxT2/59He8Lyda4DYOxPRyRG9zBaOpTZAqJCVf2xLrBlZkT8Va6Lo9u3X2qz8Bpq4HrDi4JsrQhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jscpd/badge-reporter": "4.0.4", + "@jscpd/core": "4.0.4", + "@jscpd/finder": "4.0.4", + "@jscpd/html-reporter": "4.0.4", + "@jscpd/tokenizer": "4.0.4", + "colors": "^1.4.0", + "commander": "^5.0.0", + "fs-extra": "^11.2.0", + "gitignore-to-glob": "^0.3.0", + "jscpd-sarif-reporter": "4.0.6" + }, + "bin": { + "jscpd": "bin/jscpd" + } + }, + "node_modules/jscpd-sarif-reporter": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/jscpd-sarif-reporter/-/jscpd-sarif-reporter-4.0.6.tgz", + "integrity": "sha512-b9Sm3IPZ3+m8Lwa4gZa+4/LhDhlc/ZLEsLXKSOy1DANQ6kx0ueqZT+fUHWEdQ6m0o3+RIVIa7DmvLSojQD05ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "colors": "^1.4.0", + "fs-extra": "^11.2.0", + "node-sarif-builder": "^3.4.0" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jstransformer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", + "integrity": "sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-promise": "^2.0.0", + "promise": "^7.0.1" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/markdown-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz", + "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "repeat-string": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-sarif-builder": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/node-sarif-builder/-/node-sarif-builder-3.4.0.tgz", + "integrity": "sha512-tGnJW6OKRii9u/b2WiUViTJS+h7Apxx17qsMUjsUeNDiMMX5ZFf8F8Fcz7PAQ6omvOxHZtvDTmOYKJQwmfpjeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/sarif": "^2.1.7", + "fs-extra": "^11.1.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/playwright": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz", + "integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.59.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz", + "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "asap": "~2.0.3" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prompts/node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pug": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.4.tgz", + "integrity": "sha512-kFfq5mMzrS7+wrl5pLJzZEzemx34OQ0w4SARfhy/3yxTlhbstsudDwJzhf1hP02yHzbjoVMSXUj/Sz6RNfMyXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pug-code-gen": "^3.0.4", + "pug-filters": "^4.0.0", + "pug-lexer": "^5.0.1", + "pug-linker": "^4.0.0", + "pug-load": "^3.0.0", + "pug-parser": "^6.0.0", + "pug-runtime": "^3.0.1", + "pug-strip-comments": "^2.0.0" + } + }, + "node_modules/pug-attrs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-3.0.0.tgz", + "integrity": "sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==", + "dev": true, + "license": "MIT", + "dependencies": { + "constantinople": "^4.0.1", + "js-stringify": "^1.0.2", + "pug-runtime": "^3.0.0" + } + }, + "node_modules/pug-code-gen": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-3.0.4.tgz", + "integrity": "sha512-6okWYIKdasTyXICyEtvobmTZAVX57JkzgzIi4iRJlin8kmhG+Xry2dsus+Mun/nGCn6F2U49haHI5mkELXB14g==", + "dev": true, + "license": "MIT", + "dependencies": { + "constantinople": "^4.0.1", + "doctypes": "^1.1.0", + "js-stringify": "^1.0.2", + "pug-attrs": "^3.0.0", + "pug-error": "^2.1.0", + "pug-runtime": "^3.0.1", + "void-elements": "^3.1.0", + "with": "^7.0.0" + } + }, + "node_modules/pug-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-2.1.0.tgz", + "integrity": "sha512-lv7sU9e5Jk8IeUheHata6/UThZ7RK2jnaaNztxfPYUY+VxZyk/ePVaNZ/vwmH8WqGvDz3LrNYt/+gA55NDg6Pg==", + "dev": true, + "license": "MIT" + }, + "node_modules/pug-filters": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-4.0.0.tgz", + "integrity": "sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "constantinople": "^4.0.1", + "jstransformer": "1.0.0", + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0", + "resolve": "^1.15.1" + } + }, + "node_modules/pug-lexer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-5.0.1.tgz", + "integrity": "sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==", + "dev": true, + "license": "MIT", + "dependencies": { + "character-parser": "^2.2.0", + "is-expression": "^4.0.0", + "pug-error": "^2.0.0" + } + }, + "node_modules/pug-linker": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-4.0.0.tgz", + "integrity": "sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0" + } + }, + "node_modules/pug-load": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-3.0.0.tgz", + "integrity": "sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "object-assign": "^4.1.1", + "pug-walk": "^2.0.0" + } + }, + "node_modules/pug-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-6.0.0.tgz", + "integrity": "sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pug-error": "^2.0.0", + "token-stream": "1.0.0" + } + }, + "node_modules/pug-runtime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-3.0.1.tgz", + "integrity": "sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==", + "dev": true, + "license": "MIT" + }, + "node_modules/pug-strip-comments": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz", + "integrity": "sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "pug-error": "^2.0.0" + } + }, + "node_modules/pug-walk": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz", + "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/reprism": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/reprism/-/reprism-0.0.11.tgz", + "integrity": "sha512-VsxDR5QxZo08M/3nRypNlScw5r3rKeSOPdU/QhDmu3Ai3BJxHn/qgfXGWQp/tAxUtzwYNo9W6997JZR0tPLZsA==", + "dev": true, + "license": "MIT" + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/spark-md5": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/spark-md5/-/spark-md5-3.0.2.tgz", + "integrity": "sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw==", + "dev": true, + "license": "(WTFPL OR MIT)" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/token-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", + "integrity": "sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg==", + "dev": true, + "license": "MIT" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", + "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/with": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz", + "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.9.6", + "@babel/types": "^7.9.6", + "assert-never": "^1.2.1", + "babel-walk": "3.0.0-canary-5" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/package.json b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/package.json @@ -0,0 +1,24 @@ +{ + "name": "loop-bench-iuvnueix", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "build": "tsc", + "watch": "tsc --watch", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "commonjs", + "devDependencies": { + "@eslint/js": "^10.0.1", + "@playwright/test": "^1.59.1", + "@types/node": "^25.5.2", + "eslint": "^10.2.0", + "html-validate": "^10.11.3", + "jscpd": "^4.0.8", + "typescript": "^6.0.2" + } +} diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/report/jscpd-report.json b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/report/jscpd-report.json @@ -0,0 +1,144 @@ +{ + "statistics": { + "detectionDate": "2026-04-04T21:11:27.619Z", + "formats": { + "typescript": { + "sources": { + "src/tetris.ts": { + "lines": 672, + "tokens": 7120, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "total": { + "lines": 672, + "tokens": 7120, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "javascript": { + "sources": { + "dist/tetris.js": { + "lines": 576, + "tokens": 6577, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "total": { + "lines": 576, + "tokens": 6577, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "json": { + "sources": { + "tsconfig.json": { + "lines": 12, + "tokens": 80, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + }, + "package.json": { + "lines": 23, + "tokens": 146, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "total": { + "lines": 35, + "tokens": 226, + "sources": 2, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "markup": { + "sources": { + "index.html": { + "lines": 197, + "tokens": 1497, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "total": { + "lines": 197, + "tokens": 1497, + "sources": 1, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + } + }, + "total": { + "lines": 1480, + "tokens": 15420, + "sources": 5, + "clones": 0, + "duplicatedLines": 0, + "duplicatedTokens": 0, + "percentage": 0, + "percentageTokens": 0, + "newDuplicatedLines": 0, + "newClones": 0 + } + }, + "duplicates": [] +} +\ No newline at end of file diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/src/tetris.ts b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/src/tetris.ts @@ -0,0 +1,673 @@ +// ─── Constants ──────────────────────────────────────────────────────────────── + +const COLS = 10; +const ROWS = 20; +const CELL = 32; // px per grid cell +const DAS = 170; // Delayed Auto Shift (ms) +const ARR = 50; // Auto Repeat Rate (ms) +const FLASH_MS = 280; // Line-clear flash duration (ms) +const LOCK_DELAY = 500; // Lock delay after piece hits floor (ms) + +// ─── Piece Definitions ──────────────────────────────────────────────────────── + +const COLORS: Record<string, string> = { + I: '#00c8ff', + O: '#ffd700', + T: '#c040f0', + S: '#10e060', + Z: '#ff3030', + J: '#3878ff', + L: '#ff8800', +}; + +// All pieces use square bounding boxes (2×2, 3×3, or 4×4) +const SHAPES: Record<string, number[][]> = { + I: [[0,0,0,0], + [1,1,1,1], + [0,0,0,0], + [0,0,0,0]], + O: [[1,1], + [1,1]], + T: [[0,1,0], + [1,1,1], + [0,0,0]], + S: [[0,1,1], + [1,1,0], + [0,0,0]], + Z: [[1,1,0], + [0,1,1], + [0,0,0]], + J: [[1,0,0], + [1,1,1], + [0,0,0]], + L: [[0,0,1], + [1,1,1], + [0,0,0]], +}; + +// Standard speed curve (ms per gravity drop) for levels 1-13 +const SPEED_CURVE = [800,717,633,550,467,383,300,217,133,100,83,67,50]; + +// ─── Types ──────────────────────────────────────────────────────────────────── + +type Cell = string | null; +type Board = Cell[][]; + +interface Piece { + type: string; + shape: number[][]; + color: string; + x: number; + y: number; +} + +// ─── Utility Functions ──────────────────────────────────────────────────────── + +function createBoard(): Board { + return Array.from({ length: ROWS }, () => new Array<Cell>(COLS).fill(null)); +} + +/** Rotate matrix 90° clockwise */ +function rotateCW(m: number[][]): number[][] { + return m[0].map((_, i) => m.map(row => row[i]).reverse()); +} + +/** Rotate matrix 90° counter-clockwise */ +function rotateCCW(m: number[][]): number[][] { + return m[0].map((_, i) => m.map(row => row[row.length - 1 - i])).reverse(); +} + +/** Spawn a new piece centered at the top */ +function makePiece(type: string): Piece { + const shape = SHAPES[type].map(r => [...r]); + return { + type, + shape, + color: COLORS[type], + x: Math.floor((COLS - shape[0].length) / 2), + y: 0, + }; +} + +/** Returns true if the piece (optionally with offsets / alternate shape) fits on the board */ +function fits(board: Board, piece: Piece, dx = 0, dy = 0, shape = piece.shape): boolean { + for (let r = 0; r < shape.length; r++) { + for (let c = 0; c < shape[r].length; c++) { + if (!shape[r][c]) continue; + const nx = piece.x + c + dx; + const ny = piece.y + r + dy; + if (nx < 0 || nx >= COLS || ny >= ROWS) return false; + if (ny >= 0 && board[ny][nx]) return false; + } + } + return true; +} + +/** Place the piece onto the board, returning a new board */ +function stamp(board: Board, piece: Piece): Board { + const b = board.map(r => [...r]); + piece.shape.forEach((row, r) => { + row.forEach((cell, c) => { + if (cell) { + const ny = piece.y + r; + const nx = piece.x + c; + if (ny >= 0 && ny < ROWS && nx >= 0 && nx < COLS) b[ny][nx] = piece.color; + } + }); + }); + return b; +} + +/** Return indices of completely filled rows */ +function fullRows(board: Board): number[] { + return board.reduce<number[]>((acc, row, i) => { + if (row.every(c => c !== null)) acc.push(i); + return acc; + }, []); +} + +/** Remove the specified rows and prepend empty rows at the top */ +function removeRows(board: Board, rows: number[]): Board { + const kept = board.filter((_, i) => !rows.includes(i)); + const empties = Array.from({ length: rows.length }, () => new Array<Cell>(COLS).fill(null)); + return [...empties, ...kept]; +} + +const SCORE_TABLE = [0, 100, 300, 500, 800]; + +function lineScore(n: number, level: number): number { + return (SCORE_TABLE[n] ?? 0) * level; +} + +function dropInterval(level: number): number { + return SPEED_CURVE[Math.min(level - 1, SPEED_CURVE.length - 1)]; +} + +// ─── 7-Bag Randomiser ───────────────────────────────────────────────────────── + +class Bag { + private items: string[] = []; + + private refill(): void { + this.items = Object.keys(SHAPES); + for (let i = this.items.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [this.items[i], this.items[j]] = [this.items[j], this.items[i]]; + } + } + + draw(): string { + if (this.items.length === 0) this.refill(); + return this.items.pop()!; + } +} + +// ─── Main Game Class ────────────────────────────────────────────────────────── + +class TetrisGame { + // Canvases + private readonly boardCanvas: HTMLCanvasElement; + private readonly boardCtx: CanvasRenderingContext2D; + private readonly nextCanvas: HTMLCanvasElement; + private readonly nextCtx: CanvasRenderingContext2D; + private readonly holdCanvas: HTMLCanvasElement; + private readonly holdCtx: CanvasRenderingContext2D; + + // Game state + private bag!: Bag; + private board!: Board; + private current!: Piece; + private next!: Piece; + private held: Piece | null = null; + private canHold!: boolean; + private score!: number; + private lines!: number; + private level!: number; + private highScore: number; + private gameOver!: boolean; + private paused!: boolean; + + // Timing + private lastTime = 0; + private dropCounter!: number; + private lockTimer!: number; // counts toward lock delay + private onFloor!: boolean; // true when piece is resting on something + + // Line-clear flash + private flashTimer = 0; + private flashRows: number[] = []; + private pendingClr = 0; + private pendingScr = 0; + + // Input + private keysDown = new Set<string>(); + private dasTimer = 0; + private arrTimer = 0; + + // ── Constructor ───────────────────────────────────────────────────────────── + + constructor() { + const get = <T extends HTMLElement>(id: string) => + document.getElementById(id) as T; + + this.boardCanvas = get<HTMLCanvasElement>('board'); + this.boardCtx = this.boardCanvas.getContext('2d')!; + this.nextCanvas = get<HTMLCanvasElement>('next'); + this.nextCtx = this.nextCanvas.getContext('2d')!; + this.holdCanvas = get<HTMLCanvasElement>('hold'); + this.holdCtx = this.holdCanvas.getContext('2d')!; + + this.boardCanvas.width = COLS * CELL; + this.boardCanvas.height = ROWS * CELL; + this.nextCanvas.width = 4 * CELL; + this.nextCanvas.height = 4 * CELL; + this.holdCanvas.width = 4 * CELL; + this.holdCanvas.height = 4 * CELL; + + this.highScore = parseInt(localStorage.getItem('tetris-hs') ?? '0', 10); + + this.setupInput(); + this.init(); + requestAnimationFrame(t => this.loop(t)); + } + + // ── Initialisation ────────────────────────────────────────────────────────── + + private init(): void { + this.bag = new Bag(); + this.board = createBoard(); + this.current = makePiece(this.bag.draw()); + this.next = makePiece(this.bag.draw()); + this.held = null; + this.canHold = true; + this.score = 0; + this.lines = 0; + this.level = 1; + this.gameOver = false; + this.paused = false; + this.dropCounter = 0; + this.lockTimer = 0; + this.onFloor = false; + this.flashTimer = 0; + this.flashRows = []; + this.pendingClr = 0; + this.pendingScr = 0; + this.updateHUD(); + } + + // ── Input ─────────────────────────────────────────────────────────────────── + + private setupInput(): void { + // Prevent page scroll for game keys + window.addEventListener('keydown', e => { + if (['ArrowUp','ArrowDown','ArrowLeft','ArrowRight','Space'].includes(e.code)) + e.preventDefault(); + + if (!e.repeat) { + this.keysDown.add(e.code); + this.onFirstPress(e.code); + } + }, { passive: false }); + + window.addEventListener('keyup', e => { + this.keysDown.delete(e.code); + if (e.code === 'ArrowLeft' || e.code === 'ArrowRight') { + this.dasTimer = 0; + this.arrTimer = 0; + } + }); + + // Clear all held keys when window loses focus (prevents stuck movement) + window.addEventListener('blur', () => { + this.keysDown.clear(); + this.dasTimer = 0; + this.arrTimer = 0; + }); + } + + private onFirstPress(code: string): void { + if (code === 'KeyP') { + if (!this.gameOver) this.paused = !this.paused; + return; + } + + if (this.gameOver) { + if (code === 'Enter' || code === 'KeyR') this.init(); + return; + } + + if (this.paused || this.flashTimer > 0) return; + + switch (code) { + case 'ArrowLeft': + this.move(-1); + this.dasTimer = 0; + this.arrTimer = 0; + break; + case 'ArrowRight': + this.move(1); + this.dasTimer = 0; + this.arrTimer = 0; + break; + case 'ArrowDown': this.softDrop(); break; + case 'ArrowUp': this.rotate(1); break; + case 'KeyZ': this.rotate(-1); break; + case 'KeyX': this.rotate(1); break; + case 'Space': this.hardDrop(); break; + case 'ShiftLeft': + case 'ShiftRight': + case 'KeyC': this.holdPiece(); break; + } + } + + // ── Actions ───────────────────────────────────────────────────────────────── + + private move(dx: number): boolean { + if (fits(this.board, this.current, dx)) { + this.current.x += dx; + if (this.onFloor) this.lockTimer = 0; // reset lock delay on successful move + return true; + } + return false; + } + + private softDrop(): void { + if (fits(this.board, this.current, 0, 1)) { + this.current.y++; + this.score++; + this.dropCounter = 0; + this.lockTimer = 0; + this.updateHUD(); + } + } + + private hardDrop(): void { + let n = 0; + while (fits(this.board, this.current, 0, 1)) { + this.current.y++; + n++; + } + this.score += n * 2; + this.lock(); + } + + private rotate(dir: 1 | -1): void { + const rotated = dir === 1 ? rotateCW(this.current.shape) : rotateCCW(this.current.shape); + // Wall-kick offsets: try center, then nudge left/right, then further + const kicks = [0, -1, 1, -2, 2]; + for (const kick of kicks) { + if (fits(this.board, this.current, kick, 0, rotated)) { + this.current.shape = rotated; + this.current.x += kick; + if (this.onFloor) this.lockTimer = 0; // reset lock delay on successful rotate + return; + } + } + } + + private holdPiece(): void { + if (!this.canHold) return; + const savedType = this.current.type; + if (this.held) { + this.current = makePiece(this.held.type); + } else { + this.current = this.next; + this.next = makePiece(this.bag.draw()); + } + this.held = makePiece(savedType); + this.canHold = false; + this.onFloor = false; + this.lockTimer = 0; + this.dropCounter = 0; + } + + private lock(): void { + this.board = stamp(this.board, this.current); + this.canHold = true; + this.onFloor = false; + + const fr = fullRows(this.board); + if (fr.length > 0) { + this.flashRows = fr; + this.flashTimer = FLASH_MS; + this.pendingClr = fr.length; + this.pendingScr = lineScore(fr.length, this.level); + } else { + this.spawnNext(); + } + } + + private spawnNext(): void { + this.current = this.next; + this.next = makePiece(this.bag.draw()); + this.dropCounter = 0; + this.lockTimer = 0; + this.onFloor = false; + + if (!fits(this.board, this.current)) { + this.gameOver = true; + if (this.score > this.highScore) { + this.highScore = this.score; + localStorage.setItem('tetris-hs', String(this.highScore)); + } + } + this.updateHUD(); + } + + // ── HUD ───────────────────────────────────────────────────────────────────── + + private updateHUD(): void { + const el = (id: string) => document.getElementById(id)!; + el('score').textContent = this.score.toLocaleString(); + el('lines').textContent = String(this.lines); + el('level').textContent = String(this.level); + el('high-score').textContent = Math.max(this.highScore, this.score).toLocaleString(); + } + + // ── Rendering ──────────────────────────────────────────────────────────────── + + private ghostOffset(): number { + let dy = 0; + while (fits(this.board, this.current, 0, dy + 1)) dy++; + return dy; + } + + private drawBlock( + ctx: CanvasRenderingContext2D, + x: number, + y: number, + color: string, + alpha = 1, + ): void { + const px = x * CELL; + const py = y * CELL; + const m = 1; // margin between cells + const s = CELL - m * 2; + + ctx.globalAlpha = alpha; + + // Base fill + ctx.fillStyle = color; + ctx.fillRect(px + m, py + m, s, s); + + // Gradient sheen + const g = ctx.createLinearGradient(px, py, px, py + CELL); + g.addColorStop(0, 'rgba(255,255,255,0.38)'); + g.addColorStop(0.40, 'rgba(255,255,255,0.04)'); + g.addColorStop(1, 'rgba(0,0,0,0.22)'); + ctx.fillStyle = g; + ctx.fillRect(px + m, py + m, s, s); + + // Top-left highlight bevel + ctx.fillStyle = 'rgba(255,255,255,0.55)'; + ctx.fillRect(px + m, py + m, s, 3); // top edge + ctx.fillRect(px + m, py + m, 3, s); // left edge + + // Bottom-right shadow bevel + ctx.fillStyle = 'rgba(0,0,0,0.35)'; + ctx.fillRect(px + m, py + CELL - m - 3, s, 3); // bottom edge + ctx.fillRect(px + CELL - m - 3, py + m, 3, s); // right edge + + ctx.globalAlpha = 1; + } + + private renderBoard(): void { + const ctx = this.boardCtx; + const W = this.boardCanvas.width; + const H = this.boardCanvas.height; + + // Background + ctx.fillStyle = '#0d1117'; + ctx.fillRect(0, 0, W, H); + + // Subtle grid lines + ctx.strokeStyle = 'rgba(255,255,255,0.04)'; + ctx.lineWidth = 1; + for (let c = 0; c <= COLS; c++) { + ctx.beginPath(); ctx.moveTo(c * CELL, 0); ctx.lineTo(c * CELL, H); ctx.stroke(); + } + for (let r = 0; r <= ROWS; r++) { + ctx.beginPath(); ctx.moveTo(0, r * CELL); ctx.lineTo(W, r * CELL); ctx.stroke(); + } + + // Placed board cells (with flash effect on clearing rows) + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + const cell = this.board[r][c]; + if (!cell) continue; + + if (this.flashRows.includes(r)) { + // Alternate white / colour every ~35ms + const t = this.flashTimer / FLASH_MS; + const flash = Math.floor(t * 8) % 2 === 0; + this.drawBlock(ctx, c, r, flash ? '#ffffff' : cell); + } else { + this.drawBlock(ctx, c, r, cell); + } + } + } + + // Ghost & active piece — hidden during flash animation + if (this.flashTimer <= 0 && !this.gameOver) { + const gDy = this.ghostOffset(); + + // Ghost + if (gDy > 0) { + this.current.shape.forEach((row, r) => { + row.forEach((cell, c) => { + const gy = this.current.y + r + gDy; + if (cell && gy >= 0) { + this.drawBlock(ctx, this.current.x + c, gy, this.current.color, 0.18); + } + }); + }); + } + + // Active piece + this.current.shape.forEach((row, r) => { + row.forEach((cell, c) => { + const py = this.current.y + r; + if (cell && py >= 0) { + this.drawBlock(ctx, this.current.x + c, py, this.current.color); + } + }); + }); + } + + // ── Overlays ────────────────────────────────────────────────────────────── + + if (this.paused) { + ctx.fillStyle = 'rgba(0,0,0,0.72)'; + ctx.fillRect(0, 0, W, H); + this.cText(ctx, 'PAUSED', W/2, H/2 - 22, '#ffffff', 'bold 34px monospace'); + this.cText(ctx, 'P — resume', W/2, H/2 + 18, '#778899', '15px monospace'); + } + + if (this.gameOver) { + ctx.fillStyle = 'rgba(0,0,0,0.80)'; + ctx.fillRect(0, 0, W, H); + this.cText(ctx, 'GAME OVER', W/2, H/2 - 46, '#ff4040', 'bold 36px monospace'); + this.cText(ctx, `Score: ${this.score.toLocaleString()}`, W/2, H/2 - 2, '#e0e0e0', '20px monospace'); + this.cText(ctx, 'Enter — play again', W/2, H/2 + 38, '#778899', '14px monospace'); + } + } + + private cText( + ctx: CanvasRenderingContext2D, + text: string, + x: number, y: number, + color: string, font: string, + ): void { + ctx.save(); + ctx.fillStyle = color; + ctx.font = font; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.fillText(text, x, y); + ctx.restore(); + } + + private renderPreview( + ctx: CanvasRenderingContext2D, + canvas: HTMLCanvasElement, + piece: Piece | null, + dimmed = false, + ): void { + ctx.fillStyle = '#0d1117'; + ctx.fillRect(0, 0, canvas.width, canvas.height); + if (!piece) return; + + const s = piece.shape; + const ox = Math.floor((4 - s[0].length) / 2); + const oy = Math.floor((4 - s.length) / 2); + s.forEach((row, r) => { + row.forEach((cell, c) => { + if (cell) this.drawBlock(ctx, ox + c, oy + r, piece.color, dimmed ? 0.38 : 1); + }); + }); + } + + // ── Game Loop ──────────────────────────────────────────────────────────────── + + private loop(time: number): void { + const dt = Math.min(time - (this.lastTime || time), 100); // cap at 100ms + this.lastTime = time; + + if (!this.paused && !this.gameOver) { + // ── Flash timer (line-clear animation) ────────────────────────────────── + if (this.flashTimer > 0) { + this.flashTimer -= dt; + if (this.flashTimer <= 0) { + this.flashTimer = 0; + this.board = removeRows(this.board, this.flashRows); + this.flashRows = []; + this.lines += this.pendingClr; + this.score += this.pendingScr; + this.level = Math.floor(this.lines / 10) + 1; + this.pendingClr = 0; + this.pendingScr = 0; + this.spawnNext(); + } + } else { + // ── DAS / ARR horizontal movement ───────────────────────────────────── + const leftDown = this.keysDown.has('ArrowLeft'); + const rightDown = this.keysDown.has('ArrowRight'); + + if (leftDown || rightDown) { + this.dasTimer += dt; + if (this.dasTimer > DAS) { + this.arrTimer += dt; + if (this.arrTimer >= ARR) { + const steps = Math.floor(this.arrTimer / ARR); + this.arrTimer -= steps * ARR; + for (let i = 0; i < steps; i++) { + if (leftDown && !rightDown) this.move(-1); + if (rightDown && !leftDown) this.move(1); + } + } + } + } + + // ── Gravity ─────────────────────────────────────────────────────────── + const softDropping = this.keysDown.has('ArrowDown'); + const interval = softDropping ? 50 : dropInterval(this.level); + + this.dropCounter += dt; + if (this.dropCounter >= interval) { + this.dropCounter = 0; + if (fits(this.board, this.current, 0, 1)) { + this.current.y++; + this.onFloor = false; + this.lockTimer = 0; + if (softDropping) { + this.score++; + this.updateHUD(); + } + } else { + // Piece can't fall — mark as on floor; lock delay handled below + this.onFloor = true; + } + } + + // Accumulate lock delay every frame while piece rests on something + if (this.onFloor && !fits(this.board, this.current, 0, 1)) { + this.lockTimer += dt; + if (this.lockTimer >= LOCK_DELAY) { + this.lock(); + } + } + } + } + + // ── Render ──────────────────────────────────────────────────────────────── + this.renderBoard(); + this.renderPreview(this.nextCtx, this.nextCanvas, this.next); + this.renderPreview(this.holdCtx, this.holdCanvas, this.held, !this.canHold); + + requestAnimationFrame(t => this.loop(t)); + } +} + +// ─── Bootstrap ─────────────────────────────────────────────────────────────── + +window.addEventListener('load', () => new TetrisGame()); diff --git a/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/tsconfig.json b/dashboard/public/artifacts/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ES2017", + "module": "none", + "ignoreDeprecations": "6.0", + "strict": true, + "rootDir": "src", + "outDir": "dist", + "noEmitOnError": true, + "lib": ["ES2017", "DOM"] + }, + "include": ["src/**/*.ts"] +} diff --git a/results/analysis/main_effects_cost.json b/results/analysis/main_effects_cost.json @@ -1,262 +1,267 @@ { + "model": { + "values": { + "haiku": { + "mean": 0.255, + "effect": -0.0463, + "n": 59 + }, + "opus": { + "mean": 0.573, + "effect": 0.2717, + "n": 3 + }, + "sonnet": { + "mean": 0.7806, + "effect": 0.4793, + "n": 4 + } + }, + "spread": 0.5256 + }, "language": { "values": { "javascript": { "mean": 0.0491, - "effect": -0.2064, + "effect": -0.2522, "n": 3 }, "typescript": { - "mean": 0.2744, - "effect": 0.0189, - "n": 55 + "mean": 0.3232, + "effect": 0.0219, + "n": 60 }, "unspecified": { "mean": 0.1149, - "effect": -0.1406, - "n": 3 - } - }, - "spread": 0.2253 - }, - "human_language": { - "values": { - "en": { - "mean": 0.249, - "effect": -0.0065, - "n": 58 - }, - "es": { - "mean": 0.3807, - "effect": 0.1252, + "effect": -0.1864, "n": 3 } }, - "spread": 0.1317 + "spread": 0.2741 }, "claude_version": { "values": { "2.1.91 (Claude Code)": { "mean": 0.2695, - "effect": 0.014, + "effect": -0.0317, "n": 53 }, "2.1.92 (Claude Code)": { - "mean": 0.1625, - "effect": -0.093, - "n": 8 + "mean": 0.4307, + "effect": 0.1294, + "n": 13 } }, - "spread": 0.107 + "spread": 0.1612 }, - "effort": { + "context_file": { "values": { - "high": { - "mean": 0.2514, - "effect": -0.0041, - "n": 58 + "none": { + "mean": 0.3095, + "effect": 0.0082, + "n": 60 }, - "max": { - "mean": 0.3342, - "effect": 0.0787, + "provided": { + "mean": 0.2192, + "effect": -0.0821, + "n": 6 + } + }, + "spread": 0.0903 + }, + "human_language": { + "values": { + "en": { + "mean": 0.2975, + "effect": -0.0038, + "n": 63 + }, + "es": { + "mean": 0.3807, + "effect": 0.0794, "n": 3 } }, - "spread": 0.0828 + "spread": 0.0832 }, - "tool_write": { + "prompt_style": { "values": { - "off": { - "mean": 0.3121, - "effect": 0.0566, - "n": 6 + "detailed": { + "mean": 0.2444, + "effect": -0.0568, + "n": 4 }, - "on": { - "mean": 0.2493, - "effect": -0.0062, - "n": 55 + "simple": { + "mean": 0.305, + "effect": 0.0037, + "n": 62 } }, - "spread": 0.0628 + "spread": 0.0606 }, - "tool_edit": { + "max_budget": { "values": { - "off": { - "mean": 0.3049, - "effect": 0.0494, + "high": { + "mean": 0.2475, + "effect": -0.0538, "n": 6 }, - "on": { - "mean": 0.2501, - "effect": -0.0054, - "n": 55 + "low": { + "mean": 0.3067, + "effect": 0.0054, + "n": 60 } }, - "spread": 0.0548 + "spread": 0.0592 }, - "tool_grep": { + "sub_agents": { "values": { "off": { - "mean": 0.2971, - "effect": 0.0416, - "n": 6 + "mean": 0.2582, + "effect": -0.0431, + "n": 8 }, "on": { - "mean": 0.251, - "effect": -0.0045, - "n": 55 + "mean": 0.3072, + "effect": 0.0059, + "n": 58 } }, - "spread": 0.0461 + "spread": 0.049 }, - "tool_read": { + "tool_glob": { "values": { "off": { - "mean": 0.2951, - "effect": 0.0396, - "n": 7 + "mean": 0.2611, + "effect": -0.0402, + "n": 6 }, "on": { - "mean": 0.2504, - "effect": -0.0051, - "n": 54 + "mean": 0.3053, + "effect": 0.004, + "n": 60 } }, - "spread": 0.0447 + "spread": 0.0442 }, - "context_file": { + "effort": { "values": { - "none": { - "mean": 0.2595, - "effect": 0.004, - "n": 55 + "high": { + "mean": 0.2997, + "effect": -0.0016, + "n": 63 }, - "provided": { - "mean": 0.2192, - "effect": -0.0363, - "n": 6 + "max": { + "mean": 0.3342, + "effect": 0.0329, + "n": 3 } }, - "spread": 0.0403 + "spread": 0.0345 }, - "web_search": { + "linter": { "values": { "off": { - "mean": 0.2871, - "effect": 0.0316, - "n": 8 + "mean": 0.2832, + "effect": -0.018, + "n": 9 }, "on": { - "mean": 0.2507, - "effect": -0.0048, - "n": 53 + "mean": 0.3041, + "effect": 0.0028, + "n": 57 } }, - "spread": 0.0364 + "spread": 0.0209 }, "playwright": { "values": { "off": { "mean": 0.2842, - "effect": 0.0287, + "effect": -0.0171, "n": 8 }, "on": { - "mean": 0.2512, - "effect": -0.0043, - "n": 53 + "mean": 0.3036, + "effect": 0.0024, + "n": 58 } }, - "spread": 0.033 + "spread": 0.0194 }, - "linter": { + "web_search": { "values": { "off": { - "mean": 0.2832, - "effect": 0.0277, - "n": 9 + "mean": 0.2871, + "effect": -0.0142, + "n": 8 }, "on": { - "mean": 0.2507, - "effect": -0.0048, - "n": 52 - } - }, - "spread": 0.0325 - }, - "model": { - "values": { - "haiku": { - "mean": 0.255, - "effect": -0.0005, - "n": 59 - }, - "sonnet": { - "mean": 0.2709, - "effect": 0.0154, - "n": 2 + "mean": 0.3032, + "effect": 0.002, + "n": 58 } }, - "spread": 0.0159 + "spread": 0.0161 }, - "prompt_style": { + "tool_write": { "values": { - "detailed": { - "mean": 0.2444, - "effect": -0.0111, - "n": 4 + "off": { + "mean": 0.3121, + "effect": 0.0108, + "n": 6 }, - "simple": { - "mean": 0.2563, - "effect": 0.0008, - "n": 57 + "on": { + "mean": 0.3002, + "effect": -0.0011, + "n": 60 } }, "spread": 0.0119 }, - "max_budget": { + "tool_read": { "values": { - "high": { - "mean": 0.2475, - "effect": -0.008, - "n": 6 + "off": { + "mean": 0.2951, + "effect": -0.0062, + "n": 7 }, - "low": { - "mean": 0.2564, - "effect": 0.0009, - "n": 55 + "on": { + "mean": 0.302, + "effect": 0.0007, + "n": 59 } }, - "spread": 0.0089 + "spread": 0.0069 }, - "tool_glob": { + "tool_grep": { "values": { "off": { - "mean": 0.2611, - "effect": 0.0056, + "mean": 0.2971, + "effect": -0.0042, "n": 6 }, "on": { - "mean": 0.2549, - "effect": -0.0006, - "n": 55 + "mean": 0.3017, + "effect": 0.0004, + "n": 60 } }, - "spread": 0.0062 + "spread": 0.0046 }, - "sub_agents": { + "tool_edit": { "values": { "off": { - "mean": 0.2582, - "effect": 0.0027, - "n": 8 + "mean": 0.3049, + "effect": 0.0036, + "n": 6 }, "on": { - "mean": 0.2551, + "mean": 0.3009, "effect": -0.0004, - "n": 53 + "n": 60 } }, - "spread": 0.0031 + "spread": 0.004 } } \ No newline at end of file diff --git a/results/analysis/main_effects_score.json b/results/analysis/main_effects_score.json @@ -3,17 +3,17 @@ "values": { "javascript": { "mean": 0.7383, - "effect": 0.1794, + "effect": 0.166, "n": 3 }, "typescript": { - "mean": 0.5528, - "effect": -0.0062, - "n": 55 + "mean": 0.5681, + "effect": -0.0042, + "n": 61 }, "unspecified": { "mean": 0.4927, - "effect": -0.0663, + "effect": -0.0797, "n": 3 } }, @@ -23,240 +23,245 @@ "values": { "haiku": { "mean": 0.5669, - "effect": 0.0079, + "effect": -0.0055, "n": 59 }, + "opus": { + "mean": 0.724, + "effect": 0.1516, + "n": 3 + }, "sonnet": { - "mean": 0.3245, - "effect": -0.2344, - "n": 2 + "mean": 0.5461, + "effect": -0.0263, + "n": 5 } }, - "spread": 0.2424 + "spread": 0.1779 }, "tool_read": { "values": { "off": { "mean": 0.4976, - "effect": -0.0613, + "effect": -0.0747, "n": 7 }, "on": { - "mean": 0.5669, - "effect": 0.0079, - "n": 54 + "mean": 0.5811, + "effect": 0.0087, + "n": 60 + } + }, + "spread": 0.0835 + }, + "human_language": { + "values": { + "en": { + "mean": 0.5757, + "effect": 0.0033, + "n": 64 + }, + "es": { + "mean": 0.5013, + "effect": -0.071, + "n": 3 } }, - "spread": 0.0693 + "spread": 0.0744 }, "context_file": { "values": { "none": { - "mean": 0.5526, - "effect": -0.0063, - "n": 55 + "mean": 0.568, + "effect": -0.0043, + "n": 61 }, "provided": { "mean": 0.6165, - "effect": 0.0576, + "effect": 0.0441, "n": 6 } }, - "spread": 0.0639 + "spread": 0.0485 }, - "human_language": { + "effort": { "values": { - "en": { - "mean": 0.5619, - "effect": 0.003, - "n": 58 + "high": { + "mean": 0.5744, + "effect": 0.002, + "n": 64 }, - "es": { - "mean": 0.5013, - "effect": -0.0576, + "max": { + "mean": 0.5297, + "effect": -0.0427, "n": 3 } }, - "spread": 0.0606 + "spread": 0.0447 }, - "prompt_style": { + "claude_version": { "values": { - "detailed": { - "mean": 0.592, - "effect": 0.0331, - "n": 4 + "2.1.91 (Claude Code)": { + "mean": 0.5631, + "effect": -0.0093, + "n": 53 }, - "simple": { - "mean": 0.5566, - "effect": -0.0023, - "n": 57 + "2.1.92 (Claude Code)": { + "mean": 0.6074, + "effect": 0.0351, + "n": 14 } }, - "spread": 0.0354 + "spread": 0.0443 }, - "playwright": { + "web_search": { "values": { "off": { - "mean": 0.5869, - "effect": 0.028, + "mean": 0.5391, + "effect": -0.0333, "n": 8 }, "on": { - "mean": 0.5547, - "effect": -0.0042, - "n": 53 + "mean": 0.5769, + "effect": 0.0045, + "n": 59 } }, - "spread": 0.0322 + "spread": 0.0378 }, - "tool_grep": { + "linter": { "values": { "off": { - "mean": 0.588, - "effect": 0.0291, - "n": 6 + "mean": 0.5436, + "effect": -0.0287, + "n": 9 }, "on": { - "mean": 0.5558, - "effect": -0.0032, - "n": 55 + "mean": 0.5768, + "effect": 0.0045, + "n": 58 } }, - "spread": 0.0322 + "spread": 0.0332 }, - "claude_version": { + "tool_edit": { "values": { - "2.1.91 (Claude Code)": { - "mean": 0.5631, - "effect": 0.0042, - "n": 53 + "off": { + "mean": 0.5464, + "effect": -0.0259, + "n": 6 }, - "2.1.92 (Claude Code)": { - "mean": 0.5313, - "effect": -0.0276, - "n": 8 + "on": { + "mean": 0.5749, + "effect": 0.0026, + "n": 61 } }, - "spread": 0.0318 + "spread": 0.0285 }, - "effort": { + "prompt_style": { "values": { - "high": { - "mean": 0.5604, - "effect": 0.0015, - "n": 58 + "detailed": { + "mean": 0.592, + "effect": 0.0196, + "n": 4 }, - "max": { - "mean": 0.5297, - "effect": -0.0293, - "n": 3 + "simple": { + "mean": 0.5711, + "effect": -0.0012, + "n": 63 } }, - "spread": 0.0307 + "spread": 0.0209 }, - "web_search": { + "max_budget": { "values": { - "off": { - "mean": 0.5391, - "effect": -0.0199, - "n": 8 + "high": { + "mean": 0.5537, + "effect": -0.0187, + "n": 6 }, - "on": { - "mean": 0.5619, - "effect": 0.003, - "n": 53 + "low": { + "mean": 0.5742, + "effect": 0.0018, + "n": 61 } }, - "spread": 0.0228 + "spread": 0.0205 }, - "tool_glob": { + "tool_grep": { "values": { "off": { - "mean": 0.5794, - "effect": 0.0205, + "mean": 0.588, + "effect": 0.0156, "n": 6 }, "on": { - "mean": 0.5567, - "effect": -0.0022, - "n": 55 + "mean": 0.5708, + "effect": -0.0015, + "n": 61 } }, - "spread": 0.0227 + "spread": 0.0172 }, - "linter": { + "playwright": { "values": { "off": { - "mean": 0.5436, - "effect": -0.0153, - "n": 9 + "mean": 0.5869, + "effect": 0.0146, + "n": 8 }, "on": { - "mean": 0.5616, - "effect": 0.0027, - "n": 52 + "mean": 0.5704, + "effect": -0.002, + "n": 59 } }, - "spread": 0.018 + "spread": 0.0165 }, - "tool_edit": { + "sub_agents": { "values": { "off": { - "mean": 0.5464, - "effect": -0.0125, - "n": 6 + "mean": 0.5655, + "effect": -0.0069, + "n": 8 }, "on": { - "mean": 0.5603, - "effect": 0.0014, - "n": 55 + "mean": 0.5733, + "effect": 0.0009, + "n": 59 } }, - "spread": 0.0139 + "spread": 0.0078 }, - "tool_write": { + "tool_glob": { "values": { "off": { - "mean": 0.5697, - "effect": 0.0108, + "mean": 0.5794, + "effect": 0.0071, "n": 6 }, "on": { - "mean": 0.5577, - "effect": -0.0012, - "n": 55 + "mean": 0.5717, + "effect": -0.0007, + "n": 61 } }, - "spread": 0.012 + "spread": 0.0077 }, - "sub_agents": { + "tool_write": { "values": { "off": { - "mean": 0.5655, - "effect": 0.0066, - "n": 8 - }, - "on": { - "mean": 0.5579, - "effect": -0.001, - "n": 53 - } - }, - "spread": 0.0076 - }, - "max_budget": { - "values": { - "high": { - "mean": 0.5537, - "effect": -0.0053, + "mean": 0.5697, + "effect": -0.0026, "n": 6 }, - "low": { - "mean": 0.5595, - "effect": 0.0006, - "n": 55 + "on": { + "mean": 0.5726, + "effect": 0.0003, + "n": 61 } }, - "spread": 0.0058 + "spread": 0.0029 } } \ No newline at end of file diff --git a/results/analysis/main_effects_turns.json b/results/analysis/main_effects_turns.json @@ -3,260 +3,265 @@ "values": { "javascript": { "mean": 3.6667, - "effect": -21.9891, + "effect": -21.4848, "n": 3 }, "typescript": { - "mean": 27.8364, - "effect": 2.1806, - "n": 55 + "mean": 27.1, + "effect": 1.9485, + "n": 60 }, "unspecified": { "mean": 7.6667, - "effect": -17.9891, + "effect": -17.4848, "n": 3 } }, - "spread": 24.1697 + "spread": 23.4333 }, "model": { "values": { "haiku": { "mean": 26.3898, - "effect": 0.7341, + "effect": 1.2383, "n": 59 }, + "opus": { + "mean": 15.6667, + "effect": -9.4848, + "n": 3 + }, "sonnet": { - "mean": 4.0, - "effect": -21.6557, - "n": 2 + "mean": 14.0, + "effect": -11.1515, + "n": 4 } }, - "spread": 22.3898 + "spread": 12.3898 }, "claude_version": { "values": { "2.1.91 (Claude Code)": { "mean": 27.1509, - "effect": 1.4952, + "effect": 1.9994, "n": 53 }, "2.1.92 (Claude Code)": { - "mean": 15.75, - "effect": -9.9057, - "n": 8 + "mean": 17.0, + "effect": -8.1515, + "n": 13 } }, - "spread": 11.4009 + "spread": 10.1509 }, "tool_write": { "values": { "off": { "mean": 33.3333, - "effect": 7.6776, + "effect": 8.1818, "n": 6 }, "on": { - "mean": 24.8182, - "effect": -0.8376, - "n": 55 + "mean": 24.3333, + "effect": -0.8182, + "n": 60 } }, - "spread": 8.5151 + "spread": 9.0 }, "prompt_style": { "values": { "detailed": { "mean": 18.25, - "effect": -7.4057, + "effect": -6.9015, "n": 4 }, "simple": { - "mean": 26.1754, - "effect": 0.5197, - "n": 57 + "mean": 25.5968, + "effect": 0.4453, + "n": 62 } }, - "spread": 7.9254 + "spread": 7.3468 }, "tool_grep": { "values": { "off": { "mean": 30.8333, - "effect": 5.1776, + "effect": 5.6818, "n": 6 }, "on": { - "mean": 25.0909, - "effect": -0.5648, - "n": 55 + "mean": 24.5833, + "effect": -0.5682, + "n": 60 } }, - "spread": 5.7424 + "spread": 6.25 }, "effort": { "values": { "high": { - "mean": 25.3793, - "effect": -0.2764, - "n": 58 + "mean": 24.873, + "effect": -0.2785, + "n": 63 }, "max": { "mean": 31.0, - "effect": 5.3443, + "effect": 5.8485, "n": 3 } }, - "spread": 5.6207 + "spread": 6.127 }, "human_language": { "values": { "en": { - "mean": 25.3793, - "effect": -0.2764, - "n": 58 + "mean": 24.873, + "effect": -0.2785, + "n": 63 }, "es": { "mean": 31.0, - "effect": 5.3443, + "effect": 5.8485, "n": 3 } }, - "spread": 5.6207 + "spread": 6.127 }, "max_budget": { "values": { "high": { "mean": 30.6667, - "effect": 5.0109, + "effect": 5.5152, "n": 6 }, "low": { - "mean": 25.1091, - "effect": -0.5466, - "n": 55 + "mean": 24.6, + "effect": -0.5515, + "n": 60 } }, - "spread": 5.5576 + "spread": 6.0667 }, "playwright": { "values": { "off": { "mean": 29.5, - "effect": 3.8443, + "effect": 4.3485, "n": 8 }, "on": { - "mean": 25.0755, - "effect": -0.5803, - "n": 53 + "mean": 24.5517, + "effect": -0.5998, + "n": 58 } }, - "spread": 4.4245 + "spread": 4.9483 }, "web_search": { "values": { "off": { "mean": 29.375, - "effect": 3.7193, + "effect": 4.2235, "n": 8 }, "on": { - "mean": 25.0943, - "effect": -0.5614, - "n": 53 + "mean": 24.569, + "effect": -0.5825, + "n": 58 } }, - "spread": 4.2807 + "spread": 4.806 }, "tool_glob": { "values": { "off": { "mean": 29.5, - "effect": 3.8443, + "effect": 4.3485, "n": 6 }, "on": { - "mean": 25.2364, - "effect": -0.4194, - "n": 55 + "mean": 24.7167, + "effect": -0.4348, + "n": 60 } }, - "spread": 4.2636 + "spread": 4.7833 }, "tool_read": { "values": { "off": { "mean": 28.1429, - "effect": 2.4871, + "effect": 2.9913, "n": 7 }, "on": { - "mean": 25.3333, - "effect": -0.3224, - "n": 54 + "mean": 24.7966, + "effect": -0.3549, + "n": 59 } }, - "spread": 2.8096 + "spread": 3.3463 }, "context_file": { "values": { "none": { - "mean": 25.4, - "effect": -0.2557, - "n": 55 + "mean": 24.8667, + "effect": -0.2848, + "n": 60 }, "provided": { "mean": 28.0, - "effect": 2.3443, + "effect": 2.8485, "n": 6 } }, - "spread": 2.6 + "spread": 3.1333 }, "tool_edit": { "values": { "off": { "mean": 28.0, - "effect": 2.3443, + "effect": 2.8485, "n": 6 }, "on": { - "mean": 25.4, - "effect": -0.2557, - "n": 55 + "mean": 24.8667, + "effect": -0.2848, + "n": 60 } }, - "spread": 2.6 + "spread": 3.1333 }, "sub_agents": { "values": { "off": { "mean": 27.75, - "effect": 2.0943, + "effect": 2.5985, "n": 8 }, "on": { - "mean": 25.3396, - "effect": -0.3161, - "n": 53 + "mean": 24.7931, + "effect": -0.3584, + "n": 58 } }, - "spread": 2.4104 + "spread": 2.9569 }, "linter": { "values": { "off": { "mean": 25.3333, - "effect": -0.3224, + "effect": 0.1818, "n": 9 }, "on": { - "mean": 25.7115, - "effect": 0.0558, - "n": 52 + "mean": 25.1228, + "effect": -0.0287, + "n": 57 } }, - "spread": 0.3782 + "spread": 0.2105 } } \ No newline at end of file diff --git a/results/analysis/main_effects_wall_time.json b/results/analysis/main_effects_wall_time.json @@ -1,262 +1,267 @@ { - "language": { - "values": { - "javascript": { - "mean": 42.0, - "effect": -175.4426, - "n": 3 - }, - "typescript": { - "mean": 233.8727, - "effect": 16.4301, - "n": 55 - }, - "unspecified": { - "mean": 91.6667, - "effect": -125.776, - "n": 3 - } - }, - "spread": 191.8727 - }, "model": { "values": { "haiku": { "mean": 211.5085, - "effect": -5.9341, + "effect": -31.9691, "n": 59 }, + "opus": { + "mean": 203.0, + "effect": -40.4776, + "n": 3 + }, "sonnet": { - "mean": 392.5, - "effect": 175.0574, - "n": 2 + "mean": 645.0, + "effect": 401.5224, + "n": 5 } }, - "spread": 180.9915 + "spread": 442.0 }, - "human_language": { + "language": { "values": { - "en": { - "mean": 213.6034, - "effect": -3.8392, - "n": 58 - }, - "es": { - "mean": 291.6667, - "effect": 74.224, + "javascript": { + "mean": 42.0, + "effect": -201.4776, "n": 3 - } - }, - "spread": 78.0633 - }, - "tool_read": { - "values": { - "off": { - "mean": 280.7143, - "effect": 63.2717, - "n": 7 }, - "on": { - "mean": 209.2407, - "effect": -8.2019, - "n": 54 - } - }, - "spread": 71.4736 - }, - "effort": { - "values": { - "high": { - "mean": 214.4138, - "effect": -3.0288, - "n": 58 + "typescript": { + "mean": 260.8525, + "effect": 17.3748, + "n": 61 }, - "max": { - "mean": 276.0, - "effect": 58.5574, + "unspecified": { + "mean": 91.6667, + "effect": -151.8109, "n": 3 } }, - "spread": 61.5862 + "spread": 218.8525 }, "claude_version": { "values": { "2.1.91 (Claude Code)": { "mean": 223.8302, - "effect": 6.3876, + "effect": -19.6474, "n": 53 }, "2.1.92 (Claude Code)": { - "mean": 175.125, - "effect": -42.3176, - "n": 8 + "mean": 317.8571, + "effect": 74.3795, + "n": 14 } }, - "spread": 48.7052 + "spread": 94.0269 }, "context_file": { "values": { "none": { - "mean": 221.7818, - "effect": 4.3392, - "n": 55 + "mean": 249.9508, + "effect": 6.4732, + "n": 61 }, "provided": { "mean": 177.6667, - "effect": -39.776, + "effect": -65.8109, "n": 6 } }, - "spread": 44.1151 + "spread": 72.2841 }, - "tool_edit": { - "values": { - "off": { - "mean": 254.0, - "effect": 36.5574, - "n": 6 - }, - "on": { - "mean": 213.4545, - "effect": -3.9881, - "n": 55 - } - }, - "spread": 40.5455 - }, - "tool_write": { + "sub_agents": { "values": { "off": { - "mean": 244.0, - "effect": 26.5574, - "n": 6 + "mean": 198.25, + "effect": -45.2276, + "n": 8 }, "on": { - "mean": 214.5455, - "effect": -2.8972, - "n": 55 + "mean": 249.6102, + "effect": 6.1326, + "n": 59 } }, - "spread": 29.4545 + "spread": 51.3602 }, - "sub_agents": { + "human_language": { "values": { - "off": { - "mean": 198.25, - "effect": -19.1926, - "n": 8 + "en": { + "mean": 241.2188, + "effect": -2.2589, + "n": 64 }, - "on": { - "mean": 220.3396, - "effect": 2.897, - "n": 53 + "es": { + "mean": 291.6667, + "effect": 48.1891, + "n": 3 } }, - "spread": 22.0896 + "spread": 50.4479 }, "tool_glob": { "values": { "off": { "mean": 198.5, - "effect": -18.9426, + "effect": -44.9776, "n": 6 }, "on": { - "mean": 219.5091, - "effect": 2.0665, - "n": 55 + "mean": 247.9016, + "effect": 4.424, + "n": 61 } }, - "spread": 21.0091 + "spread": 49.4016 }, "prompt_style": { "values": { "detailed": { "mean": 200.5, - "effect": -16.9426, + "effect": -42.9776, "n": 4 }, "simple": { - "mean": 218.6316, - "effect": 1.189, - "n": 57 + "mean": 246.2063, + "effect": 2.7287, + "n": 63 } }, - "spread": 18.1316 + "spread": 45.7063 }, - "tool_grep": { + "tool_read": { "values": { "off": { - "mean": 232.8333, - "effect": 15.3907, - "n": 6 + "mean": 280.7143, + "effect": 37.2367, + "n": 7 }, "on": { - "mean": 215.7636, - "effect": -1.679, - "n": 55 + "mean": 239.1333, + "effect": -4.3443, + "n": 60 } }, - "spread": 17.0697 + "spread": 41.581 }, - "web_search": { + "effort": { "values": { - "off": { - "mean": 227.625, - "effect": 10.1824, - "n": 8 + "high": { + "mean": 241.9531, + "effect": -1.5245, + "n": 64 }, - "on": { - "mean": 215.9057, - "effect": -1.537, - "n": 53 + "max": { + "mean": 276.0, + "effect": 32.5224, + "n": 3 } }, - "spread": 11.7193 + "spread": 34.0469 }, "max_budget": { "values": { "high": { "mean": 212.6667, - "effect": -4.776, + "effect": -30.8109, "n": 6 }, "low": { - "mean": 217.9636, - "effect": 0.521, - "n": 55 + "mean": 246.5082, + "effect": 3.0306, + "n": 61 + } + }, + "spread": 33.8415 + }, + "linter": { + "values": { + "off": { + "mean": 218.2222, + "effect": -25.2554, + "n": 9 + }, + "on": { + "mean": 247.3966, + "effect": 3.9189, + "n": 58 } }, - "spread": 5.2969 + "spread": 29.1744 }, "playwright": { "values": { "off": { "mean": 219.0, - "effect": 1.5574, + "effect": -24.4776, "n": 8 }, "on": { - "mean": 217.2075, - "effect": -0.2351, - "n": 53 + "mean": 246.7966, + "effect": 3.319, + "n": 59 } }, - "spread": 1.7925 + "spread": 27.7966 }, - "linter": { + "web_search": { "values": { "off": { - "mean": 218.2222, - "effect": 0.7796, - "n": 9 + "mean": 227.625, + "effect": -15.8526, + "n": 8 + }, + "on": { + "mean": 245.6271, + "effect": 2.1495, + "n": 59 + } + }, + "spread": 18.0021 + }, + "tool_grep": { + "values": { + "off": { + "mean": 232.8333, + "effect": -10.6443, + "n": 6 + }, + "on": { + "mean": 244.5246, + "effect": 1.047, + "n": 61 + } + }, + "spread": 11.6913 + }, + "tool_edit": { + "values": { + "off": { + "mean": 254.0, + "effect": 10.5224, + "n": 6 + }, + "on": { + "mean": 242.4426, + "effect": -1.035, + "n": 61 + } + }, + "spread": 11.5574 + }, + "tool_write": { + "values": { + "off": { + "mean": 244.0, + "effect": 0.5224, + "n": 6 }, "on": { - "mean": 217.3077, - "effect": -0.1349, - "n": 52 + "mean": 243.4262, + "effect": -0.0514, + "n": 61 } }, - "spread": 0.9145 + "spread": 0.5738 } } \ No newline at end of file diff --git a/results/index.jsonl b/results/index.jsonl @@ -59,3 +59,9 @@ {"run_id":"tetris_context_file=provided_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=haiku_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3","task":"tetris","model":"haiku","cell_id":"tetris_context_file=provided_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=haiku_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on","completed_at":"2026-04-03T20:38:44.044823+00:00"} {"run_id": "tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=off_max_budget=low_model=haiku_playwright=off_prompt_style=simple_sub_agents=off_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=off_run1", "task": "tetris", "model": "haiku", "cell_id": "tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=off_max_budget=low_model=haiku_playwright=off_prompt_style=simple_sub_agents=off_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=off", "completed_at": "2026-04-04T20:17:47.627694+00:00"} {"run_id": "tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=off_max_budget=low_model=haiku_playwright=off_prompt_style=detailed_sub_agents=off_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=off_run1", "task": "tetris", "model": "haiku", "cell_id": "tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=off_max_budget=low_model=haiku_playwright=off_prompt_style=detailed_sub_agents=off_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=off", "completed_at": "2026-04-04T20:24:23.986627+00:00"} +{"run_id": "tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3", "task": "tetris", "model": "opus", "cell_id": "tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on", "completed_at": "2026-04-04T21:00:10.672556+00:00"} +{"run_id": "tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1", "task": "tetris", "model": "opus", "cell_id": "tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on", "completed_at": "2026-04-04T21:00:53.756188+00:00"} +{"run_id": "tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2", "task": "tetris", "model": "opus", "cell_id": "tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on", "completed_at": "2026-04-04T21:01:53.108336+00:00"} +{"run_id": "tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1", "task": "tetris", "model": "sonnet", "cell_id": "tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on", "completed_at": "2026-04-04T21:04:34.885122+00:00"} +{"run_id": "tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3", "task": "tetris", "model": "sonnet", "cell_id": "tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on", "completed_at": "2026-04-04T21:11:17.256644+00:00"} +{"run_id": "tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2", "task": "tetris", "model": "sonnet", "cell_id": "tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on", "completed_at": "2026-04-04T21:17:35.875117+00:00"} diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/claude_output.json b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/claude_output.json @@ -0,0 +1 @@ +{"type":"result","subtype":"success","is_error":false,"duration_ms":196994,"duration_api_ms":186977,"num_turns":18,"result":"Everything is clean and working. Here's a summary of what was built:\n\n## Tetris Game — Complete Implementation\n\n### Files created\n\n| File | Purpose |\n|---|---|\n| `src/tetris.ts` | Full game logic (~700 lines of TypeScript) |\n| `index.html` | Game UI with canvas, side panels, and controls reference |\n| `tsconfig.json` | TypeScript → ES2020 module compilation |\n| `playwright.config.ts` | Test runner configuration |\n| `tests/tetris.spec.ts` | 11 Playwright end-to-end tests |\n\n### Game features\n\n- **All 7 tetrominoes** (I, O, T, S, Z, J, L) with correct SRS rotation states\n- **SRS wall kicks** — both standard and I-piece kick tables so rotations work against walls and other pieces\n- **7-bag randomizer** — guarantees all 7 pieces appear before any repeats\n- **Ghost piece** — translucent preview showing where the piece will land\n- **Hold piece** — swap the current piece into hold (once per drop)\n- **Next piece preview**\n- **Line clearing** with flash animation\n- **Scoring** — soft drop (1pt/row), hard drop (2pt/row), line clears (100/300/500/800 × level)\n- **Increasing speed** — level increases every 10 lines; drop interval decreases from 1000ms down to 50ms\n- **Lock delay** — 500ms grace period to slide/rotate before a piece locks\n- **Pause** (P/Escape), **Game Over** detection, and **Restart** (Enter)\n\n### Controls\n\n| Key | Action |\n|---|---|\n| ← → / A D | Move left/right |\n| ↓ / S | Soft drop |\n| ↑ / X | Rotate clockwise |\n| Z / Ctrl | Rotate counter-clockwise |\n| Space | Hard drop |\n| Shift / C | Hold piece |\n| P / Escape | Pause |\n| Enter | Start / Restart |\n\n### Tests — 11/11 passing ✅\n\nAll tests verify: rendering, start/pause flow, scoring on soft & hard drops, movement, rotation, hold, game-over restart, and line clearing.","stop_reason":"end_turn","session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","total_cost_usd":0.6086812500000001,"usage":{"input_tokens":20,"cache_creation_input_tokens":29327,"cache_read_input_tokens":204675,"output_tokens":12918,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":29327},"inference_geo":"","iterations":[],"speed":"standard"},"modelUsage":{"claude-opus-4-6":{"inputTokens":20,"outputTokens":12918,"cacheReadInputTokens":204675,"cacheCreationInputTokens":29327,"webSearchRequests":0,"costUSD":0.6086812500000001,"contextWindow":200000,"maxOutputTokens":64000}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","uuid":"2c8d8e7d-0a7a-47b3-9d55-a9b16c24b9fe"} +\ No newline at end of file diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/claude_stderr.log b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/claude_stderr.log diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/eval_results.json b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/eval_results.json @@ -0,0 +1,262 @@ +{ + "structural": { + "pass": true, + "checks": [ + { + "name": "entry_point_exists", + "pass": true, + "detail": "index.html found" + }, + { + "name": "package_json_exists", + "pass": true, + "detail": "package.json found" + }, + { + "name": "build_succeeds", + "pass": true, + "detail": "npm run build completed successfully" + }, + { + "name": "typescript_compiles", + "pass": true, + "detail": "tsc --noEmit passed" + } + ], + "score": 1.0 + }, + "functional": { + "pass": false, + "error": "playwright eval not yet wired", + "score": 0 + }, + "quality": { + "lint": { + "pass": true, + "errors": 0, + "warnings": 0 + }, + "typecheck": { + "pass": true + }, + "performance": { + "bundle_size_bytes": 51159, + "size_under_512kb": true + }, + "score": 0.67 + }, + "code_analysis": { + "files": { + "total": 10, + "code": 5, + "docs": 0, + "unnecessary": 0, + "unnecessary_list": [] + }, + "lines_of_code": 1624, + "dependencies": { + "production": 0, + "dev": 6, + "total": 6 + }, + "complexity": "moderate", + "console_logs": 0, + "magic_numbers": { + "count": 42, + "excessive": true + }, + "function_length": { + "count": 68, + "average": 7.4, + "max": 43, + "long_functions": 0 + }, + "max_nesting_depth": 12, + "global_declarations": 20, + "naming": { + "dominant_style": "camelCase", + "consistency_pct": 100.0, + "camel_case": 549, + "snake_case": 0 + }, + "error_handling": { + "try_catch_blocks": 0, + "has_error_handling": false + }, + "comments": { + "comment_lines": 84, + "source_lines": 1269, + "ratio_pct": 6.6 + }, + "separation_of_concerns": { + "verdict": "mixed", + "files_with_rendering": 3, + "files_with_logic": 3, + "files_with_both": 3 + }, + "html_validation": { + "valid": false, + "errors": 9 + }, + "duplication_percentage": 0.0, + "score": 0.85 + }, + "transcript_analysis": { + "total_events": 48, + "tool_calls": { + "total": 17, + "bash": 12, + "write": 1, + "edit": 1, + "read": 3 + }, + "wasted_turns": { + "total": 1, + "docs": 0, + "ascii_art": 0, + "server_starts": 1 + }, + "errors_encountered": 0, + "thinking_blocks": 2, + "text_blocks": 8, + "productivity_ratio": 0.94, + "self_tested": true, + "score": 1.0 + }, + "gameplay_bot": { + "pass": false, + "score": 0.63, + "total": 16, + "passed": 10, + "failed": 6, + "report": { + "implementation": { + "renderer": "canvas", + "grid_detected": true, + "grid_bounds": { + "x": 0, + "y": 0, + "width": 40, + "height": 80 + }, + "controls": { + "left": "ArrowLeft", + "right": "ArrowRight", + "down": "ArrowDown", + "rotate": "z", + "drop": "Space" + }, + "start_mechanism": "enter", + "score_element_found": true + }, + "tests": [ + { + "name": "game_loads", + "pass": true, + "detail": "no console errors" + }, + { + "name": "game_starts", + "pass": true, + "detail": "started via enter" + }, + { + "name": "auto_drop", + "pass": true, + "detail": "pixels changed after 5s with no input" + }, + { + "name": "move_left", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "move_right", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "move_down", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "rotate", + "pass": true, + "detail": "piece shape changed after rotate key" + }, + { + "name": "all_pieces_rotate", + "pass": false, + "detail": "could not detect any piece rotations" + }, + { + "name": "hard_drop", + "pass": false, + "detail": "no change detected after hard drop key" + }, + { + "name": "piece_locks", + "pass": false, + "detail": "could not verify piece locking at bottom" + }, + { + "name": "new_piece_spawns", + "pass": false, + "detail": "could not detect new piece at top" + }, + { + "name": "multiple_pieces", + "pass": false, + "detail": "grid did not accumulate filled cells" + }, + { + "name": "line_clear", + "pass": true, + "detail": "line cleared via strategic placement" + }, + { + "name": "score_changes", + "pass": false, + "detail": "score did not increase: [168] -> no change after polling" + }, + { + "name": "game_over", + "pass": true, + "detail": "game stopped after stacking to top" + }, + { + "name": "playable_30s", + "pass": true, + "detail": "played for 30s, placed 79 pieces, no crashes" + } + ], + "summary": { + "total": 16, + "passed": 10, + "failed": 6, + "score": 0.63 + }, + "gameplay": { + "pieces_placed": 189, + "lines_cleared": 1, + "max_score_observed": 238, + "play_duration_seconds": 30, + "errors_during_play": 0 + }, + "performance": { + "load_time_ms": 21 + }, + "accessibility": { + "issues": [ + "canvas without aria-label or role", + "canvas without aria-label or role", + "canvas without aria-label or role" + ], + "issue_count": 3, + "pass": false + } + } + }, + "score": 0.7095 +} +\ No newline at end of file diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/gameplay-bot-report.json b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/gameplay-bot-report.json @@ -0,0 +1,128 @@ +{ + "implementation": { + "renderer": "canvas", + "grid_detected": true, + "grid_bounds": { + "x": 0, + "y": 0, + "width": 40, + "height": 80 + }, + "controls": { + "left": "ArrowLeft", + "right": "ArrowRight", + "down": "ArrowDown", + "rotate": "z", + "drop": "Space" + }, + "start_mechanism": "enter", + "score_element_found": true + }, + "tests": [ + { + "name": "game_loads", + "pass": true, + "detail": "no console errors" + }, + { + "name": "game_starts", + "pass": true, + "detail": "started via enter" + }, + { + "name": "auto_drop", + "pass": true, + "detail": "pixels changed after 5s with no input" + }, + { + "name": "move_left", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "move_right", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "move_down", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "rotate", + "pass": true, + "detail": "piece shape changed after rotate key" + }, + { + "name": "all_pieces_rotate", + "pass": false, + "detail": "could not detect any piece rotations" + }, + { + "name": "hard_drop", + "pass": false, + "detail": "no change detected after hard drop key" + }, + { + "name": "piece_locks", + "pass": false, + "detail": "could not verify piece locking at bottom" + }, + { + "name": "new_piece_spawns", + "pass": false, + "detail": "could not detect new piece at top" + }, + { + "name": "multiple_pieces", + "pass": false, + "detail": "grid did not accumulate filled cells" + }, + { + "name": "line_clear", + "pass": true, + "detail": "line cleared via strategic placement" + }, + { + "name": "score_changes", + "pass": false, + "detail": "score did not increase: [168] -> no change after polling" + }, + { + "name": "game_over", + "pass": true, + "detail": "game stopped after stacking to top" + }, + { + "name": "playable_30s", + "pass": true, + "detail": "played for 30s, placed 79 pieces, no crashes" + } + ], + "summary": { + "total": 16, + "passed": 10, + "failed": 6, + "score": 0.63 + }, + "gameplay": { + "pieces_placed": 189, + "lines_cleared": 1, + "max_score_observed": 238, + "play_duration_seconds": 30, + "errors_during_play": 0 + }, + "performance": { + "load_time_ms": 21 + }, + "accessibility": { + "issues": [ + "canvas without aria-label or role", + "canvas without aria-label or role", + "canvas without aria-label or role" + ], + "issue_count": 3, + "pass": false + } +} +\ No newline at end of file diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/meta.json b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/meta.json @@ -0,0 +1,30 @@ +{ + "model": "opus", + "effort": "high", + "prompt_style": "simple", + "language": "typescript", + "human_language": "en", + "tool_read": "on", + "tool_write": "on", + "tool_edit": "on", + "tool_glob": "on", + "tool_grep": "on", + "linter": "on", + "playwright": "on", + "context_file": "none", + "sub_agents": "on", + "web_search": "on", + "max_budget": "low", + "task": "tetris", + "cell_id": "tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on", + "runs_per_cell": 3, + "timeout_seconds": 1200, + "max_budget_usd": 2.0, + "run_id": "tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1", + "run_number": 1, + "claude_version": "2.1.92 (Claude Code)", + "started_at": "2026-04-04T20:57:28.503599+00:00", + "wall_time_seconds": 198, + "exit_code": 0, + "completed_at": "2026-04-04T21:00:53.756188+00:00" +} +\ No newline at end of file diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/transcript.jsonl b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/transcript.jsonl @@ -0,0 +1,48 @@ +{"type": "harness", "subtype": "config", "model": "opus", "effort": "high", "tools": ["Bash", "Read", "Write", "Edit", "Glob", "Grep", "Agent", "WebSearch", "WebFetch"], "max_budget_usd": 2.0, "timeout_seconds": 1200, "task": "tetris", "language": "typescript", "prompt_style": "simple"} +{"type": "user", "subtype": "prompt", "message": {"role": "user", "content": "Build a playable Tetris game that runs in a browser. It should have all the standard mechanics: piece rotation, line clearing, scoring, and increasing speed. Use keyboard controls for movement and rotation.\n\n\nUse TypeScript."}} +{"type":"system","subtype":"init","cwd":"/tmp/loop-bench-doqfpofi","session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","tools":["Bash","Edit","Read"],"mcp_servers":[],"model":"claude-opus-4-6","permissionMode":"dontAsk","slash_commands":["update-config","debug","simplify","batch","loop","schedule","claude-api","compact","context","cost","heapdump","init","review","security-review","insights"],"apiKeySource":"apiKeyHelper","claude_code_version":"2.1.92","output_style":"default","agents":["general-purpose","statusline-setup","Explore","Plan"],"skills":["update-config","debug","simplify","batch","loop","schedule","claude-api"],"plugins":[{"name":"claude-hud","path":"/root/.claude/plugins/marketplaces/claude-hud/","source":"claude-hud@claude-hud"},{"name":"rust-analyzer-lsp","path":"/root/.claude/plugins/marketplaces/claude-plugins-official/plugins/rust-analyzer-lsp","source":"rust-analyzer-lsp@claude-plugins-official"}],"uuid":"53de5a97-dd14-44e1-848b-9a90e6a3115c","fast_mode_state":"off"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_015FPbXqxqHDyaWu22MK9oq3","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"The user wants me to build a playable Tetris game in TypeScript that runs in a browser. Let me create a complete implementation with all standard mechanics.\n\nLet me first check what's in the current working directory.","signature":"EpUDClkIDBgCKkAXAjGf/KfElUK0WuVVzN49x/zS9nezolv4ppL80cZu8KsPKfERg64OCABQXEHD8MrFdy5soMvfxVklhCBZ48fjMg9jbGF1ZGUtb3B1cy00LTY4ABIMyNFIQOQdXOMG95L7Ggx40n78HWFm6YX5Xh0iMCsfBidKKHmhfDNoBptPQ6nvTGEcO+QG5HuRHjouLOZnC4ksazMce7M7bfgjfvCR5CrpAZaPiVyNdtXKtYrA57HA9Tf+4yAyif7AlItbPtzxCEmIDxi+8JXzswf1UerTnp1h5wU5M5lYa0md7OWpvQ06hdl/qw+ojgbyen2Y8NChZ2P7ddRpAeBhI/Ngb7oQWsDguT29jEbRvT07AB3dx2WWM/F4U1jxNbUdR8A+R5KPYoGHWc7/cfRDcP43oGigBlj6gEQGbrP9i1jwfvVJ2uOlLEDYEFqZVFjCiaurXi0HQ2TtRBCAlDF1XKWl+YenoJoHVKWXDiWQ2xit6h7F/zgDOT5TqOTOKjr7VnoMznGhUcuzTE8TUFCaiNUiGAE="}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":4778,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":4778,"ephemeral_1h_input_tokens":0},"output_tokens":45,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"9f387774-e853-481d-9b41-6b43330de648"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_015FPbXqxqHDyaWu22MK9oq3","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01UQQq7vSUGJ91EUKCrkSLrh","name":"Bash","input":{"command":"ls /tmp/loop-bench-doqfpofi","description":"List files in working directory"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":4778,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":4778,"ephemeral_1h_input_tokens":0},"output_tokens":45,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"a201217c-6ca7-4677-bcc7-2d9c294f50e6"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01UQQq7vSUGJ91EUKCrkSLrh","type":"tool_result","content":"node_modules\npackage.json\npackage-lock.json","is_error":false}]},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"ac5192d8-5bec-4bf6-84f1-1f73b5d4e9b2","timestamp":"2026-04-04T20:57:41.078Z","tool_use_result":{"stdout":"node_modules\npackage.json\npackage-lock.json","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_012RKhCK3ieTVwMEamVgy32S","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01SQt2DcxaSZ6dpSWXGCSDfe","name":"Read","input":{"file_path":"/tmp/loop-bench-doqfpofi/package.json"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":659,"cache_read_input_tokens":4292,"cache_creation":{"ephemeral_5m_input_tokens":659,"ephemeral_1h_input_tokens":0},"output_tokens":65,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"337101de-0ee9-4224-a26e-d52b796845f4"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01SQt2DcxaSZ6dpSWXGCSDfe","type":"tool_result","content":"1\t{\n2\t \"name\": \"loop-bench-doqfpofi\",\n3\t \"version\": \"1.0.0\",\n4\t \"description\": \"\",\n5\t \"main\": \"index.js\",\n6\t \"scripts\": {\n7\t \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n8\t },\n9\t \"keywords\": [],\n10\t \"author\": \"\",\n11\t \"license\": \"ISC\",\n12\t \"type\": \"commonjs\",\n13\t \"devDependencies\": {\n14\t \"@eslint/js\": \"^10.0.1\",\n15\t \"@playwright/test\": \"^1.59.1\",\n16\t \"@types/node\": \"^25.5.2\",\n17\t \"eslint\": \"^10.2.0\",\n18\t \"typescript\": \"^6.0.2\"\n19\t }\n20\t}\n21\t"}]},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"49f2058b-e0b5-4e7b-9a87-fb5214c74088","timestamp":"2026-04-04T20:57:43.092Z","tool_use_result":{"type":"text","file":{"filePath":"/tmp/loop-bench-doqfpofi/package.json","content":"{\n \"name\": \"loop-bench-doqfpofi\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },\n \"keywords\": [],\n \"author\": \"\",\n \"license\": \"ISC\",\n \"type\": \"commonjs\",\n \"devDependencies\": {\n \"@eslint/js\": \"^10.0.1\",\n \"@playwright/test\": \"^1.59.1\",\n \"@types/node\": \"^25.5.2\",\n \"eslint\": \"^10.2.0\",\n \"typescript\": \"^6.0.2\"\n }\n}\n","numLines":21,"startLine":1,"totalLines":21}}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01Y6L1sSdKeXk8AFbyQdvwv8","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"Let me set up the project structure. I'll create:\n1. `tsconfig.json` for TypeScript configuration\n2. `src/tetris.ts` - the main game logic\n3. `index.html` - the HTML page\n\nSince this is a browser game, I'll compile the TypeScript to JavaScript and serve it. Let me create a clean, complete implementation.","signature":"Eu0DClkIDBgCKkB4TwCVk5MEepISc+OfVMfTOaocKXghOJK7KztmWYEXiJ2w5VHgNGf2+wjSk7zQHF8hUgRcjs9CpCD6Ofl9a3E7Mg9jbGF1ZGUtb3B1cy00LTY4ABIMLM5wdcjEe+IYspZoGgwnTOljQNQYf1q4RywiMAZz0dhDpkhdJWN4+o3mBV3wN0IwYBUgPPoqPwY/MJAxD7WbMKxsuqkNtGLyh/F4MirBAuZiZCFcOyooGsZVBfbBq9rX5eCoY2rdu31yGGGQkYZJHsplfYgvXvpo4xOkfoiw8VchfhO6RHJgmt0dLmvlgtGuLscMezrHV7p+hri78Id5KlVwLGgmjMhMGBJBPMHv6tRY6xF/f71ruW7UgOWUKGZPhI1niPzsXvRrjK6yiH4zQWT2GCPhGu8ojTAumnElWC8pMAiSdtN5uFOHGSZZVnycZ4TNaUjUVDFQ1NrDgHgl4a9JzmCS+Tehn7AL8VdoEXqZm8nb5gYilN9qEX8jMY8zjr1Etu0ExiuVVMCanFIoiQtMm/XDPR3cYsNMZdFa65tklVT7TNp8rbwljtZHuMvmzO6mYDPxuyO9KYJY97aSZw8flsIHKoGEWH+3cdGaqgIjqzP/uYWsaMg8ivO3CrUHd5/CTz+kzVKw4Xok4rE1TRgB"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":313,"cache_read_input_tokens":4951,"cache_creation":{"ephemeral_5m_input_tokens":313,"ephemeral_1h_input_tokens":0},"output_tokens":8,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"e2a747bb-0bab-46e4-808d-5c85e2753985"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01Y6L1sSdKeXk8AFbyQdvwv8","type":"message","role":"assistant","content":[{"type":"text","text":"I'll build a complete Tetris game with TypeScript. Let me set up the project structure and implement everything."}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":313,"cache_read_input_tokens":4951,"cache_creation":{"ephemeral_5m_input_tokens":313,"ephemeral_1h_input_tokens":0},"output_tokens":8,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"a4b46419-3391-4786-b39a-05d98717a7a9"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01Y6L1sSdKeXk8AFbyQdvwv8","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01P4MhLWfruFVR2rCTnayCmR","name":"Bash","input":{"command":"mkdir -p /tmp/loop-bench-doqfpofi/src","description":"Create src directory"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":313,"cache_read_input_tokens":4951,"cache_creation":{"ephemeral_5m_input_tokens":313,"ephemeral_1h_input_tokens":0},"output_tokens":8,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"1c21921f-004c-453a-9770-a6ed8c3557fa"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01P4MhLWfruFVR2rCTnayCmR","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"6e4ca91f-056c-42dd-bfe9-d08a530ba628","timestamp":"2026-04-04T20:57:48.515Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":true}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01U7YQMk7ASwcBUFRNH9pjg5","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01SUMJnGQeydcuy7FUUU3khK","name":"Write","input":{"file_path":"/tmp/loop-bench-doqfpofi/tsconfig.json","content":"{\n \"compilerOptions\": {\n \"target\": \"ES2020\",\n \"module\": \"ES2020\",\n \"strict\": true,\n \"outDir\": \"./dist\",\n \"rootDir\": \"./src\",\n \"sourceMap\": true,\n \"lib\": [\"ES2020\", \"DOM\", \"DOM.Iterable\"]\n },\n \"include\": [\"src/**/*.ts\"]\n}\n"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":227,"cache_read_input_tokens":5264,"cache_creation":{"ephemeral_5m_input_tokens":227,"ephemeral_1h_input_tokens":0},"output_tokens":62,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"19957c79-57ce-4ac4-bb27-4b0ae84244e4"} +{"type":"user","message":{"role":"user","content":[{"type":"tool_result","content":"<tool_use_error>Error: No such tool available: Write</tool_use_error>","is_error":true,"tool_use_id":"toolu_01SUMJnGQeydcuy7FUUU3khK"}]},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"28e3323c-6f47-4675-b7fa-5b8c55a4138f","timestamp":"2026-04-04T20:57:51.905Z","tool_use_result":"Error: No such tool available: Write"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01RKNipqqDaUQv62LEcQp3BK","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01JDBQDEwc2tn2H2TovFqSrA","name":"Bash","input":{"command":"cat > /tmp/loop-bench-doqfpofi/tsconfig.json << 'TSEOF'\n{\n \"compilerOptions\": {\n \"target\": \"ES2020\",\n \"module\": \"ES2020\",\n \"strict\": true,\n \"outDir\": \"./dist\",\n \"rootDir\": \"./src\",\n \"sourceMap\": true,\n \"lib\": [\"ES2020\", \"DOM\", \"DOM.Iterable\"]\n },\n \"include\": [\"src/**/*.ts\"]\n}\nTSEOF","description":"Create tsconfig.json"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":224,"cache_read_input_tokens":5491,"cache_creation":{"ephemeral_5m_input_tokens":224,"ephemeral_1h_input_tokens":0},"output_tokens":54,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"e837c07c-a4f7-4c19-9f34-0d421c28bd3d"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01JDBQDEwc2tn2H2TovFqSrA","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"61e07d85-0020-41f9-99ef-50ecce0e5f38","timestamp":"2026-04-04T20:57:55.143Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_016ymgXo2BmxDPXMRYxUwB41","type":"message","role":"assistant","content":[{"type":"text","text":"Now let me create the main game TypeScript file:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":219,"cache_read_input_tokens":5715,"cache_creation":{"ephemeral_5m_input_tokens":219,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"be118aaa-bc49-4214-9ff4-96a930efe30a"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_016ymgXo2BmxDPXMRYxUwB41","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_013nomyGDpGcyxVaRnd5WB6S","name":"Bash","input":{"command":"cat > /tmp/loop-bench-doqfpofi/src/tetris.ts << 'TSEOF'\n// ── Types ──────────────────────────────────────────────────────────────────\n\ntype Cell = string | null;\ntype Board = Cell[][];\ntype Shape = number[][];\n\ninterface Piece {\n shape: Shape;\n color: string;\n x: number;\n y: number;\n}\n\ninterface Tetromino {\n shapes: Shape[];\n color: string;\n}\n\n// ── Constants ──────────────────────────────────────────────────────────────\n\nconst COLS = 10;\nconst ROWS = 20;\nconst BLOCK_SIZE = 30;\nconst PREVIEW_BLOCK = 20;\n\nconst TETROMINOES: Tetromino[] = [\n { // I\n shapes: [\n [[0,0,0,0],[1,1,1,1],[0,0,0,0],[0,0,0,0]],\n [[0,0,1,0],[0,0,1,0],[0,0,1,0],[0,0,1,0]],\n [[0,0,0,0],[0,0,0,0],[1,1,1,1],[0,0,0,0]],\n [[0,1,0,0],[0,1,0,0],[0,1,0,0],[0,1,0,0]],\n ],\n color: \"#00f0f0\",\n },\n { // O\n shapes: [\n [[1,1],[1,1]],\n [[1,1],[1,1]],\n [[1,1],[1,1]],\n [[1,1],[1,1]],\n ],\n color: \"#f0f000\",\n },\n { // T\n shapes: [\n [[0,1,0],[1,1,1],[0,0,0]],\n [[0,1,0],[0,1,1],[0,1,0]],\n [[0,0,0],[1,1,1],[0,1,0]],\n [[0,1,0],[1,1,0],[0,1,0]],\n ],\n color: \"#a000f0\",\n },\n { // S\n shapes: [\n [[0,1,1],[1,1,0],[0,0,0]],\n [[0,1,0],[0,1,1],[0,0,1]],\n [[0,0,0],[0,1,1],[1,1,0]],\n [[1,0,0],[1,1,0],[0,1,0]],\n ],\n color: \"#00f000\",\n },\n { // Z\n shapes: [\n [[1,1,0],[0,1,1],[0,0,0]],\n [[0,0,1],[0,1,1],[0,1,0]],\n [[0,0,0],[1,1,0],[0,1,1]],\n [[0,1,0],[1,1,0],[1,0,0]],\n ],\n color: \"#f00000\",\n },\n { // J\n shapes: [\n [[1,0,0],[1,1,1],[0,0,0]],\n [[0,1,1],[0,1,0],[0,1,0]],\n [[0,0,0],[1,1,1],[0,0,1]],\n [[0,1,0],[0,1,0],[1,1,0]],\n ],\n color: \"#0000f0\",\n },\n { // L\n shapes: [\n [[0,0,1],[1,1,1],[0,0,0]],\n [[0,1,0],[0,1,0],[0,1,1]],\n [[0,0,0],[1,1,1],[1,0,0]],\n [[1,1,0],[0,1,0],[0,1,0]],\n ],\n color: \"#f0a000\",\n },\n];\n\nconst LINE_POINTS = [0, 100, 300, 500, 800];\nconst SOFT_DROP_POINTS = 1;\nconst HARD_DROP_POINTS = 2;\n\n// ── Wall Kick Data (SRS) ──────────────────────────────────────────────────\n\nconst WALL_KICKS: Record<string, [number, number][]> = {\n \"0>1\": [[-1,0],[-1,1],[0,-2],[-1,-2]],\n \"1>0\": [[1,0],[1,-1],[0,2],[1,2]],\n \"1>2\": [[1,0],[1,-1],[0,2],[1,2]],\n \"2>1\": [[-1,0],[-1,1],[0,-2],[-1,-2]],\n \"2>3\": [[1,0],[1,1],[0,-2],[1,-2]],\n \"3>2\": [[-1,0],[-1,-1],[0,2],[-1,2]],\n \"3>0\": [[-1,0],[-1,-1],[0,2],[-1,2]],\n \"0>3\": [[1,0],[1,1],[0,-2],[1,-2]],\n};\n\nconst I_WALL_KICKS: Record<string, [number, number][]> = {\n \"0>1\": [[-2,0],[1,0],[-2,-1],[1,2]],\n \"1>0\": [[2,0],[-1,0],[2,1],[-1,-2]],\n \"1>2\": [[-1,0],[2,0],[-1,2],[2,-1]],\n \"2>1\": [[1,0],[-2,0],[1,-2],[-2,1]],\n \"2>3\": [[2,0],[-1,0],[2,1],[-1,-2]],\n \"3>2\": [[-2,0],[1,0],[-2,-1],[1,2]],\n \"3>0\": [[1,0],[-2,0],[1,-2],[-2,1]],\n \"0>3\": [[-1,0],[2,0],[-1,2],[2,-1]],\n};\n\n// ── Game Class ─────────────────────────────────────────────────────────────\n\nclass TetrisGame {\n private canvas: HTMLCanvasElement;\n private ctx: CanvasRenderingContext2D;\n private previewCanvas: HTMLCanvasElement;\n private previewCtx: CanvasRenderingContext2D;\n private holdCanvas: HTMLCanvasElement;\n private holdCtx: CanvasRenderingContext2D;\n\n private board: Board;\n private current: Piece | null = null;\n private currentRotation = 0;\n private currentTetrominoIndex = -1;\n private nextTetrominoIndex = -1;\n private heldTetrominoIndex = -1;\n private canHold = true;\n\n private score = 0;\n private level = 1;\n private lines = 0;\n private gameOver = false;\n private paused = false;\n private started = false;\n\n private dropInterval = 1000;\n private lastDrop = 0;\n private lockDelay = 500;\n private lockTimer = 0;\n private isLocking = false;\n\n private animatingRows: number[] = [];\n private animationStart = 0;\n private readonly ANIMATION_DURATION = 300;\n\n private bag: number[] = [];\n\n private scoreEl: HTMLElement;\n private levelEl: HTMLElement;\n private linesEl: HTMLElement;\n private messageEl: HTMLElement;\n\n constructor() {\n this.canvas = document.getElementById(\"game-canvas\") as HTMLCanvasElement;\n this.ctx = this.canvas.getContext(\"2d\")!;\n this.canvas.width = COLS * BLOCK_SIZE;\n this.canvas.height = ROWS * BLOCK_SIZE;\n\n this.previewCanvas = document.getElementById(\"preview-canvas\") as HTMLCanvasElement;\n this.previewCtx = this.previewCanvas.getContext(\"2d\")!;\n this.previewCanvas.width = 4 * PREVIEW_BLOCK;\n this.previewCanvas.height = 4 * PREVIEW_BLOCK;\n\n this.holdCanvas = document.getElementById(\"hold-canvas\") as HTMLCanvasElement;\n this.holdCtx = this.holdCanvas.getContext(\"2d\")!;\n this.holdCanvas.width = 4 * PREVIEW_BLOCK;\n this.holdCanvas.height = 4 * PREVIEW_BLOCK;\n\n this.scoreEl = document.getElementById(\"score\")!;\n this.levelEl = document.getElementById(\"level\")!;\n this.linesEl = document.getElementById(\"lines\")!;\n this.messageEl = document.getElementById(\"message\")!;\n\n this.board = this.createBoard();\n\n this.bindKeys();\n this.showMessage(\"Press ENTER to start\");\n this.drawBoard();\n }\n\n // ── Board helpers ──────────────────────────────────────────────────────\n\n private createBoard(): Board {\n return Array.from({ length: ROWS }, () => Array(COLS).fill(null));\n }\n\n // ── 7-bag randomiser ──────────────────────────────────────────────────\n\n private refillBag(): void {\n const indices = [0, 1, 2, 3, 4, 5, 6];\n for (let i = indices.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [indices[i], indices[j]] = [indices[j], indices[i]];\n }\n this.bag.push(...indices);\n }\n\n private nextFromBag(): number {\n if (this.bag.length < 2) this.refillBag();\n return this.bag.shift()!;\n }\n\n // ── Piece management ──────────────────────────────────────────────────\n\n private spawnPiece(tetrominoIndex?: number): void {\n const idx = tetrominoIndex ?? this.nextTetrominoIndex;\n this.nextTetrominoIndex = this.nextFromBag();\n\n const t = TETROMINOES[idx];\n this.currentTetrominoIndex = idx;\n this.currentRotation = 0;\n this.current = {\n shape: t.shapes[0],\n color: t.color,\n x: Math.floor((COLS - t.shapes[0][0].length) / 2),\n y: 0,\n };\n this.isLocking = false;\n this.canHold = true;\n\n if (this.collides(this.current.shape, this.current.x, this.current.y)) {\n this.gameOver = true;\n this.showMessage(\"GAME OVER — press ENTER\");\n }\n }\n\n // ── Collision detection ───────────────────────────────────────────────\n\n private collides(shape: Shape, px: number, py: number): boolean {\n for (let r = 0; r < shape.length; r++) {\n for (let c = 0; c < shape[r].length; c++) {\n if (!shape[r][c]) continue;\n const bx = px + c;\n const by = py + r;\n if (bx < 0 || bx >= COLS || by >= ROWS) return true;\n if (by >= 0 && this.board[by][bx]) return true;\n }\n }\n return false;\n }\n\n // ── Movement ──────────────────────────────────────────────────────────\n\n private moveLeft(): void {\n if (!this.current || this.gameOver || this.paused) return;\n if (!this.collides(this.current.shape, this.current.x - 1, this.current.y)) {\n this.current.x--;\n if (this.isLocking) this.lockTimer = performance.now();\n }\n }\n\n private moveRight(): void {\n if (!this.current || this.gameOver || this.paused) return;\n if (!this.collides(this.current.shape, this.current.x + 1, this.current.y)) {\n this.current.x++;\n if (this.isLocking) this.lockTimer = performance.now();\n }\n }\n\n private moveDown(): boolean {\n if (!this.current || this.gameOver || this.paused) return false;\n if (!this.collides(this.current.shape, this.current.x, this.current.y + 1)) {\n this.current.y++;\n this.isLocking = false;\n return true;\n }\n return false;\n }\n\n private softDrop(): void {\n if (this.moveDown()) {\n this.score += SOFT_DROP_POINTS;\n this.updateScore();\n }\n }\n\n private hardDrop(): void {\n if (!this.current || this.gameOver || this.paused) return;\n let dropped = 0;\n while (!this.collides(this.current.shape, this.current.x, this.current.y + 1)) {\n this.current.y++;\n dropped++;\n }\n this.score += dropped * HARD_DROP_POINTS;\n this.updateScore();\n this.lockPiece();\n }\n\n private getGhostY(): number {\n if (!this.current) return 0;\n let gy = this.current.y;\n while (!this.collides(this.current.shape, this.current.x, gy + 1)) {\n gy++;\n }\n return gy;\n }\n\n // ── Rotation with SRS wall kicks ──────────────────────────────────────\n\n private rotate(dir: 1 | -1): void {\n if (!this.current || this.gameOver || this.paused) return;\n\n const t = TETROMINOES[this.currentTetrominoIndex];\n const from = this.currentRotation;\n const to = (from + dir + 4) % 4;\n const newShape = t.shapes[to];\n\n // Basic rotation\n if (!this.collides(newShape, this.current.x, this.current.y)) {\n this.current.shape = newShape;\n this.currentRotation = to;\n if (this.isLocking) this.lockTimer = performance.now();\n return;\n }\n\n // Wall kicks\n const kickKey = `${from}>${to}`;\n const kicks = this.currentTetrominoIndex === 0 ? I_WALL_KICKS[kickKey] : WALL_KICKS[kickKey];\n if (!kicks) return;\n\n for (const [dx, dy] of kicks) {\n if (!this.collides(newShape, this.current.x + dx, this.current.y - dy)) {\n this.current.x += dx;\n this.current.y -= dy;\n this.current.shape = newShape;\n this.currentRotation = to;\n if (this.isLocking) this.lockTimer = performance.now();\n return;\n }\n }\n }\n\n // ── Hold ──────────────────────────────────────────────────────────────\n\n private holdPiece(): void {\n if (!this.current || !this.canHold || this.gameOver || this.paused) return;\n this.canHold = false;\n const prev = this.heldTetrominoIndex;\n this.heldTetrominoIndex = this.currentTetrominoIndex;\n if (prev === -1) {\n this.spawnPiece();\n } else {\n this.spawnPiece(prev);\n }\n }\n\n // ── Lock & clear ─────────────────────────────────────────────────────\n\n private lockPiece(): void {\n if (!this.current) return;\n const { shape, color, x, y } = this.current;\n for (let r = 0; r < shape.length; r++) {\n for (let c = 0; c < shape[r].length; c++) {\n if (!shape[r][c]) continue;\n const by = y + r;\n const bx = x + c;\n if (by >= 0 && by < ROWS && bx >= 0 && bx < COLS) {\n this.board[by][bx] = color;\n }\n }\n }\n this.current = null;\n this.clearLines();\n }\n\n private clearLines(): void {\n const fullRows: number[] = [];\n for (let r = 0; r < ROWS; r++) {\n if (this.board[r].every((c) => c !== null)) {\n fullRows.push(r);\n }\n }\n\n if (fullRows.length === 0) {\n this.spawnPiece();\n return;\n }\n\n // Start line-clear animation\n this.animatingRows = fullRows;\n this.animationStart = performance.now();\n }\n\n private finishClear(): void {\n const fullRows = this.animatingRows;\n this.animatingRows = [];\n\n // Remove rows top-to-bottom so indices stay stable when shifting\n for (const row of fullRows.sort((a, b) => a - b)) {\n this.board.splice(row, 1);\n this.board.unshift(Array(COLS).fill(null));\n }\n\n const cleared = fullRows.length;\n this.lines += cleared;\n this.score += LINE_POINTS[cleared] * this.level;\n this.level = Math.floor(this.lines / 10) + 1;\n this.dropInterval = Math.max(50, 1000 - (this.level - 1) * 80);\n this.updateScore();\n this.spawnPiece();\n }\n\n // ── Rendering ─────────────────────────────────────────────────────────\n\n private drawBlock(\n ctx: CanvasRenderingContext2D,\n x: number,\n y: number,\n color: string,\n size: number,\n alpha = 1,\n ): void {\n ctx.globalAlpha = alpha;\n ctx.fillStyle = color;\n ctx.fillRect(x * size, y * size, size, size);\n\n // Highlight\n ctx.fillStyle = \"rgba(255,255,255,0.25)\";\n ctx.fillRect(x * size, y * size, size, size * 0.15);\n ctx.fillRect(x * size, y * size, size * 0.15, size);\n\n // Shadow\n ctx.fillStyle = \"rgba(0,0,0,0.25)\";\n ctx.fillRect(x * size, y * size + size * 0.85, size, size * 0.15);\n ctx.fillRect(x * size + size * 0.85, y * size, size * 0.15, size);\n\n // Border\n ctx.strokeStyle = \"rgba(0,0,0,0.4)\";\n ctx.lineWidth = 1;\n ctx.strokeRect(x * size + 0.5, y * size + 0.5, size - 1, size - 1);\n\n ctx.globalAlpha = 1;\n }\n\n private drawBoard(): void {\n const ctx = this.ctx;\n // Background\n ctx.fillStyle = \"#111\";\n ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);\n\n // Grid lines\n ctx.strokeStyle = \"rgba(255,255,255,0.04)\";\n ctx.lineWidth = 1;\n for (let r = 0; r <= ROWS; r++) {\n ctx.beginPath();\n ctx.moveTo(0, r * BLOCK_SIZE);\n ctx.lineTo(COLS * BLOCK_SIZE, r * BLOCK_SIZE);\n ctx.stroke();\n }\n for (let c = 0; c <= COLS; c++) {\n ctx.beginPath();\n ctx.moveTo(c * BLOCK_SIZE, 0);\n ctx.lineTo(c * BLOCK_SIZE, ROWS * BLOCK_SIZE);\n ctx.stroke();\n }\n\n // Compute animation flash alpha\n const now = performance.now();\n let flashAlpha = 1;\n if (this.animatingRows.length > 0) {\n const elapsed = now - this.animationStart;\n flashAlpha = 0.3 + 0.7 * Math.abs(Math.sin((elapsed / this.ANIMATION_DURATION) * Math.PI * 3));\n }\n\n // Locked cells\n for (let r = 0; r < ROWS; r++) {\n for (let c = 0; c < COLS; c++) {\n if (this.board[r][c]) {\n const alpha = this.animatingRows.includes(r) ? flashAlpha : 1;\n this.drawBlock(ctx, c, r, this.board[r][c]!, BLOCK_SIZE, alpha);\n }\n }\n }\n\n if (this.current && this.animatingRows.length === 0) {\n // Ghost piece\n const gy = this.getGhostY();\n for (let r = 0; r < this.current.shape.length; r++) {\n for (let c = 0; c < this.current.shape[r].length; c++) {\n if (!this.current.shape[r][c]) continue;\n this.drawBlock(ctx, this.current.x + c, gy + r, this.current.color, BLOCK_SIZE, 0.2);\n }\n }\n\n // Active piece\n for (let r = 0; r < this.current.shape.length; r++) {\n for (let c = 0; c < this.current.shape[r].length; c++) {\n if (!this.current.shape[r][c]) continue;\n const py = this.current.y + r;\n if (py >= 0) {\n this.drawBlock(ctx, this.current.x + c, py, this.current.color, BLOCK_SIZE);\n }\n }\n }\n }\n }\n\n private drawPreview(): void {\n const ctx = this.previewCtx;\n ctx.fillStyle = \"#1a1a2e\";\n ctx.fillRect(0, 0, this.previewCanvas.width, this.previewCanvas.height);\n if (this.nextTetrominoIndex < 0) return;\n const t = TETROMINOES[this.nextTetrominoIndex];\n const shape = t.shapes[0];\n const ox = (4 - shape[0].length) / 2;\n const oy = (4 - shape.length) / 2;\n for (let r = 0; r < shape.length; r++) {\n for (let c = 0; c < shape[r].length; c++) {\n if (shape[r][c]) {\n this.drawBlock(ctx, ox + c, oy + r, t.color, PREVIEW_BLOCK);\n }\n }\n }\n }\n\n private drawHold(): void {\n const ctx = this.holdCtx;\n ctx.fillStyle = \"#1a1a2e\";\n ctx.fillRect(0, 0, this.holdCanvas.width, this.holdCanvas.height);\n if (this.heldTetrominoIndex < 0) return;\n const t = TETROMINOES[this.heldTetrominoIndex];\n const shape = t.shapes[0];\n const ox = (4 - shape[0].length) / 2;\n const oy = (4 - shape.length) / 2;\n const alpha = this.canHold ? 1 : 0.35;\n for (let r = 0; r < shape.length; r++) {\n for (let c = 0; c < shape[r].length; c++) {\n if (shape[r][c]) {\n this.drawBlock(ctx, ox + c, oy + r, t.color, PREVIEW_BLOCK, alpha);\n }\n }\n }\n }\n\n // ── HUD helpers ───────────────────────────────────────────────────────\n\n private updateScore(): void {\n this.scoreEl.textContent = this.score.toString();\n this.levelEl.textContent = this.level.toString();\n this.linesEl.textContent = this.lines.toString();\n }\n\n private showMessage(msg: string): void {\n this.messageEl.textContent = msg;\n this.messageEl.style.display = \"block\";\n }\n\n private hideMessage(): void {\n this.messageEl.style.display = \"none\";\n }\n\n // ── Input ─────────────────────────────────────────────────────────────\n\n private bindKeys(): void {\n document.addEventListener(\"keydown\", (e: KeyboardEvent) => {\n if (e.key === \"Enter\") {\n if (this.gameOver || !this.started) {\n this.reset();\n return;\n }\n }\n\n if (e.key === \"p\" || e.key === \"P\" || e.key === \"Escape\") {\n if (!this.gameOver && this.started) {\n this.paused = !this.paused;\n if (this.paused) {\n this.showMessage(\"PAUSED — press P\");\n } else {\n this.hideMessage();\n this.lastDrop = performance.now();\n }\n }\n return;\n }\n\n if (this.paused || this.gameOver || !this.started) return;\n\n switch (e.key) {\n case \"ArrowLeft\":\n case \"a\":\n e.preventDefault();\n this.moveLeft();\n break;\n case \"ArrowRight\":\n case \"d\":\n e.preventDefault();\n this.moveRight();\n break;\n case \"ArrowDown\":\n case \"s\":\n e.preventDefault();\n this.softDrop();\n break;\n case \"ArrowUp\":\n case \"x\":\n e.preventDefault();\n this.rotate(1);\n break;\n case \"z\":\n case \"Control\":\n e.preventDefault();\n this.rotate(-1);\n break;\n case \" \":\n e.preventDefault();\n this.hardDrop();\n break;\n case \"Shift\":\n case \"c\":\n e.preventDefault();\n this.holdPiece();\n break;\n }\n });\n }\n\n // ── Game loop ─────────────────────────────────────────────────────────\n\n private reset(): void {\n this.board = this.createBoard();\n this.score = 0;\n this.level = 1;\n this.lines = 0;\n this.gameOver = false;\n this.paused = false;\n this.dropInterval = 1000;\n this.lastDrop = performance.now();\n this.isLocking = false;\n this.heldTetrominoIndex = -1;\n this.canHold = true;\n this.bag = [];\n this.animatingRows = [];\n\n this.nextTetrominoIndex = this.nextFromBag();\n this.spawnPiece();\n this.updateScore();\n this.hideMessage();\n\n if (!this.started) {\n this.started = true;\n requestAnimationFrame((t) => this.loop(t));\n }\n }\n\n private loop(now: number): void {\n if (!this.gameOver) {\n requestAnimationFrame((t) => this.loop(t));\n }\n\n if (this.paused) {\n this.drawBoard();\n this.drawPreview();\n this.drawHold();\n return;\n }\n\n // Handle line-clear animation\n if (this.animatingRows.length > 0) {\n if (now - this.animationStart >= this.ANIMATION_DURATION) {\n this.finishClear();\n }\n this.drawBoard();\n this.drawPreview();\n this.drawHold();\n return;\n }\n\n // Gravity\n if (this.current) {\n if (!this.collides(this.current.shape, this.current.x, this.current.y + 1)) {\n if (now - this.lastDrop >= this.dropInterval) {\n this.current.y++;\n this.lastDrop = now;\n this.isLocking = false;\n }\n } else {\n // Piece is resting – start / check lock delay\n if (!this.isLocking) {\n this.isLocking = true;\n this.lockTimer = now;\n } else if (now - this.lockTimer >= this.lockDelay) {\n this.lockPiece();\n this.lastDrop = now;\n }\n }\n }\n\n this.drawBoard();\n this.drawPreview();\n this.drawHold();\n }\n}\n\n// ── Bootstrap ──────────────────────────────────────────────────────────────\n\nwindow.addEventListener(\"DOMContentLoaded\", () => {\n new TetrisGame();\n});\nTSEOF","description":"Create main Tetris game TypeScript source"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":219,"cache_read_input_tokens":5715,"cache_creation":{"ephemeral_5m_input_tokens":219,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"4e262355-e89e-467b-a147-2ff168571a80"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_013nomyGDpGcyxVaRnd5WB6S","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"41dae7fc-e884-4a49-a730-0c2b752d5ddc","timestamp":"2026-04-04T20:59:16.678Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01VjSYDCpoT5MvwPxMKhXNox","type":"message","role":"assistant","content":[{"type":"text","text":"Now create the HTML file:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":7556,"cache_read_input_tokens":5934,"cache_creation":{"ephemeral_5m_input_tokens":7556,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"a74948aa-7d0e-430e-b941-98a41c55b5fc"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01VjSYDCpoT5MvwPxMKhXNox","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_011i8AiwJKsyDXjv94YHCEVA","name":"Bash","input":{"command":"cat > /tmp/loop-bench-doqfpofi/index.html << 'HTMLEOF'\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Tetris</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n\n body {\n background: #0a0a1a;\n color: #e0e0e0;\n font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 100vh;\n overflow: hidden;\n }\n\n #game-wrapper {\n display: flex;\n gap: 24px;\n align-items: flex-start;\n }\n\n /* Side panels */\n .side-panel {\n display: flex;\n flex-direction: column;\n gap: 16px;\n min-width: 100px;\n }\n\n .panel-box {\n background: #1a1a2e;\n border: 2px solid #2a2a4a;\n border-radius: 8px;\n padding: 12px;\n text-align: center;\n }\n\n .panel-box h3 {\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 2px;\n color: #888;\n margin-bottom: 8px;\n }\n\n .panel-box .value {\n font-size: 22px;\n font-weight: 700;\n color: #fff;\n font-variant-numeric: tabular-nums;\n }\n\n /* Canvases */\n #game-area {\n position: relative;\n }\n\n #game-canvas {\n display: block;\n border: 2px solid #2a2a4a;\n border-radius: 4px;\n }\n\n #message {\n display: none;\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n background: rgba(0, 0, 0, 0.85);\n color: #fff;\n padding: 16px 28px;\n border-radius: 8px;\n font-size: 18px;\n font-weight: 600;\n letter-spacing: 1px;\n white-space: nowrap;\n pointer-events: none;\n z-index: 10;\n }\n\n canvas.preview {\n display: block;\n border: 2px solid #2a2a4a;\n border-radius: 4px;\n }\n\n /* Controls help */\n .controls {\n font-size: 11px;\n color: #666;\n line-height: 1.8;\n }\n .controls kbd {\n background: #2a2a4a;\n border-radius: 3px;\n padding: 1px 5px;\n font-family: inherit;\n color: #aaa;\n }\n </style>\n</head>\n<body>\n <div id=\"game-wrapper\">\n <!-- Left panel -->\n <div class=\"side-panel\">\n <div class=\"panel-box\">\n <h3>Hold</h3>\n <canvas id=\"hold-canvas\" class=\"preview\"></canvas>\n </div>\n <div class=\"panel-box\">\n <h3>Score</h3>\n <div class=\"value\" id=\"score\">0</div>\n </div>\n <div class=\"panel-box\">\n <h3>Level</h3>\n <div class=\"value\" id=\"level\">1</div>\n </div>\n <div class=\"panel-box\">\n <h3>Lines</h3>\n <div class=\"value\" id=\"lines\">0</div>\n </div>\n </div>\n\n <!-- Board -->\n <div id=\"game-area\">\n <canvas id=\"game-canvas\"></canvas>\n <div id=\"message\"></div>\n </div>\n\n <!-- Right panel -->\n <div class=\"side-panel\">\n <div class=\"panel-box\">\n <h3>Next</h3>\n <canvas id=\"preview-canvas\" class=\"preview\"></canvas>\n </div>\n <div class=\"panel-box controls\">\n <h3>Controls</h3>\n <kbd>←</kbd> <kbd>→</kbd> Move<br/>\n <kbd>↓</kbd> Soft drop<br/>\n <kbd>↑</kbd> Rotate CW<br/>\n <kbd>Z</kbd> Rotate CCW<br/>\n <kbd>Space</kbd> Hard drop<br/>\n <kbd>Shift</kbd> / <kbd>C</kbd> Hold<br/>\n <kbd>P</kbd> Pause<br/>\n </div>\n </div>\n </div>\n\n <script type=\"module\" src=\"./dist/tetris.js\"></script>\n</body>\n</html>\nHTMLEOF","description":"Create index.html with game layout and styling"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":7556,"cache_read_input_tokens":5934,"cache_creation":{"ephemeral_5m_input_tokens":7556,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"363ba259-293b-472f-b01c-fd75f8a1f235"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_011i8AiwJKsyDXjv94YHCEVA","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"887b9ed5-7331-42db-84a9-dca68256b3f8","timestamp":"2026-04-04T20:59:37.172Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01CBDT7SYnkDHenKUULNo46B","type":"message","role":"assistant","content":[{"type":"text","text":"Now update `package.json` with build/serve scripts and compile:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":1422,"cache_read_input_tokens":13490,"cache_creation":{"ephemeral_5m_input_tokens":1422,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"529025c8-e42e-4acd-9758-83fd832b514f"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01CBDT7SYnkDHenKUULNo46B","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01X6cTiMjYh5sgdU5Bxu2Pio","name":"Read","input":{"file_path":"/tmp/loop-bench-doqfpofi/package.json"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":1422,"cache_read_input_tokens":13490,"cache_creation":{"ephemeral_5m_input_tokens":1422,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"5b810785-7fa5-4e75-bb80-ba216dfd87d6"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01X6cTiMjYh5sgdU5Bxu2Pio","type":"tool_result","content":"File unchanged since last read. The content from the earlier Read tool_result in this conversation is still current — refer to that instead of re-reading."}]},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"99a1a8f2-59b7-4f6e-bdb4-bc4312f168eb","timestamp":"2026-04-04T20:59:40.922Z","tool_use_result":{"type":"file_unchanged","file":{"filePath":"/tmp/loop-bench-doqfpofi/package.json"}}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01JHEoT7DQCLcKSgzef9n7Kd","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_018vEZqhWcvuohP11YyBmWdT","name":"Edit","input":{"replace_all":false,"file_path":"/tmp/loop-bench-doqfpofi/package.json","old_string":" \"scripts\": {\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },","new_string":" \"scripts\": {\n \"build\": \"tsc\",\n \"test\": \"npx playwright test\"\n },"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":1548,"cache_read_input_tokens":13490,"cache_creation":{"ephemeral_5m_input_tokens":1548,"ephemeral_1h_input_tokens":0},"output_tokens":61,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"45cbd713-e9cd-439b-a5ca-e34b7ea415d6"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_018vEZqhWcvuohP11YyBmWdT","type":"tool_result","content":"The file /tmp/loop-bench-doqfpofi/package.json has been updated successfully."}]},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"5aa297ea-fc4c-4e9e-810e-8d68f465c449","timestamp":"2026-04-04T20:59:44.220Z","tool_use_result":{"filePath":"/tmp/loop-bench-doqfpofi/package.json","oldString":" \"scripts\": {\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },","newString":" \"scripts\": {\n \"build\": \"tsc\",\n \"test\": \"npx playwright test\"\n },","originalFile":"{\n \"name\": \"loop-bench-doqfpofi\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },\n \"keywords\": [],\n \"author\": \"\",\n \"license\": \"ISC\",\n \"type\": \"commonjs\",\n \"devDependencies\": {\n \"@eslint/js\": \"^10.0.1\",\n \"@playwright/test\": \"^1.59.1\",\n \"@types/node\": \"^25.5.2\",\n \"eslint\": \"^10.2.0\",\n \"typescript\": \"^6.0.2\"\n }\n}\n","structuredPatch":[{"oldStart":4,"oldLines":7,"newStart":4,"newLines":8,"lines":[" \"description\": \"\","," \"main\": \"index.js\","," \"scripts\": {","- \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"","+ \"build\": \"tsc\",","+ \"test\": \"npx playwright test\""," },"," \"keywords\": [],"," \"author\": \"\","]}],"userModified":false,"replaceAll":false}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01DBno5bEcXUP4WY59rzMnUy","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01CC4EZcYT3tZtbhN2K3pL6E","name":"Bash","input":{"command":"npx tsc","description":"Compile TypeScript"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":212,"cache_read_input_tokens":15038,"cache_creation":{"ephemeral_5m_input_tokens":212,"ephemeral_1h_input_tokens":0},"output_tokens":62,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"c25c7c86-0162-4cea-b6f3-95a08563b04c"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01CC4EZcYT3tZtbhN2K3pL6E","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"acac8112-4f64-4df2-95d8-f4e391437575","timestamp":"2026-04-04T20:59:47.966Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01Azk5biUGU1UGXhKmMK7mCg","type":"message","role":"assistant","content":[{"type":"text","text":"TypeScript compiles cleanly. Let me verify the output:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":305,"cache_read_input_tokens":15038,"cache_creation":{"ephemeral_5m_input_tokens":305,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"215ac6e3-7f92-43f7-8ea7-97f1b0f33aa2"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01Azk5biUGU1UGXhKmMK7mCg","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01WtXPPpTXyXvZjtEgFkXk5E","name":"Bash","input":{"command":"ls -la /tmp/loop-bench-doqfpofi/dist/","description":"Verify compiled output exists"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":305,"cache_read_input_tokens":15038,"cache_creation":{"ephemeral_5m_input_tokens":305,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"feeda1d4-1673-4f67-be28-81de6f00a32f"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01WtXPPpTXyXvZjtEgFkXk5E","type":"tool_result","content":"total 60\ndrwxr-xr-x 2 root root 4096 Apr 4 22:59 .\ndrwx------ 5 root root 4096 Apr 4 22:59 ..\n-rw-r--r-- 1 root root 23763 Apr 4 22:59 tetris.js\n-rw-r--r-- 1 root root 27396 Apr 4 22:59 tetris.js.map","is_error":false}]},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"c8892608-06ba-4344-bfd3-adc3acc8c2ca","timestamp":"2026-04-04T20:59:52.281Z","tool_use_result":{"stdout":"total 60\ndrwxr-xr-x 2 root root 4096 Apr 4 22:59 .\ndrwx------ 5 root root 4096 Apr 4 22:59 ..\n-rw-r--r-- 1 root root 23763 Apr 4 22:59 tetris.js\n-rw-r--r-- 1 root root 27396 Apr 4 22:59 tetris.js.map","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01CAokBJp8XgeUZUhrq7URSG","type":"message","role":"assistant","content":[{"type":"text","text":"Now let me write Playwright tests to verify the game works:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":233,"cache_read_input_tokens":15343,"cache_creation":{"ephemeral_5m_input_tokens":233,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"21cb3928-fcdd-4845-ba0e-967fdd58449a"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01CAokBJp8XgeUZUhrq7URSG","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01HAgjfsdiDJT1RptxYF2YmN","name":"Bash","input":{"command":"cat > /tmp/loop-bench-doqfpofi/playwright.config.ts << 'EOF'\nimport { defineConfig } from \"@playwright/test\";\n\nexport default defineConfig({\n testDir: \"./tests\",\n timeout: 15000,\n use: {\n baseURL: \"http://localhost:3999\",\n },\n webServer: {\n command: \"npx serve -l 3999 -s .\",\n port: 3999,\n reuseExistingServer: false,\n },\n});\nEOF","description":"Create Playwright config"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":233,"cache_read_input_tokens":15343,"cache_creation":{"ephemeral_5m_input_tokens":233,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"67e7faf2-5ec4-46af-a548-39703ca99825"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01HAgjfsdiDJT1RptxYF2YmN","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"495726ad-f9c9-4394-8a36-02125e30da33","timestamp":"2026-04-04T20:59:57.375Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01NsmEWyTDnpzr3ifytKGuKj","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01Hr6j9X1r9HhUnDmH2xGYjb","name":"Bash","input":{"command":"npm install --save-dev serve 2>&1 | tail -3","description":"Install serve for local dev server"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":234,"cache_read_input_tokens":15576,"cache_creation":{"ephemeral_5m_input_tokens":234,"ephemeral_1h_input_tokens":0},"output_tokens":39,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"a012968e-15a5-4865-b1f6-d5f9a5eb9e50"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01Hr6j9X1r9HhUnDmH2xGYjb","type":"tool_result","content":" run `npm fund` for details\n\nfound 0 vulnerabilities","is_error":false}]},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"136836c3-2005-4be9-a049-4511cbf87b15","timestamp":"2026-04-04T21:00:02.904Z","tool_use_result":{"stdout":" run `npm fund` for details\n\nfound 0 vulnerabilities","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01ESiB8YHRMLg6diZD96u96V","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01Udt6z2eabLGzmPYKA2Sq6a","name":"Bash","input":{"command":"mkdir -p /tmp/loop-bench-doqfpofi/tests && cat > /tmp/loop-bench-doqfpofi/tests/tetris.spec.ts << 'TESTEOF'\nimport { test, expect } from \"@playwright/test\";\n\ntest.describe(\"Tetris Game\", () => {\n test.beforeEach(async ({ page }) => {\n await page.goto(\"/\");\n });\n\n test(\"renders the game canvas and UI elements\", async ({ page }) => {\n const canvas = page.locator(\"#game-canvas\");\n await expect(canvas).toBeVisible();\n\n // Side panels\n await expect(page.locator(\"#score\")).toHaveText(\"0\");\n await expect(page.locator(\"#level\")).toHaveText(\"1\");\n await expect(page.locator(\"#lines\")).toHaveText(\"0\");\n\n // Preview and hold canvases\n await expect(page.locator(\"#preview-canvas\")).toBeVisible();\n await expect(page.locator(\"#hold-canvas\")).toBeVisible();\n });\n\n test(\"shows start message before game begins\", async ({ page }) => {\n const msg = page.locator(\"#message\");\n await expect(msg).toBeVisible();\n await expect(msg).toContainText(\"Press ENTER to start\");\n });\n\n test(\"starts the game when Enter is pressed\", async ({ page }) => {\n await page.keyboard.press(\"Enter\");\n // Message should disappear\n const msg = page.locator(\"#message\");\n await expect(msg).toBeHidden();\n });\n\n test(\"pauses and unpauses with P key\", async ({ page }) => {\n await page.keyboard.press(\"Enter\");\n const msg = page.locator(\"#message\");\n\n await page.keyboard.press(\"p\");\n await expect(msg).toBeVisible();\n await expect(msg).toContainText(\"PAUSED\");\n\n await page.keyboard.press(\"p\");\n await expect(msg).toBeHidden();\n });\n\n test(\"score increases on soft drop\", async ({ page }) => {\n await page.keyboard.press(\"Enter\");\n // Soft-drop several times\n for (let i = 0; i < 5; i++) {\n await page.keyboard.press(\"ArrowDown\");\n }\n const score = await page.locator(\"#score\").textContent();\n expect(Number(score)).toBeGreaterThanOrEqual(5);\n });\n\n test(\"score increases on hard drop\", async ({ page }) => {\n await page.keyboard.press(\"Enter\");\n await page.keyboard.press(\" \"); // Space = hard drop\n // The piece should have dropped 17+ rows → score ≥ 34\n const score = await page.locator(\"#score\").textContent();\n expect(Number(score)).toBeGreaterThanOrEqual(10);\n });\n\n test(\"left and right arrow keys do not crash the game\", async ({ page }) => {\n await page.keyboard.press(\"Enter\");\n await page.keyboard.press(\"ArrowLeft\");\n await page.keyboard.press(\"ArrowLeft\");\n await page.keyboard.press(\"ArrowRight\");\n await page.keyboard.press(\"ArrowRight\");\n await page.keyboard.press(\"ArrowRight\");\n // Game should still be running (message hidden)\n await expect(page.locator(\"#message\")).toBeHidden();\n });\n\n test(\"rotation keys do not crash the game\", async ({ page }) => {\n await page.keyboard.press(\"Enter\");\n await page.keyboard.press(\"ArrowUp\"); // CW\n await page.keyboard.press(\"z\"); // CCW\n await page.keyboard.press(\"ArrowUp\");\n await expect(page.locator(\"#message\")).toBeHidden();\n });\n\n test(\"hold piece works\", async ({ page }) => {\n await page.keyboard.press(\"Enter\");\n // Hold the first piece\n await page.keyboard.press(\"Shift\");\n // Game should still be running\n await expect(page.locator(\"#message\")).toBeHidden();\n });\n\n test(\"game over can be restarted with Enter\", async ({ page }) => {\n await page.keyboard.press(\"Enter\");\n\n // Stack pieces by hard-dropping many times to trigger game over\n for (let i = 0; i < 100; i++) {\n await page.keyboard.press(\" \", { delay: 10 });\n }\n\n // Wait for game over message\n const msg = page.locator(\"#message\");\n await expect(msg).toContainText(\"GAME OVER\", { timeout: 10000 });\n\n // Restart\n await page.keyboard.press(\"Enter\");\n await expect(msg).toBeHidden();\n await expect(page.locator(\"#score\")).toHaveText(\"0\");\n await expect(page.locator(\"#level\")).toHaveText(\"1\");\n await expect(page.locator(\"#lines\")).toHaveText(\"0\");\n });\n\n test(\"line clear increases lines count and score\", async ({ page }) => {\n await page.keyboard.press(\"Enter\");\n\n // We'll use a brute-force approach: drop many pieces and check that\n // if any lines were cleared, the count is > 0.\n // Since the game is random, we'll play for a while.\n for (let i = 0; i < 40; i++) {\n // Move piece to a somewhat random position and hard drop\n const dir = i % 5 - 2; // -2, -1, 0, 1, 2\n for (let m = 0; m < Math.abs(dir); m++) {\n await page.keyboard.press(dir < 0 ? \"ArrowLeft\" : \"ArrowRight\");\n }\n await page.keyboard.press(\" \");\n // Small delay to let the game process\n await page.waitForTimeout(50);\n }\n\n // After 40 drops, score should have increased from hard drops at minimum\n const score = await page.locator(\"#score\").textContent();\n expect(Number(score)).toBeGreaterThan(0);\n });\n});\nTESTEOF","description":"Create Playwright tests for the Tetris game"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":116,"cache_read_input_tokens":15810,"cache_creation":{"ephemeral_5m_input_tokens":116,"ephemeral_1h_input_tokens":0},"output_tokens":56,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"2d0ad732-2669-4367-9be0-6f54a2ce5784"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01Udt6z2eabLGzmPYKA2Sq6a","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"b683ae95-4523-4203-a756-837b0683d8dc","timestamp":"2026-04-04T21:00:23.903Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01H9vcRyB7FkRzbfA34RrSs3","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01RGt3o5yWN2BC7qP7Qk9nQM","name":"Bash","input":{"command":"npx playwright install chromium 2>&1 | tail -5","description":"Install Playwright browser","timeout":60000},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":1582,"cache_read_input_tokens":15926,"cache_creation":{"ephemeral_5m_input_tokens":1582,"ephemeral_1h_input_tokens":0},"output_tokens":50,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"d23a48c9-edfc-4363-95e9-8e30c89c42a9"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01RGt3o5yWN2BC7qP7Qk9nQM","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"a3eca214-505b-46f5-ae55-55e45767e2fe","timestamp":"2026-04-04T21:00:27.645Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01SNgRmKQqjVfwZak2NsmKXm","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01CNHDSF9yyDanJbaMJtpf3e","name":"Bash","input":{"command":"npx playwright test 2>&1","description":"Run Playwright tests","timeout":120000},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":122,"cache_read_input_tokens":17508,"cache_creation":{"ephemeral_5m_input_tokens":122,"ephemeral_1h_input_tokens":0},"output_tokens":68,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"552fd02a-5b07-4bf6-906c-63ba02003b7d"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01CNHDSF9yyDanJbaMJtpf3e","type":"tool_result","content":"Running 11 tests using 1 worker\n\n ✓ 1 tests/tetris.spec.ts:8:7 › Tetris Game › renders the game canvas and UI elements (112ms)\n ✓ 2 tests/tetris.spec.ts:22:7 › Tetris Game › shows start message before game begins (87ms)\n ✓ 3 tests/tetris.spec.ts:28:7 › Tetris Game › starts the game when Enter is pressed (74ms)\n ✓ 4 tests/tetris.spec.ts:35:7 › Tetris Game › pauses and unpauses with P key (61ms)\n ✓ 5 tests/tetris.spec.ts:47:7 › Tetris Game › score increases on soft drop (62ms)\n ✓ 6 tests/tetris.spec.ts:57:7 › Tetris Game › score increases on hard drop (51ms)\n ✓ 7 tests/tetris.spec.ts:65:7 › Tetris Game › left and right arrow keys do not crash the game (55ms)\n ✓ 8 tests/tetris.spec.ts:76:7 › Tetris Game › rotation keys do not crash the game (54ms)\n ✓ 9 tests/tetris.spec.ts:84:7 › Tetris Game › hold piece works (50ms)\n ✓ 10 tests/tetris.spec.ts:92:7 › Tetris Game › game over can be restarted with Enter (1.2s)\n ✓ 11 tests/tetris.spec.ts:112:7 › Tetris Game › line clear increases lines count and score (2.2s)\n\n 11 passed (4.9s)","is_error":false}]},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"2905ac78-1cc2-499b-8229-d34877b3c5a4","timestamp":"2026-04-04T21:00:36.015Z","tool_use_result":{"stdout":"Running 11 tests using 1 worker\n\n ✓ 1 tests/tetris.spec.ts:8:7 › Tetris Game › renders the game canvas and UI elements (112ms)\n ✓ 2 tests/tetris.spec.ts:22:7 › Tetris Game › shows start message before game begins (87ms)\n ✓ 3 tests/tetris.spec.ts:28:7 › Tetris Game › starts the game when Enter is pressed (74ms)\n ✓ 4 tests/tetris.spec.ts:35:7 › Tetris Game › pauses and unpauses with P key (61ms)\n ✓ 5 tests/tetris.spec.ts:47:7 › Tetris Game › score increases on soft drop (62ms)\n ✓ 6 tests/tetris.spec.ts:57:7 › Tetris Game › score increases on hard drop (51ms)\n ✓ 7 tests/tetris.spec.ts:65:7 › Tetris Game › left and right arrow keys do not crash the game (55ms)\n ✓ 8 tests/tetris.spec.ts:76:7 › Tetris Game › rotation keys do not crash the game (54ms)\n ✓ 9 tests/tetris.spec.ts:84:7 › Tetris Game › hold piece works (50ms)\n ✓ 10 tests/tetris.spec.ts:92:7 › Tetris Game › game over can be restarted with Enter (1.2s)\n ✓ 11 tests/tetris.spec.ts:112:7 › Tetris Game › line clear increases lines count and score (2.2s)\n\n 11 passed (4.9s)","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_011xD1dnoxWs4aN9bkqcV3uU","type":"message","role":"assistant","content":[{"type":"text","text":"All 11 tests pass. Let me do a final review of the key files:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":549,"cache_read_input_tokens":17630,"cache_creation":{"ephemeral_5m_input_tokens":549,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"0e5bc725-31ab-49a1-9bf8-2843ad92c214"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_011xD1dnoxWs4aN9bkqcV3uU","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01T1ouAo7CAYy2mUcUUU2923","name":"Read","input":{"file_path":"/tmp/loop-bench-doqfpofi/src/tetris.ts"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":549,"cache_read_input_tokens":17630,"cache_creation":{"ephemeral_5m_input_tokens":549,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"d968ca2c-aa01-4a1e-b94d-bf6dc3f042e4"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01T1ouAo7CAYy2mUcUUU2923","type":"tool_result","content":"1\t// ── Types ──────────────────────────────────────────────────────────────────\n2\t\n3\ttype Cell = string | null;\n4\ttype Board = Cell[][];\n5\ttype Shape = number[][];\n6\t\n7\tinterface Piece {\n8\t shape: Shape;\n9\t color: string;\n10\t x: number;\n11\t y: number;\n12\t}\n13\t\n14\tinterface Tetromino {\n15\t shapes: Shape[];\n16\t color: string;\n17\t}\n18\t\n19\t// ── Constants ──────────────────────────────────────────────────────────────\n20\t\n21\tconst COLS = 10;\n22\tconst ROWS = 20;\n23\tconst BLOCK_SIZE = 30;\n24\tconst PREVIEW_BLOCK = 20;\n25\t\n26\tconst TETROMINOES: Tetromino[] = [\n27\t { // I\n28\t shapes: [\n29\t [[0,0,0,0],[1,1,1,1],[0,0,0,0],[0,0,0,0]],\n30\t [[0,0,1,0],[0,0,1,0],[0,0,1,0],[0,0,1,0]],\n31\t [[0,0,0,0],[0,0,0,0],[1,1,1,1],[0,0,0,0]],\n32\t [[0,1,0,0],[0,1,0,0],[0,1,0,0],[0,1,0,0]],\n33\t ],\n34\t color: \"#00f0f0\",\n35\t },\n36\t { // O\n37\t shapes: [\n38\t [[1,1],[1,1]],\n39\t [[1,1],[1,1]],\n40\t [[1,1],[1,1]],\n41\t [[1,1],[1,1]],\n42\t ],\n43\t color: \"#f0f000\",\n44\t },\n45\t { // T\n46\t shapes: [\n47\t [[0,1,0],[1,1,1],[0,0,0]],\n48\t [[0,1,0],[0,1,1],[0,1,0]],\n49\t [[0,0,0],[1,1,1],[0,1,0]],\n50\t [[0,1,0],[1,1,0],[0,1,0]],\n51\t ],\n52\t color: \"#a000f0\",\n53\t },\n54\t { // S\n55\t shapes: [\n56\t [[0,1,1],[1,1,0],[0,0,0]],\n57\t [[0,1,0],[0,1,1],[0,0,1]],\n58\t [[0,0,0],[0,1,1],[1,1,0]],\n59\t [[1,0,0],[1,1,0],[0,1,0]],\n60\t ],\n61\t color: \"#00f000\",\n62\t },\n63\t { // Z\n64\t shapes: [\n65\t [[1,1,0],[0,1,1],[0,0,0]],\n66\t [[0,0,1],[0,1,1],[0,1,0]],\n67\t [[0,0,0],[1,1,0],[0,1,1]],\n68\t [[0,1,0],[1,1,0],[1,0,0]],\n69\t ],\n70\t color: \"#f00000\",\n71\t },\n72\t { // J\n73\t shapes: [\n74\t [[1,0,0],[1,1,1],[0,0,0]],\n75\t [[0,1,1],[0,1,0],[0,1,0]],\n76\t [[0,0,0],[1,1,1],[0,0,1]],\n77\t [[0,1,0],[0,1,0],[1,1,0]],\n78\t ],\n79\t color: \"#0000f0\",\n80\t },\n81\t { // L\n82\t shapes: [\n83\t [[0,0,1],[1,1,1],[0,0,0]],\n84\t [[0,1,0],[0,1,0],[0,1,1]],\n85\t [[0,0,0],[1,1,1],[1,0,0]],\n86\t [[1,1,0],[0,1,0],[0,1,0]],\n87\t ],\n88\t color: \"#f0a000\",\n89\t },\n90\t];\n91\t\n92\tconst LINE_POINTS = [0, 100, 300, 500, 800];\n93\tconst SOFT_DROP_POINTS = 1;\n94\tconst HARD_DROP_POINTS = 2;\n95\t\n96\t// ── Wall Kick Data (SRS) ──────────────────────────────────────────────────\n97\t\n98\tconst WALL_KICKS: Record<string, [number, number][]> = {\n99\t \"0>1\": [[-1,0],[-1,1],[0,-2],[-1,-2]],\n100\t \"1>0\": [[1,0],[1,-1],[0,2],[1,2]],\n101\t \"1>2\": [[1,0],[1,-1],[0,2],[1,2]],\n102\t \"2>1\": [[-1,0],[-1,1],[0,-2],[-1,-2]],\n103\t \"2>3\": [[1,0],[1,1],[0,-2],[1,-2]],\n104\t \"3>2\": [[-1,0],[-1,-1],[0,2],[-1,2]],\n105\t \"3>0\": [[-1,0],[-1,-1],[0,2],[-1,2]],\n106\t \"0>3\": [[1,0],[1,1],[0,-2],[1,-2]],\n107\t};\n108\t\n109\tconst I_WALL_KICKS: Record<string, [number, number][]> = {\n110\t \"0>1\": [[-2,0],[1,0],[-2,-1],[1,2]],\n111\t \"1>0\": [[2,0],[-1,0],[2,1],[-1,-2]],\n112\t \"1>2\": [[-1,0],[2,0],[-1,2],[2,-1]],\n113\t \"2>1\": [[1,0],[-2,0],[1,-2],[-2,1]],\n114\t \"2>3\": [[2,0],[-1,0],[2,1],[-1,-2]],\n115\t \"3>2\": [[-2,0],[1,0],[-2,-1],[1,2]],\n116\t \"3>0\": [[1,0],[-2,0],[1,-2],[-2,1]],\n117\t \"0>3\": [[-1,0],[2,0],[-1,2],[2,-1]],\n118\t};\n119\t\n120\t// ── Game Class ─────────────────────────────────────────────────────────────\n121\t\n122\tclass TetrisGame {\n123\t private canvas: HTMLCanvasElement;\n124\t private ctx: CanvasRenderingContext2D;\n125\t private previewCanvas: HTMLCanvasElement;\n126\t private previewCtx: CanvasRenderingContext2D;\n127\t private holdCanvas: HTMLCanvasElement;\n128\t private holdCtx: CanvasRenderingContext2D;\n129\t\n130\t private board: Board;\n131\t private current: Piece | null = null;\n132\t private currentRotation = 0;\n133\t private currentTetrominoIndex = -1;\n134\t private nextTetrominoIndex = -1;\n135\t private heldTetrominoIndex = -1;\n136\t private canHold = true;\n137\t\n138\t private score = 0;\n139\t private level = 1;\n140\t private lines = 0;\n141\t private gameOver = false;\n142\t private paused = false;\n143\t private started = false;\n144\t\n145\t private dropInterval = 1000;\n146\t private lastDrop = 0;\n147\t private lockDelay = 500;\n148\t private lockTimer = 0;\n149\t private isLocking = false;\n150\t\n151\t private animatingRows: number[] = [];\n152\t private animationStart = 0;\n153\t private readonly ANIMATION_DURATION = 300;\n154\t\n155\t private bag: number[] = [];\n156\t\n157\t private scoreEl: HTMLElement;\n158\t private levelEl: HTMLElement;\n159\t private linesEl: HTMLElement;\n160\t private messageEl: HTMLElement;\n161\t\n162\t constructor() {\n163\t this.canvas = document.getElementById(\"game-canvas\") as HTMLCanvasElement;\n164\t this.ctx = this.canvas.getContext(\"2d\")!;\n165\t this.canvas.width = COLS * BLOCK_SIZE;\n166\t this.canvas.height = ROWS * BLOCK_SIZE;\n167\t\n168\t this.previewCanvas = document.getElementById(\"preview-canvas\") as HTMLCanvasElement;\n169\t this.previewCtx = this.previewCanvas.getContext(\"2d\")!;\n170\t this.previewCanvas.width = 4 * PREVIEW_BLOCK;\n171\t this.previewCanvas.height = 4 * PREVIEW_BLOCK;\n172\t\n173\t this.holdCanvas = document.getElementById(\"hold-canvas\") as HTMLCanvasElement;\n174\t this.holdCtx = this.holdCanvas.getContext(\"2d\")!;\n175\t this.holdCanvas.width = 4 * PREVIEW_BLOCK;\n176\t this.holdCanvas.height = 4 * PREVIEW_BLOCK;\n177\t\n178\t this.scoreEl = document.getElementById(\"score\")!;\n179\t this.levelEl = document.getElementById(\"level\")!;\n180\t this.linesEl = document.getElementById(\"lines\")!;\n181\t this.messageEl = document.getElementById(\"message\")!;\n182\t\n183\t this.board = this.createBoard();\n184\t\n185\t this.bindKeys();\n186\t this.showMessage(\"Press ENTER to start\");\n187\t this.drawBoard();\n188\t }\n189\t\n190\t // ── Board helpers ──────────────────────────────────────────────────────\n191\t\n192\t private createBoard(): Board {\n193\t return Array.from({ length: ROWS }, () => Array(COLS).fill(null));\n194\t }\n195\t\n196\t // ── 7-bag randomiser ──────────────────────────────────────────────────\n197\t\n198\t private refillBag(): void {\n199\t const indices = [0, 1, 2, 3, 4, 5, 6];\n200\t for (let i = indices.length - 1; i > 0; i--) {\n201\t const j = Math.floor(Math.random() * (i + 1));\n202\t [indices[i], indices[j]] = [indices[j], indices[i]];\n203\t }\n204\t this.bag.push(...indices);\n205\t }\n206\t\n207\t private nextFromBag(): number {\n208\t if (this.bag.length < 2) this.refillBag();\n209\t return this.bag.shift()!;\n210\t }\n211\t\n212\t // ── Piece management ──────────────────────────────────────────────────\n213\t\n214\t private spawnPiece(tetrominoIndex?: number): void {\n215\t const idx = tetrominoIndex ?? this.nextTetrominoIndex;\n216\t this.nextTetrominoIndex = this.nextFromBag();\n217\t\n218\t const t = TETROMINOES[idx];\n219\t this.currentTetrominoIndex = idx;\n220\t this.currentRotation = 0;\n221\t this.current = {\n222\t shape: t.shapes[0],\n223\t color: t.color,\n224\t x: Math.floor((COLS - t.shapes[0][0].length) / 2),\n225\t y: 0,\n226\t };\n227\t this.isLocking = false;\n228\t this.canHold = true;\n229\t\n230\t if (this.collides(this.current.shape, this.current.x, this.current.y)) {\n231\t this.gameOver = true;\n232\t this.showMessage(\"GAME OVER — press ENTER\");\n233\t }\n234\t }\n235\t\n236\t // ── Collision detection ───────────────────────────────────────────────\n237\t\n238\t private collides(shape: Shape, px: number, py: number): boolean {\n239\t for (let r = 0; r < shape.length; r++) {\n240\t for (let c = 0; c < shape[r].length; c++) {\n241\t if (!shape[r][c]) continue;\n242\t const bx = px + c;\n243\t const by = py + r;\n244\t if (bx < 0 || bx >= COLS || by >= ROWS) return true;\n245\t if (by >= 0 && this.board[by][bx]) return true;\n246\t }\n247\t }\n248\t return false;\n249\t }\n250\t\n251\t // ── Movement ──────────────────────────────────────────────────────────\n252\t\n253\t private moveLeft(): void {\n254\t if (!this.current || this.gameOver || this.paused) return;\n255\t if (!this.collides(this.current.shape, this.current.x - 1, this.current.y)) {\n256\t this.current.x--;\n257\t if (this.isLocking) this.lockTimer = performance.now();\n258\t }\n259\t }\n260\t\n261\t private moveRight(): void {\n262\t if (!this.current || this.gameOver || this.paused) return;\n263\t if (!this.collides(this.current.shape, this.current.x + 1, this.current.y)) {\n264\t this.current.x++;\n265\t if (this.isLocking) this.lockTimer = performance.now();\n266\t }\n267\t }\n268\t\n269\t private moveDown(): boolean {\n270\t if (!this.current || this.gameOver || this.paused) return false;\n271\t if (!this.collides(this.current.shape, this.current.x, this.current.y + 1)) {\n272\t this.current.y++;\n273\t this.isLocking = false;\n274\t return true;\n275\t }\n276\t return false;\n277\t }\n278\t\n279\t private softDrop(): void {\n280\t if (this.moveDown()) {\n281\t this.score += SOFT_DROP_POINTS;\n282\t this.updateScore();\n283\t }\n284\t }\n285\t\n286\t private hardDrop(): void {\n287\t if (!this.current || this.gameOver || this.paused) return;\n288\t let dropped = 0;\n289\t while (!this.collides(this.current.shape, this.current.x, this.current.y + 1)) {\n290\t this.current.y++;\n291\t dropped++;\n292\t }\n293\t this.score += dropped * HARD_DROP_POINTS;\n294\t this.updateScore();\n295\t this.lockPiece();\n296\t }\n297\t\n298\t private getGhostY(): number {\n299\t if (!this.current) return 0;\n300\t let gy = this.current.y;\n301\t while (!this.collides(this.current.shape, this.current.x, gy + 1)) {\n302\t gy++;\n303\t }\n304\t return gy;\n305\t }\n306\t\n307\t // ── Rotation with SRS wall kicks ──────────────────────────────────────\n308\t\n309\t private rotate(dir: 1 | -1): void {\n310\t if (!this.current || this.gameOver || this.paused) return;\n311\t\n312\t const t = TETROMINOES[this.currentTetrominoIndex];\n313\t const from = this.currentRotation;\n314\t const to = (from + dir + 4) % 4;\n315\t const newShape = t.shapes[to];\n316\t\n317\t // Basic rotation\n318\t if (!this.collides(newShape, this.current.x, this.current.y)) {\n319\t this.current.shape = newShape;\n320\t this.currentRotation = to;\n321\t if (this.isLocking) this.lockTimer = performance.now();\n322\t return;\n323\t }\n324\t\n325\t // Wall kicks\n326\t const kickKey = `${from}>${to}`;\n327\t const kicks = this.currentTetrominoIndex === 0 ? I_WALL_KICKS[kickKey] : WALL_KICKS[kickKey];\n328\t if (!kicks) return;\n329\t\n330\t for (const [dx, dy] of kicks) {\n331\t if (!this.collides(newShape, this.current.x + dx, this.current.y - dy)) {\n332\t this.current.x += dx;\n333\t this.current.y -= dy;\n334\t this.current.shape = newShape;\n335\t this.currentRotation = to;\n336\t if (this.isLocking) this.lockTimer = performance.now();\n337\t return;\n338\t }\n339\t }\n340\t }\n341\t\n342\t // ── Hold ──────────────────────────────────────────────────────────────\n343\t\n344\t private holdPiece(): void {\n345\t if (!this.current || !this.canHold || this.gameOver || this.paused) return;\n346\t this.canHold = false;\n347\t const prev = this.heldTetrominoIndex;\n348\t this.heldTetrominoIndex = this.currentTetrominoIndex;\n349\t if (prev === -1) {\n350\t this.spawnPiece();\n351\t } else {\n352\t this.spawnPiece(prev);\n353\t }\n354\t }\n355\t\n356\t // ── Lock & clear ─────────────────────────────────────────────────────\n357\t\n358\t private lockPiece(): void {\n359\t if (!this.current) return;\n360\t const { shape, color, x, y } = this.current;\n361\t for (let r = 0; r < shape.length; r++) {\n362\t for (let c = 0; c < shape[r].length; c++) {\n363\t if (!shape[r][c]) continue;\n364\t const by = y + r;\n365\t const bx = x + c;\n366\t if (by >= 0 && by < ROWS && bx >= 0 && bx < COLS) {\n367\t this.board[by][bx] = color;\n368\t }\n369\t }\n370\t }\n371\t this.current = null;\n372\t this.clearLines();\n373\t }\n374\t\n375\t private clearLines(): void {\n376\t const fullRows: number[] = [];\n377\t for (let r = 0; r < ROWS; r++) {\n378\t if (this.board[r].every((c) => c !== null)) {\n379\t fullRows.push(r);\n380\t }\n381\t }\n382\t\n383\t if (fullRows.length === 0) {\n384\t this.spawnPiece();\n385\t return;\n386\t }\n387\t\n388\t // Start line-clear animation\n389\t this.animatingRows = fullRows;\n390\t this.animationStart = performance.now();\n391\t }\n392\t\n393\t private finishClear(): void {\n394\t const fullRows = this.animatingRows;\n395\t this.animatingRows = [];\n396\t\n397\t // Remove rows top-to-bottom so indices stay stable when shifting\n398\t for (const row of fullRows.sort((a, b) => a - b)) {\n399\t this.board.splice(row, 1);\n400\t this.board.unshift(Array(COLS).fill(null));\n401\t }\n402\t\n403\t const cleared = fullRows.length;\n404\t this.lines += cleared;\n405\t this.score += LINE_POINTS[cleared] * this.level;\n406\t this.level = Math.floor(this.lines / 10) + 1;\n407\t this.dropInterval = Math.max(50, 1000 - (this.level - 1) * 80);\n408\t this.updateScore();\n409\t this.spawnPiece();\n410\t }\n411\t\n412\t // ── Rendering ─────────────────────────────────────────────────────────\n413\t\n414\t private drawBlock(\n415\t ctx: CanvasRenderingContext2D,\n416\t x: number,\n417\t y: number,\n418\t color: string,\n419\t size: number,\n420\t alpha = 1,\n421\t ): void {\n422\t ctx.globalAlpha = alpha;\n423\t ctx.fillStyle = color;\n424\t ctx.fillRect(x * size, y * size, size, size);\n425\t\n426\t // Highlight\n427\t ctx.fillStyle = \"rgba(255,255,255,0.25)\";\n428\t ctx.fillRect(x * size, y * size, size, size * 0.15);\n429\t ctx.fillRect(x * size, y * size, size * 0.15, size);\n430\t\n431\t // Shadow\n432\t ctx.fillStyle = \"rgba(0,0,0,0.25)\";\n433\t ctx.fillRect(x * size, y * size + size * 0.85, size, size * 0.15);\n434\t ctx.fillRect(x * size + size * 0.85, y * size, size * 0.15, size);\n435\t\n436\t // Border\n437\t ctx.strokeStyle = \"rgba(0,0,0,0.4)\";\n438\t ctx.lineWidth = 1;\n439\t ctx.strokeRect(x * size + 0.5, y * size + 0.5, size - 1, size - 1);\n440\t\n441\t ctx.globalAlpha = 1;\n442\t }\n443\t\n444\t private drawBoard(): void {\n445\t const ctx = this.ctx;\n446\t // Background\n447\t ctx.fillStyle = \"#111\";\n448\t ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);\n449\t\n450\t // Grid lines\n451\t ctx.strokeStyle = \"rgba(255,255,255,0.04)\";\n452\t ctx.lineWidth = 1;\n453\t for (let r = 0; r <= ROWS; r++) {\n454\t ctx.beginPath();\n455\t ctx.moveTo(0, r * BLOCK_SIZE);\n456\t ctx.lineTo(COLS * BLOCK_SIZE, r * BLOCK_SIZE);\n457\t ctx.stroke();\n458\t }\n459\t for (let c = 0; c <= COLS; c++) {\n460\t ctx.beginPath();\n461\t ctx.moveTo(c * BLOCK_SIZE, 0);\n462\t ctx.lineTo(c * BLOCK_SIZE, ROWS * BLOCK_SIZE);\n463\t ctx.stroke();\n464\t }\n465\t\n466\t // Compute animation flash alpha\n467\t const now = performance.now();\n468\t let flashAlpha = 1;\n469\t if (this.animatingRows.length > 0) {\n470\t const elapsed = now - this.animationStart;\n471\t flashAlpha = 0.3 + 0.7 * Math.abs(Math.sin((elapsed / this.ANIMATION_DURATION) * Math.PI * 3));\n472\t }\n473\t\n474\t // Locked cells\n475\t for (let r = 0; r < ROWS; r++) {\n476\t for (let c = 0; c < COLS; c++) {\n477\t if (this.board[r][c]) {\n478\t const alpha = this.animatingRows.includes(r) ? flashAlpha : 1;\n479\t this.drawBlock(ctx, c, r, this.board[r][c]!, BLOCK_SIZE, alpha);\n480\t }\n481\t }\n482\t }\n483\t\n484\t if (this.current && this.animatingRows.length === 0) {\n485\t // Ghost piece\n486\t const gy = this.getGhostY();\n487\t for (let r = 0; r < this.current.shape.length; r++) {\n488\t for (let c = 0; c < this.current.shape[r].length; c++) {\n489\t if (!this.current.shape[r][c]) continue;\n490\t this.drawBlock(ctx, this.current.x + c, gy + r, this.current.color, BLOCK_SIZE, 0.2);\n491\t }\n492\t }\n493\t\n494\t // Active piece\n495\t for (let r = 0; r < this.current.shape.length; r++) {\n496\t for (let c = 0; c < this.current.shape[r].length; c++) {\n497\t if (!this.current.shape[r][c]) continue;\n498\t const py = this.current.y + r;\n499\t if (py >= 0) {\n500\t this.drawBlock(ctx, this.current.x + c, py, this.current.color, BLOCK_SIZE);\n501\t }\n502\t }\n503\t }\n504\t }\n505\t }\n506\t\n507\t private drawPreview(): void {\n508\t const ctx = this.previewCtx;\n509\t ctx.fillStyle = \"#1a1a2e\";\n510\t ctx.fillRect(0, 0, this.previewCanvas.width, this.previewCanvas.height);\n511\t if (this.nextTetrominoIndex < 0) return;\n512\t const t = TETROMINOES[this.nextTetrominoIndex];\n513\t const shape = t.shapes[0];\n514\t const ox = (4 - shape[0].length) / 2;\n515\t const oy = (4 - shape.length) / 2;\n516\t for (let r = 0; r < shape.length; r++) {\n517\t for (let c = 0; c < shape[r].length; c++) {\n518\t if (shape[r][c]) {\n519\t this.drawBlock(ctx, ox + c, oy + r, t.color, PREVIEW_BLOCK);\n520\t }\n521\t }\n522\t }\n523\t }\n524\t\n525\t private drawHold(): void {\n526\t const ctx = this.holdCtx;\n527\t ctx.fillStyle = \"#1a1a2e\";\n528\t ctx.fillRect(0, 0, this.holdCanvas.width, this.holdCanvas.height);\n529\t if (this.heldTetrominoIndex < 0) return;\n530\t const t = TETROMINOES[this.heldTetrominoIndex];\n531\t const shape = t.shapes[0];\n532\t const ox = (4 - shape[0].length) / 2;\n533\t const oy = (4 - shape.length) / 2;\n534\t const alpha = this.canHold ? 1 : 0.35;\n535\t for (let r = 0; r < shape.length; r++) {\n536\t for (let c = 0; c < shape[r].length; c++) {\n537\t if (shape[r][c]) {\n538\t this.drawBlock(ctx, ox + c, oy + r, t.color, PREVIEW_BLOCK, alpha);\n539\t }\n540\t }\n541\t }\n542\t }\n543\t\n544\t // ── HUD helpers ───────────────────────────────────────────────────────\n545\t\n546\t private updateScore(): void {\n547\t this.scoreEl.textContent = this.score.toString();\n548\t this.levelEl.textContent = this.level.toString();\n549\t this.linesEl.textContent = this.lines.toString();\n550\t }\n551\t\n552\t private showMessage(msg: string): void {\n553\t this.messageEl.textContent = msg;\n554\t this.messageEl.style.display = \"block\";\n555\t }\n556\t\n557\t private hideMessage(): void {\n558\t this.messageEl.style.display = \"none\";\n559\t }\n560\t\n561\t // ── Input ─────────────────────────────────────────────────────────────\n562\t\n563\t private bindKeys(): void {\n564\t document.addEventListener(\"keydown\", (e: KeyboardEvent) => {\n565\t if (e.key === \"Enter\") {\n566\t if (this.gameOver || !this.started) {\n567\t this.reset();\n568\t return;\n569\t }\n570\t }\n571\t\n572\t if (e.key === \"p\" || e.key === \"P\" || e.key === \"Escape\") {\n573\t if (!this.gameOver && this.started) {\n574\t this.paused = !this.paused;\n575\t if (this.paused) {\n576\t this.showMessage(\"PAUSED — press P\");\n577\t } else {\n578\t this.hideMessage();\n579\t this.lastDrop = performance.now();\n580\t }\n581\t }\n582\t return;\n583\t }\n584\t\n585\t if (this.paused || this.gameOver || !this.started) return;\n586\t\n587\t switch (e.key) {\n588\t case \"ArrowLeft\":\n589\t case \"a\":\n590\t e.preventDefault();\n591\t this.moveLeft();\n592\t break;\n593\t case \"ArrowRight\":\n594\t case \"d\":\n595\t e.preventDefault();\n596\t this.moveRight();\n597\t break;\n598\t case \"ArrowDown\":\n599\t case \"s\":\n600\t e.preventDefault();\n601\t this.softDrop();\n602\t break;\n603\t case \"ArrowUp\":\n604\t case \"x\":\n605\t e.preventDefault();\n606\t this.rotate(1);\n607\t break;\n608\t case \"z\":\n609\t case \"Control\":\n610\t e.preventDefault();\n611\t this.rotate(-1);\n612\t break;\n613\t case \" \":\n614\t e.preventDefault();\n615\t this.hardDrop();\n616\t break;\n617\t case \"Shift\":\n618\t case \"c\":\n619\t e.preventDefault();\n620\t this.holdPiece();\n621\t break;\n622\t }\n623\t });\n624\t }\n625\t\n626\t // ── Game loop ─────────────────────────────────────────────────────────\n627\t\n628\t private reset(): void {\n629\t this.board = this.createBoard();\n630\t this.score = 0;\n631\t this.level = 1;\n632\t this.lines = 0;\n633\t this.gameOver = false;\n634\t this.paused = false;\n635\t this.dropInterval = 1000;\n636\t this.lastDrop = performance.now();\n637\t this.isLocking = false;\n638\t this.heldTetrominoIndex = -1;\n639\t this.canHold = true;\n640\t this.bag = [];\n641\t this.animatingRows = [];\n642\t\n643\t this.nextTetrominoIndex = this.nextFromBag();\n644\t this.spawnPiece();\n645\t this.updateScore();\n646\t this.hideMessage();\n647\t\n648\t if (!this.started) {\n649\t this.started = true;\n650\t requestAnimationFrame((t) => this.loop(t));\n651\t }\n652\t }\n653\t\n654\t private loop(now: number): void {\n655\t if (!this.gameOver) {\n656\t requestAnimationFrame((t) => this.loop(t));\n657\t }\n658\t\n659\t if (this.paused) {\n660\t this.drawBoard();\n661\t this.drawPreview();\n662\t this.drawHold();\n663\t return;\n664\t }\n665\t\n666\t // Handle line-clear animation\n667\t if (this.animatingRows.length > 0) {\n668\t if (now - this.animationStart >= this.ANIMATION_DURATION) {\n669\t this.finishClear();\n670\t }\n671\t this.drawBoard();\n672\t this.drawPreview();\n673\t this.drawHold();\n674\t return;\n675\t }\n676\t\n677\t // Gravity\n678\t if (this.current) {\n679\t if (!this.collides(this.current.shape, this.current.x, this.current.y + 1)) {\n680\t if (now - this.lastDrop >= this.dropInterval) {\n681\t this.current.y++;\n682\t this.lastDrop = now;\n683\t this.isLocking = false;\n684\t }\n685\t } else {\n686\t // Piece is resting – start / check lock delay\n687\t if (!this.isLocking) {\n688\t this.isLocking = true;\n689\t this.lockTimer = now;\n690\t } else if (now - this.lockTimer >= this.lockDelay) {\n691\t this.lockPiece();\n692\t this.lastDrop = now;\n693\t }\n694\t }\n695\t }\n696\t\n697\t this.drawBoard();\n698\t this.drawPreview();\n699\t this.drawHold();\n700\t }\n701\t}\n702\t\n703\t// ── Bootstrap ──────────────────────────────────────────────────────────────\n704\t\n705\twindow.addEventListener(\"DOMContentLoaded\", () => {\n706\t new TetrisGame();\n707\t});\n708\t"}]},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"0deba55c-7939-4665-bcd6-ce89f04c3d7a","timestamp":"2026-04-04T21:00:39.278Z","tool_use_result":{"type":"text","file":{"filePath":"/tmp/loop-bench-doqfpofi/src/tetris.ts","content":"// ── Types ──────────────────────────────────────────────────────────────────\n\ntype Cell = string | null;\ntype Board = Cell[][];\ntype Shape = number[][];\n\ninterface Piece {\n shape: Shape;\n color: string;\n x: number;\n y: number;\n}\n\ninterface Tetromino {\n shapes: Shape[];\n color: string;\n}\n\n// ── Constants ──────────────────────────────────────────────────────────────\n\nconst COLS = 10;\nconst ROWS = 20;\nconst BLOCK_SIZE = 30;\nconst PREVIEW_BLOCK = 20;\n\nconst TETROMINOES: Tetromino[] = [\n { // I\n shapes: [\n [[0,0,0,0],[1,1,1,1],[0,0,0,0],[0,0,0,0]],\n [[0,0,1,0],[0,0,1,0],[0,0,1,0],[0,0,1,0]],\n [[0,0,0,0],[0,0,0,0],[1,1,1,1],[0,0,0,0]],\n [[0,1,0,0],[0,1,0,0],[0,1,0,0],[0,1,0,0]],\n ],\n color: \"#00f0f0\",\n },\n { // O\n shapes: [\n [[1,1],[1,1]],\n [[1,1],[1,1]],\n [[1,1],[1,1]],\n [[1,1],[1,1]],\n ],\n color: \"#f0f000\",\n },\n { // T\n shapes: [\n [[0,1,0],[1,1,1],[0,0,0]],\n [[0,1,0],[0,1,1],[0,1,0]],\n [[0,0,0],[1,1,1],[0,1,0]],\n [[0,1,0],[1,1,0],[0,1,0]],\n ],\n color: \"#a000f0\",\n },\n { // S\n shapes: [\n [[0,1,1],[1,1,0],[0,0,0]],\n [[0,1,0],[0,1,1],[0,0,1]],\n [[0,0,0],[0,1,1],[1,1,0]],\n [[1,0,0],[1,1,0],[0,1,0]],\n ],\n color: \"#00f000\",\n },\n { // Z\n shapes: [\n [[1,1,0],[0,1,1],[0,0,0]],\n [[0,0,1],[0,1,1],[0,1,0]],\n [[0,0,0],[1,1,0],[0,1,1]],\n [[0,1,0],[1,1,0],[1,0,0]],\n ],\n color: \"#f00000\",\n },\n { // J\n shapes: [\n [[1,0,0],[1,1,1],[0,0,0]],\n [[0,1,1],[0,1,0],[0,1,0]],\n [[0,0,0],[1,1,1],[0,0,1]],\n [[0,1,0],[0,1,0],[1,1,0]],\n ],\n color: \"#0000f0\",\n },\n { // L\n shapes: [\n [[0,0,1],[1,1,1],[0,0,0]],\n [[0,1,0],[0,1,0],[0,1,1]],\n [[0,0,0],[1,1,1],[1,0,0]],\n [[1,1,0],[0,1,0],[0,1,0]],\n ],\n color: \"#f0a000\",\n },\n];\n\nconst LINE_POINTS = [0, 100, 300, 500, 800];\nconst SOFT_DROP_POINTS = 1;\nconst HARD_DROP_POINTS = 2;\n\n// ── Wall Kick Data (SRS) ──────────────────────────────────────────────────\n\nconst WALL_KICKS: Record<string, [number, number][]> = {\n \"0>1\": [[-1,0],[-1,1],[0,-2],[-1,-2]],\n \"1>0\": [[1,0],[1,-1],[0,2],[1,2]],\n \"1>2\": [[1,0],[1,-1],[0,2],[1,2]],\n \"2>1\": [[-1,0],[-1,1],[0,-2],[-1,-2]],\n \"2>3\": [[1,0],[1,1],[0,-2],[1,-2]],\n \"3>2\": [[-1,0],[-1,-1],[0,2],[-1,2]],\n \"3>0\": [[-1,0],[-1,-1],[0,2],[-1,2]],\n \"0>3\": [[1,0],[1,1],[0,-2],[1,-2]],\n};\n\nconst I_WALL_KICKS: Record<string, [number, number][]> = {\n \"0>1\": [[-2,0],[1,0],[-2,-1],[1,2]],\n \"1>0\": [[2,0],[-1,0],[2,1],[-1,-2]],\n \"1>2\": [[-1,0],[2,0],[-1,2],[2,-1]],\n \"2>1\": [[1,0],[-2,0],[1,-2],[-2,1]],\n \"2>3\": [[2,0],[-1,0],[2,1],[-1,-2]],\n \"3>2\": [[-2,0],[1,0],[-2,-1],[1,2]],\n \"3>0\": [[1,0],[-2,0],[1,-2],[-2,1]],\n \"0>3\": [[-1,0],[2,0],[-1,2],[2,-1]],\n};\n\n// ── Game Class ─────────────────────────────────────────────────────────────\n\nclass TetrisGame {\n private canvas: HTMLCanvasElement;\n private ctx: CanvasRenderingContext2D;\n private previewCanvas: HTMLCanvasElement;\n private previewCtx: CanvasRenderingContext2D;\n private holdCanvas: HTMLCanvasElement;\n private holdCtx: CanvasRenderingContext2D;\n\n private board: Board;\n private current: Piece | null = null;\n private currentRotation = 0;\n private currentTetrominoIndex = -1;\n private nextTetrominoIndex = -1;\n private heldTetrominoIndex = -1;\n private canHold = true;\n\n private score = 0;\n private level = 1;\n private lines = 0;\n private gameOver = false;\n private paused = false;\n private started = false;\n\n private dropInterval = 1000;\n private lastDrop = 0;\n private lockDelay = 500;\n private lockTimer = 0;\n private isLocking = false;\n\n private animatingRows: number[] = [];\n private animationStart = 0;\n private readonly ANIMATION_DURATION = 300;\n\n private bag: number[] = [];\n\n private scoreEl: HTMLElement;\n private levelEl: HTMLElement;\n private linesEl: HTMLElement;\n private messageEl: HTMLElement;\n\n constructor() {\n this.canvas = document.getElementById(\"game-canvas\") as HTMLCanvasElement;\n this.ctx = this.canvas.getContext(\"2d\")!;\n this.canvas.width = COLS * BLOCK_SIZE;\n this.canvas.height = ROWS * BLOCK_SIZE;\n\n this.previewCanvas = document.getElementById(\"preview-canvas\") as HTMLCanvasElement;\n this.previewCtx = this.previewCanvas.getContext(\"2d\")!;\n this.previewCanvas.width = 4 * PREVIEW_BLOCK;\n this.previewCanvas.height = 4 * PREVIEW_BLOCK;\n\n this.holdCanvas = document.getElementById(\"hold-canvas\") as HTMLCanvasElement;\n this.holdCtx = this.holdCanvas.getContext(\"2d\")!;\n this.holdCanvas.width = 4 * PREVIEW_BLOCK;\n this.holdCanvas.height = 4 * PREVIEW_BLOCK;\n\n this.scoreEl = document.getElementById(\"score\")!;\n this.levelEl = document.getElementById(\"level\")!;\n this.linesEl = document.getElementById(\"lines\")!;\n this.messageEl = document.getElementById(\"message\")!;\n\n this.board = this.createBoard();\n\n this.bindKeys();\n this.showMessage(\"Press ENTER to start\");\n this.drawBoard();\n }\n\n // ── Board helpers ──────────────────────────────────────────────────────\n\n private createBoard(): Board {\n return Array.from({ length: ROWS }, () => Array(COLS).fill(null));\n }\n\n // ── 7-bag randomiser ──────────────────────────────────────────────────\n\n private refillBag(): void {\n const indices = [0, 1, 2, 3, 4, 5, 6];\n for (let i = indices.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [indices[i], indices[j]] = [indices[j], indices[i]];\n }\n this.bag.push(...indices);\n }\n\n private nextFromBag(): number {\n if (this.bag.length < 2) this.refillBag();\n return this.bag.shift()!;\n }\n\n // ── Piece management ──────────────────────────────────────────────────\n\n private spawnPiece(tetrominoIndex?: number): void {\n const idx = tetrominoIndex ?? this.nextTetrominoIndex;\n this.nextTetrominoIndex = this.nextFromBag();\n\n const t = TETROMINOES[idx];\n this.currentTetrominoIndex = idx;\n this.currentRotation = 0;\n this.current = {\n shape: t.shapes[0],\n color: t.color,\n x: Math.floor((COLS - t.shapes[0][0].length) / 2),\n y: 0,\n };\n this.isLocking = false;\n this.canHold = true;\n\n if (this.collides(this.current.shape, this.current.x, this.current.y)) {\n this.gameOver = true;\n this.showMessage(\"GAME OVER — press ENTER\");\n }\n }\n\n // ── Collision detection ───────────────────────────────────────────────\n\n private collides(shape: Shape, px: number, py: number): boolean {\n for (let r = 0; r < shape.length; r++) {\n for (let c = 0; c < shape[r].length; c++) {\n if (!shape[r][c]) continue;\n const bx = px + c;\n const by = py + r;\n if (bx < 0 || bx >= COLS || by >= ROWS) return true;\n if (by >= 0 && this.board[by][bx]) return true;\n }\n }\n return false;\n }\n\n // ── Movement ──────────────────────────────────────────────────────────\n\n private moveLeft(): void {\n if (!this.current || this.gameOver || this.paused) return;\n if (!this.collides(this.current.shape, this.current.x - 1, this.current.y)) {\n this.current.x--;\n if (this.isLocking) this.lockTimer = performance.now();\n }\n }\n\n private moveRight(): void {\n if (!this.current || this.gameOver || this.paused) return;\n if (!this.collides(this.current.shape, this.current.x + 1, this.current.y)) {\n this.current.x++;\n if (this.isLocking) this.lockTimer = performance.now();\n }\n }\n\n private moveDown(): boolean {\n if (!this.current || this.gameOver || this.paused) return false;\n if (!this.collides(this.current.shape, this.current.x, this.current.y + 1)) {\n this.current.y++;\n this.isLocking = false;\n return true;\n }\n return false;\n }\n\n private softDrop(): void {\n if (this.moveDown()) {\n this.score += SOFT_DROP_POINTS;\n this.updateScore();\n }\n }\n\n private hardDrop(): void {\n if (!this.current || this.gameOver || this.paused) return;\n let dropped = 0;\n while (!this.collides(this.current.shape, this.current.x, this.current.y + 1)) {\n this.current.y++;\n dropped++;\n }\n this.score += dropped * HARD_DROP_POINTS;\n this.updateScore();\n this.lockPiece();\n }\n\n private getGhostY(): number {\n if (!this.current) return 0;\n let gy = this.current.y;\n while (!this.collides(this.current.shape, this.current.x, gy + 1)) {\n gy++;\n }\n return gy;\n }\n\n // ── Rotation with SRS wall kicks ──────────────────────────────────────\n\n private rotate(dir: 1 | -1): void {\n if (!this.current || this.gameOver || this.paused) return;\n\n const t = TETROMINOES[this.currentTetrominoIndex];\n const from = this.currentRotation;\n const to = (from + dir + 4) % 4;\n const newShape = t.shapes[to];\n\n // Basic rotation\n if (!this.collides(newShape, this.current.x, this.current.y)) {\n this.current.shape = newShape;\n this.currentRotation = to;\n if (this.isLocking) this.lockTimer = performance.now();\n return;\n }\n\n // Wall kicks\n const kickKey = `${from}>${to}`;\n const kicks = this.currentTetrominoIndex === 0 ? I_WALL_KICKS[kickKey] : WALL_KICKS[kickKey];\n if (!kicks) return;\n\n for (const [dx, dy] of kicks) {\n if (!this.collides(newShape, this.current.x + dx, this.current.y - dy)) {\n this.current.x += dx;\n this.current.y -= dy;\n this.current.shape = newShape;\n this.currentRotation = to;\n if (this.isLocking) this.lockTimer = performance.now();\n return;\n }\n }\n }\n\n // ── Hold ──────────────────────────────────────────────────────────────\n\n private holdPiece(): void {\n if (!this.current || !this.canHold || this.gameOver || this.paused) return;\n this.canHold = false;\n const prev = this.heldTetrominoIndex;\n this.heldTetrominoIndex = this.currentTetrominoIndex;\n if (prev === -1) {\n this.spawnPiece();\n } else {\n this.spawnPiece(prev);\n }\n }\n\n // ── Lock & clear ─────────────────────────────────────────────────────\n\n private lockPiece(): void {\n if (!this.current) return;\n const { shape, color, x, y } = this.current;\n for (let r = 0; r < shape.length; r++) {\n for (let c = 0; c < shape[r].length; c++) {\n if (!shape[r][c]) continue;\n const by = y + r;\n const bx = x + c;\n if (by >= 0 && by < ROWS && bx >= 0 && bx < COLS) {\n this.board[by][bx] = color;\n }\n }\n }\n this.current = null;\n this.clearLines();\n }\n\n private clearLines(): void {\n const fullRows: number[] = [];\n for (let r = 0; r < ROWS; r++) {\n if (this.board[r].every((c) => c !== null)) {\n fullRows.push(r);\n }\n }\n\n if (fullRows.length === 0) {\n this.spawnPiece();\n return;\n }\n\n // Start line-clear animation\n this.animatingRows = fullRows;\n this.animationStart = performance.now();\n }\n\n private finishClear(): void {\n const fullRows = this.animatingRows;\n this.animatingRows = [];\n\n // Remove rows top-to-bottom so indices stay stable when shifting\n for (const row of fullRows.sort((a, b) => a - b)) {\n this.board.splice(row, 1);\n this.board.unshift(Array(COLS).fill(null));\n }\n\n const cleared = fullRows.length;\n this.lines += cleared;\n this.score += LINE_POINTS[cleared] * this.level;\n this.level = Math.floor(this.lines / 10) + 1;\n this.dropInterval = Math.max(50, 1000 - (this.level - 1) * 80);\n this.updateScore();\n this.spawnPiece();\n }\n\n // ── Rendering ─────────────────────────────────────────────────────────\n\n private drawBlock(\n ctx: CanvasRenderingContext2D,\n x: number,\n y: number,\n color: string,\n size: number,\n alpha = 1,\n ): void {\n ctx.globalAlpha = alpha;\n ctx.fillStyle = color;\n ctx.fillRect(x * size, y * size, size, size);\n\n // Highlight\n ctx.fillStyle = \"rgba(255,255,255,0.25)\";\n ctx.fillRect(x * size, y * size, size, size * 0.15);\n ctx.fillRect(x * size, y * size, size * 0.15, size);\n\n // Shadow\n ctx.fillStyle = \"rgba(0,0,0,0.25)\";\n ctx.fillRect(x * size, y * size + size * 0.85, size, size * 0.15);\n ctx.fillRect(x * size + size * 0.85, y * size, size * 0.15, size);\n\n // Border\n ctx.strokeStyle = \"rgba(0,0,0,0.4)\";\n ctx.lineWidth = 1;\n ctx.strokeRect(x * size + 0.5, y * size + 0.5, size - 1, size - 1);\n\n ctx.globalAlpha = 1;\n }\n\n private drawBoard(): void {\n const ctx = this.ctx;\n // Background\n ctx.fillStyle = \"#111\";\n ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);\n\n // Grid lines\n ctx.strokeStyle = \"rgba(255,255,255,0.04)\";\n ctx.lineWidth = 1;\n for (let r = 0; r <= ROWS; r++) {\n ctx.beginPath();\n ctx.moveTo(0, r * BLOCK_SIZE);\n ctx.lineTo(COLS * BLOCK_SIZE, r * BLOCK_SIZE);\n ctx.stroke();\n }\n for (let c = 0; c <= COLS; c++) {\n ctx.beginPath();\n ctx.moveTo(c * BLOCK_SIZE, 0);\n ctx.lineTo(c * BLOCK_SIZE, ROWS * BLOCK_SIZE);\n ctx.stroke();\n }\n\n // Compute animation flash alpha\n const now = performance.now();\n let flashAlpha = 1;\n if (this.animatingRows.length > 0) {\n const elapsed = now - this.animationStart;\n flashAlpha = 0.3 + 0.7 * Math.abs(Math.sin((elapsed / this.ANIMATION_DURATION) * Math.PI * 3));\n }\n\n // Locked cells\n for (let r = 0; r < ROWS; r++) {\n for (let c = 0; c < COLS; c++) {\n if (this.board[r][c]) {\n const alpha = this.animatingRows.includes(r) ? flashAlpha : 1;\n this.drawBlock(ctx, c, r, this.board[r][c]!, BLOCK_SIZE, alpha);\n }\n }\n }\n\n if (this.current && this.animatingRows.length === 0) {\n // Ghost piece\n const gy = this.getGhostY();\n for (let r = 0; r < this.current.shape.length; r++) {\n for (let c = 0; c < this.current.shape[r].length; c++) {\n if (!this.current.shape[r][c]) continue;\n this.drawBlock(ctx, this.current.x + c, gy + r, this.current.color, BLOCK_SIZE, 0.2);\n }\n }\n\n // Active piece\n for (let r = 0; r < this.current.shape.length; r++) {\n for (let c = 0; c < this.current.shape[r].length; c++) {\n if (!this.current.shape[r][c]) continue;\n const py = this.current.y + r;\n if (py >= 0) {\n this.drawBlock(ctx, this.current.x + c, py, this.current.color, BLOCK_SIZE);\n }\n }\n }\n }\n }\n\n private drawPreview(): void {\n const ctx = this.previewCtx;\n ctx.fillStyle = \"#1a1a2e\";\n ctx.fillRect(0, 0, this.previewCanvas.width, this.previewCanvas.height);\n if (this.nextTetrominoIndex < 0) return;\n const t = TETROMINOES[this.nextTetrominoIndex];\n const shape = t.shapes[0];\n const ox = (4 - shape[0].length) / 2;\n const oy = (4 - shape.length) / 2;\n for (let r = 0; r < shape.length; r++) {\n for (let c = 0; c < shape[r].length; c++) {\n if (shape[r][c]) {\n this.drawBlock(ctx, ox + c, oy + r, t.color, PREVIEW_BLOCK);\n }\n }\n }\n }\n\n private drawHold(): void {\n const ctx = this.holdCtx;\n ctx.fillStyle = \"#1a1a2e\";\n ctx.fillRect(0, 0, this.holdCanvas.width, this.holdCanvas.height);\n if (this.heldTetrominoIndex < 0) return;\n const t = TETROMINOES[this.heldTetrominoIndex];\n const shape = t.shapes[0];\n const ox = (4 - shape[0].length) / 2;\n const oy = (4 - shape.length) / 2;\n const alpha = this.canHold ? 1 : 0.35;\n for (let r = 0; r < shape.length; r++) {\n for (let c = 0; c < shape[r].length; c++) {\n if (shape[r][c]) {\n this.drawBlock(ctx, ox + c, oy + r, t.color, PREVIEW_BLOCK, alpha);\n }\n }\n }\n }\n\n // ── HUD helpers ───────────────────────────────────────────────────────\n\n private updateScore(): void {\n this.scoreEl.textContent = this.score.toString();\n this.levelEl.textContent = this.level.toString();\n this.linesEl.textContent = this.lines.toString();\n }\n\n private showMessage(msg: string): void {\n this.messageEl.textContent = msg;\n this.messageEl.style.display = \"block\";\n }\n\n private hideMessage(): void {\n this.messageEl.style.display = \"none\";\n }\n\n // ── Input ─────────────────────────────────────────────────────────────\n\n private bindKeys(): void {\n document.addEventListener(\"keydown\", (e: KeyboardEvent) => {\n if (e.key === \"Enter\") {\n if (this.gameOver || !this.started) {\n this.reset();\n return;\n }\n }\n\n if (e.key === \"p\" || e.key === \"P\" || e.key === \"Escape\") {\n if (!this.gameOver && this.started) {\n this.paused = !this.paused;\n if (this.paused) {\n this.showMessage(\"PAUSED — press P\");\n } else {\n this.hideMessage();\n this.lastDrop = performance.now();\n }\n }\n return;\n }\n\n if (this.paused || this.gameOver || !this.started) return;\n\n switch (e.key) {\n case \"ArrowLeft\":\n case \"a\":\n e.preventDefault();\n this.moveLeft();\n break;\n case \"ArrowRight\":\n case \"d\":\n e.preventDefault();\n this.moveRight();\n break;\n case \"ArrowDown\":\n case \"s\":\n e.preventDefault();\n this.softDrop();\n break;\n case \"ArrowUp\":\n case \"x\":\n e.preventDefault();\n this.rotate(1);\n break;\n case \"z\":\n case \"Control\":\n e.preventDefault();\n this.rotate(-1);\n break;\n case \" \":\n e.preventDefault();\n this.hardDrop();\n break;\n case \"Shift\":\n case \"c\":\n e.preventDefault();\n this.holdPiece();\n break;\n }\n });\n }\n\n // ── Game loop ─────────────────────────────────────────────────────────\n\n private reset(): void {\n this.board = this.createBoard();\n this.score = 0;\n this.level = 1;\n this.lines = 0;\n this.gameOver = false;\n this.paused = false;\n this.dropInterval = 1000;\n this.lastDrop = performance.now();\n this.isLocking = false;\n this.heldTetrominoIndex = -1;\n this.canHold = true;\n this.bag = [];\n this.animatingRows = [];\n\n this.nextTetrominoIndex = this.nextFromBag();\n this.spawnPiece();\n this.updateScore();\n this.hideMessage();\n\n if (!this.started) {\n this.started = true;\n requestAnimationFrame((t) => this.loop(t));\n }\n }\n\n private loop(now: number): void {\n if (!this.gameOver) {\n requestAnimationFrame((t) => this.loop(t));\n }\n\n if (this.paused) {\n this.drawBoard();\n this.drawPreview();\n this.drawHold();\n return;\n }\n\n // Handle line-clear animation\n if (this.animatingRows.length > 0) {\n if (now - this.animationStart >= this.ANIMATION_DURATION) {\n this.finishClear();\n }\n this.drawBoard();\n this.drawPreview();\n this.drawHold();\n return;\n }\n\n // Gravity\n if (this.current) {\n if (!this.collides(this.current.shape, this.current.x, this.current.y + 1)) {\n if (now - this.lastDrop >= this.dropInterval) {\n this.current.y++;\n this.lastDrop = now;\n this.isLocking = false;\n }\n } else {\n // Piece is resting – start / check lock delay\n if (!this.isLocking) {\n this.isLocking = true;\n this.lockTimer = now;\n } else if (now - this.lockTimer >= this.lockDelay) {\n this.lockPiece();\n this.lastDrop = now;\n }\n }\n }\n\n this.drawBoard();\n this.drawPreview();\n this.drawHold();\n }\n}\n\n// ── Bootstrap ──────────────────────────────────────────────────────────────\n\nwindow.addEventListener(\"DOMContentLoaded\", () => {\n new TetrisGame();\n});\n","numLines":708,"startLine":1,"totalLines":708}}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01VmHZQebhLx1F8hEanLVGcg","type":"message","role":"assistant","content":[{"type":"text","text":"Everything is clean and working. Here's a summary of what was built:\n\n## Tetris Game — Complete Implementation\n\n### Files created\n\n| File | Purpose |\n|---|---|\n| `src/tetris.ts` | Full game logic (~700 lines of TypeScript) |\n| `index.html` | Game UI with canvas, side panels, and controls reference |\n| `tsconfig.json` | TypeScript → ES2020 module compilation |\n| `playwright.config.ts` | Test runner configuration |\n| `tests/tetris.spec.ts` | 11 Playwright end-to-end tests |\n\n### Game features\n\n- **All 7 tetrominoes** (I, O, T, S, Z, J, L) with correct SRS rotation states\n- **SRS wall kicks** — both standard and I-piece kick tables so rotations work against walls and other pieces\n- **7-bag randomizer** — guarantees all 7 pieces appear before any repeats\n- **Ghost piece** — translucent preview showing where the piece will land\n- **Hold piece** — swap the current piece into hold (once per drop)\n- **Next piece preview**\n- **Line clearing** with flash animation\n- **Scoring** — soft drop (1pt/row), hard drop (2pt/row), line clears (100/300/500/800 × level)\n- **Increasing speed** — level increases every 10 lines; drop interval decreases from 1000ms down to 50ms\n- **Lock delay** — 500ms grace period to slide/rotate before a piece locks\n- **Pause** (P/Escape), **Game Over** detection, and **Restart** (Enter)\n\n### Controls\n\n| Key | Action |\n|---|---|\n| ← → / A D | Move left/right |\n| ↓ / S | Soft drop |\n| ↑ / X | Rotate clockwise |\n| Z / Ctrl | Rotate counter-clockwise |\n| Space | Hard drop |\n| Shift / C | Hold piece |\n| P / Escape | Pause |\n| Enter | Start / Restart |\n\n### Tests — 11/11 passing ✅\n\nAll tests verify: rendering, start/pause flow, scoring on soft & hard drops, movement, rotation, hold, game-over restart, and line clearing."}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":9028,"cache_read_input_tokens":18179,"cache_creation":{"ephemeral_5m_input_tokens":9028,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","uuid":"ab420bce-5e25-4da8-951a-ef19c656f8c9"} +{"type":"result","subtype":"success","is_error":false,"duration_ms":196994,"duration_api_ms":186977,"num_turns":18,"result":"Everything is clean and working. Here's a summary of what was built:\n\n## Tetris Game — Complete Implementation\n\n### Files created\n\n| File | Purpose |\n|---|---|\n| `src/tetris.ts` | Full game logic (~700 lines of TypeScript) |\n| `index.html` | Game UI with canvas, side panels, and controls reference |\n| `tsconfig.json` | TypeScript → ES2020 module compilation |\n| `playwright.config.ts` | Test runner configuration |\n| `tests/tetris.spec.ts` | 11 Playwright end-to-end tests |\n\n### Game features\n\n- **All 7 tetrominoes** (I, O, T, S, Z, J, L) with correct SRS rotation states\n- **SRS wall kicks** — both standard and I-piece kick tables so rotations work against walls and other pieces\n- **7-bag randomizer** — guarantees all 7 pieces appear before any repeats\n- **Ghost piece** — translucent preview showing where the piece will land\n- **Hold piece** — swap the current piece into hold (once per drop)\n- **Next piece preview**\n- **Line clearing** with flash animation\n- **Scoring** — soft drop (1pt/row), hard drop (2pt/row), line clears (100/300/500/800 × level)\n- **Increasing speed** — level increases every 10 lines; drop interval decreases from 1000ms down to 50ms\n- **Lock delay** — 500ms grace period to slide/rotate before a piece locks\n- **Pause** (P/Escape), **Game Over** detection, and **Restart** (Enter)\n\n### Controls\n\n| Key | Action |\n|---|---|\n| ← → / A D | Move left/right |\n| ↓ / S | Soft drop |\n| ↑ / X | Rotate clockwise |\n| Z / Ctrl | Rotate counter-clockwise |\n| Space | Hard drop |\n| Shift / C | Hold piece |\n| P / Escape | Pause |\n| Enter | Start / Restart |\n\n### Tests — 11/11 passing ✅\n\nAll tests verify: rendering, start/pause flow, scoring on soft & hard drops, movement, rotation, hold, game-over restart, and line clearing.","stop_reason":"end_turn","session_id":"7d4aa499-1f76-4632-befb-cb27cacdc4e0","total_cost_usd":0.6086812500000001,"usage":{"input_tokens":20,"cache_creation_input_tokens":29327,"cache_read_input_tokens":204675,"output_tokens":12918,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":29327},"inference_geo":"","iterations":[],"speed":"standard"},"modelUsage":{"claude-opus-4-6":{"inputTokens":20,"outputTokens":12918,"cacheReadInputTokens":204675,"cacheCreationInputTokens":29327,"webSearchRequests":0,"costUSD":0.6086812500000001,"contextWindow":200000,"maxOutputTokens":64000}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","uuid":"2c8d8e7d-0a7a-47b3-9d55-a9b16c24b9fe"} diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/claude_output.json b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/claude_output.json @@ -0,0 +1 @@ +{"type":"result","subtype":"success","is_error":true,"duration_ms":256423,"duration_api_ms":254300,"num_turns":14,"result":"Invalid API key · Fix external API key","stop_reason":"stop_sequence","session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","total_cost_usd":0.6562947500000003,"usage":{"input_tokens":16,"cache_creation_input_tokens":17271,"cache_read_input_tokens":128742,"output_tokens":19356,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":17271},"inference_geo":"","iterations":[],"speed":"standard"},"modelUsage":{"claude-opus-4-6":{"inputTokens":16,"outputTokens":19356,"cacheReadInputTokens":128742,"cacheCreationInputTokens":17271,"webSearchRequests":0,"costUSD":0.6562947500000003,"contextWindow":200000,"maxOutputTokens":64000}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","uuid":"85cd0423-1af4-44f9-8216-9c369268123c"} +\ No newline at end of file diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/claude_stderr.log b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/claude_stderr.log diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/eval_results.json b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/eval_results.json @@ -0,0 +1,263 @@ +{ + "structural": { + "pass": true, + "checks": [ + { + "name": "entry_point_exists", + "pass": true, + "detail": "index.html found" + }, + { + "name": "package_json_exists", + "pass": true, + "detail": "package.json found" + }, + { + "name": "build_succeeds", + "pass": true, + "detail": "npm run build completed successfully" + }, + { + "name": "typescript_compiles", + "pass": true, + "detail": "tsc --noEmit passed" + } + ], + "score": 1.0 + }, + "functional": { + "pass": false, + "error": "playwright eval not yet wired", + "score": 0 + }, + "quality": { + "lint": { + "pass": true, + "errors": 0, + "warnings": 0 + }, + "typecheck": { + "pass": true + }, + "performance": { + "bundle_size_bytes": 51249, + "size_under_512kb": true + }, + "score": 0.67 + }, + "code_analysis": { + "files": { + "total": 7, + "code": 3, + "docs": 0, + "unnecessary": 0, + "unnecessary_list": [] + }, + "lines_of_code": 1607, + "dependencies": { + "production": 0, + "dev": 5, + "total": 5 + }, + "complexity": "moderate", + "console_logs": 0, + "magic_numbers": { + "count": 54, + "excessive": true + }, + "function_length": { + "count": 81, + "average": 7.0, + "max": 35, + "long_functions": 0 + }, + "max_nesting_depth": 12, + "global_declarations": 40, + "naming": { + "dominant_style": "camelCase", + "consistency_pct": 100.0, + "camel_case": 529, + "snake_case": 0 + }, + "error_handling": { + "try_catch_blocks": 0, + "has_error_handling": false + }, + "comments": { + "comment_lines": 99, + "source_lines": 1201, + "ratio_pct": 8.2 + }, + "separation_of_concerns": { + "verdict": "mixed", + "files_with_rendering": 2, + "files_with_logic": 2, + "files_with_both": 2 + }, + "html_validation": { + "valid": false, + "errors": 9 + }, + "duplication_percentage": 0.0, + "score": 0.85 + }, + "transcript_analysis": { + "total_events": 42, + "tool_calls": { + "total": 13, + "bash": 8, + "write": 1, + "edit": 1, + "read": 3 + }, + "wasted_turns": { + "total": 0, + "docs": 0, + "ascii_art": 0, + "server_starts": 0 + }, + "errors_encountered": 0, + "thinking_blocks": 1, + "text_blocks": 11, + "productivity_ratio": 1.0, + "self_tested": false, + "score": 1.0 + }, + "gameplay_bot": { + "pass": false, + "score": 0.69, + "total": 16, + "passed": 11, + "failed": 5, + "report": { + "implementation": { + "renderer": "canvas", + "grid_detected": true, + "grid_bounds": { + "x": 0, + "y": 0, + "width": 30, + "height": 60 + }, + "controls": { + "left": "ArrowLeft", + "right": "ArrowRight", + "down": "ArrowDown", + "rotate": "x", + "drop": "Space" + }, + "start_mechanism": "enter", + "score_element_found": true + }, + "tests": [ + { + "name": "game_loads", + "pass": true, + "detail": "no console errors" + }, + { + "name": "game_starts", + "pass": true, + "detail": "started via enter" + }, + { + "name": "auto_drop", + "pass": true, + "detail": "pixels changed after 5s with no input" + }, + { + "name": "move_left", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "move_right", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "move_down", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "rotate", + "pass": false, + "detail": "no change detected after rotate key" + }, + { + "name": "all_pieces_rotate", + "pass": false, + "detail": "could not detect any piece rotations" + }, + { + "name": "hard_drop", + "pass": true, + "detail": "visual change detected after hard drop" + }, + { + "name": "piece_locks", + "pass": false, + "detail": "could not verify piece locking at bottom" + }, + { + "name": "new_piece_spawns", + "pass": true, + "detail": "visual change suggests new piece spawned" + }, + { + "name": "multiple_pieces", + "pass": true, + "detail": "game still responding after 10 piece drops" + }, + { + "name": "line_clear", + "pass": true, + "detail": "line cleared via strategic placement" + }, + { + "name": "score_changes", + "pass": false, + "detail": "score did not increase: [208] -> no change after polling" + }, + { + "name": "game_over", + "pass": false, + "detail": "could not trigger or detect game over" + }, + { + "name": "playable_30s", + "pass": true, + "detail": "played for 30s, placed 79 pieces, no crashes" + } + ], + "summary": { + "total": 16, + "passed": 11, + "failed": 5, + "score": 0.69 + }, + "gameplay": { + "pieces_placed": 189, + "lines_cleared": 1, + "max_score_observed": 72, + "play_duration_seconds": 30, + "errors_during_play": 0 + }, + "performance": { + "load_time_ms": 27 + }, + "accessibility": { + "issues": [ + "no headings found", + "canvas without aria-label or role", + "canvas without aria-label or role", + "canvas without aria-label or role" + ], + "issue_count": 4, + "pass": false + } + } + }, + "score": 0.7155 +} +\ No newline at end of file diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/gameplay-bot-report.json b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/gameplay-bot-report.json @@ -0,0 +1,129 @@ +{ + "implementation": { + "renderer": "canvas", + "grid_detected": true, + "grid_bounds": { + "x": 0, + "y": 0, + "width": 30, + "height": 60 + }, + "controls": { + "left": "ArrowLeft", + "right": "ArrowRight", + "down": "ArrowDown", + "rotate": "x", + "drop": "Space" + }, + "start_mechanism": "enter", + "score_element_found": true + }, + "tests": [ + { + "name": "game_loads", + "pass": true, + "detail": "no console errors" + }, + { + "name": "game_starts", + "pass": true, + "detail": "started via enter" + }, + { + "name": "auto_drop", + "pass": true, + "detail": "pixels changed after 5s with no input" + }, + { + "name": "move_left", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "move_right", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "move_down", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "rotate", + "pass": false, + "detail": "no change detected after rotate key" + }, + { + "name": "all_pieces_rotate", + "pass": false, + "detail": "could not detect any piece rotations" + }, + { + "name": "hard_drop", + "pass": true, + "detail": "visual change detected after hard drop" + }, + { + "name": "piece_locks", + "pass": false, + "detail": "could not verify piece locking at bottom" + }, + { + "name": "new_piece_spawns", + "pass": true, + "detail": "visual change suggests new piece spawned" + }, + { + "name": "multiple_pieces", + "pass": true, + "detail": "game still responding after 10 piece drops" + }, + { + "name": "line_clear", + "pass": true, + "detail": "line cleared via strategic placement" + }, + { + "name": "score_changes", + "pass": false, + "detail": "score did not increase: [208] -> no change after polling" + }, + { + "name": "game_over", + "pass": false, + "detail": "could not trigger or detect game over" + }, + { + "name": "playable_30s", + "pass": true, + "detail": "played for 30s, placed 79 pieces, no crashes" + } + ], + "summary": { + "total": 16, + "passed": 11, + "failed": 5, + "score": 0.69 + }, + "gameplay": { + "pieces_placed": 189, + "lines_cleared": 1, + "max_score_observed": 72, + "play_duration_seconds": 30, + "errors_during_play": 0 + }, + "performance": { + "load_time_ms": 27 + }, + "accessibility": { + "issues": [ + "no headings found", + "canvas without aria-label or role", + "canvas without aria-label or role", + "canvas without aria-label or role" + ], + "issue_count": 4, + "pass": false + } +} +\ No newline at end of file diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/meta.json b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/meta.json @@ -0,0 +1,30 @@ +{ + "model": "opus", + "effort": "high", + "prompt_style": "simple", + "language": "typescript", + "human_language": "en", + "tool_read": "on", + "tool_write": "on", + "tool_edit": "on", + "tool_glob": "on", + "tool_grep": "on", + "linter": "on", + "playwright": "on", + "context_file": "none", + "sub_agents": "on", + "web_search": "on", + "max_budget": "low", + "task": "tetris", + "cell_id": "tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on", + "runs_per_cell": 3, + "timeout_seconds": 1200, + "max_budget_usd": 2.0, + "run_id": "tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2", + "run_number": 2, + "claude_version": "2.1.92 (Claude Code)", + "started_at": "2026-04-04T20:57:28.504633+00:00", + "wall_time_seconds": 257, + "exit_code": 1, + "completed_at": "2026-04-04T21:01:53.108336+00:00" +} +\ No newline at end of file diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/transcript.jsonl b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/transcript.jsonl @@ -0,0 +1,42 @@ +{"type": "harness", "subtype": "config", "model": "opus", "effort": "high", "tools": ["Bash", "Read", "Write", "Edit", "Glob", "Grep", "Agent", "WebSearch", "WebFetch"], "max_budget_usd": 2.0, "timeout_seconds": 1200, "task": "tetris", "language": "typescript", "prompt_style": "simple"} +{"type": "user", "subtype": "prompt", "message": {"role": "user", "content": "Build a playable Tetris game that runs in a browser. It should have all the standard mechanics: piece rotation, line clearing, scoring, and increasing speed. Use keyboard controls for movement and rotation.\n\n\nUse TypeScript."}} +{"type":"system","subtype":"init","cwd":"/tmp/loop-bench-gt1ikbl4","session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","tools":["Bash","Edit","Read"],"mcp_servers":[],"model":"claude-opus-4-6","permissionMode":"dontAsk","slash_commands":["update-config","debug","simplify","batch","loop","schedule","claude-api","compact","context","cost","heapdump","init","review","security-review","insights"],"apiKeySource":"apiKeyHelper","claude_code_version":"2.1.92","output_style":"default","agents":["general-purpose","statusline-setup","Explore","Plan"],"skills":["update-config","debug","simplify","batch","loop","schedule","claude-api"],"plugins":[{"name":"claude-hud","path":"/root/.claude/plugins/marketplaces/claude-hud/","source":"claude-hud@claude-hud"},{"name":"rust-analyzer-lsp","path":"/root/.claude/plugins/marketplaces/claude-plugins-official/plugins/rust-analyzer-lsp","source":"rust-analyzer-lsp@claude-plugins-official"}],"uuid":"0490ad21-f642-48f1-9254-542400fdf4f1","fast_mode_state":"off"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01GEP5rgJKgZm5UKCtgUEVad","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"The user wants me to build a playable Tetris game in TypeScript that runs in a browser. Let me create a complete implementation with all standard mechanics.\n\nLet me plan the structure:\n1. `index.html` - The HTML page\n2. `style.css` - Styling\n3. `game.ts` - Main game logic\n4. `tsconfig.json` - TypeScript config\n5. `package.json` - For build tooling\n\nActually, let me keep it simpler and use a single TypeScript file that gets compiled, or I could use multiple files. Let me think about the best approach.\n\n\nI'll organize this with an HTML shell, a single well-structured TypeScript file for all the game logic, and the necessary configuration files for building and running it.","signature":"EpkHClkIDBgCKkDFF4EoMnlqGdSK+nVXJHXD+MqR/pRjpg1+4RDsSSHdAT2ZKedgXRA1H093Tc2ClnCtI0lr0v+MpRKY8desQQSwMg9jbGF1ZGUtb3B1cy00LTY4ABIMjJNBYzrDe5fVU1DWGgyBUg4pCT4bGRo7FOkiMLXAWfFTSQHaYBLje/+QGWOmtMO4+Jgy2N2n/UTtr8Pjv9UkaW54P6tDcujPJK6wXyrtBc/Qg9VbbCH2kH/SeAHvvidVWxfnPZ5qnoHBcnK3C1Na9OQVLZ7hQ8GHXUVj69nMXSDj4l8IR8tBA8xXjvdSfiQ5iuG5wVKdYz5v4Rj+vvRbO72bs+e8KdyV2CyzstpNAl3L8LZBu7Rsgy1VJCU1thLw8QsUKfaKNTXuf5I5TmzThLUo8R1OxjqUeqgMdjflP1xWtVCYFqx1ErTIZAhbAT1J68BYUeZvfp/cM5EpPyzBnsCiUQ90W0IFFCyTD99rr4x6d7F3V3i3Ay/URxwYcx//e9vtUNKJOL5ru0wWd+8ziYB83KvOanXofVqdMStQVAu8mQNJSE7hGktrs8kgwCdfOvetaEGZDt0X3yYHJE96BWRzKQVYXGiJjSOTY8EeuryisjU9Z2FfRoASYuAeUYdJP2vTbPVXjfo3i43qMY2n7lfpRLjHeS4xw3yj1oxf+O4/AxaZBI3x5j084CgkleQLZB15lCwiKHp7K4fcnannfDdOF9kowp49cuqD2UMZrXe/kZbYzxRXa6gRmVho2Yt+a7NQ1SEKncbKO/cJtoBAPEms833OjJ4CqwcoidTlSnwuNdykHpgvySLeY4V1o0v47QFHHUU8GpR72pNY4ePd+QZE0DFBfkswEKT9FM4yKuz0epgS8Z5WPu7x2SSP2f7+XqjktFn17ADxOJ24VRenMJRnhlsiQHL7JHdrAFGZLEP8XxjWnLhWwE7ZHJ24vksZWpa2j3oDmzBt6dvXCJ2yRjg6cmSFp2yzw7STSf/CvqymWjwY1wcV9xjVApV31J6fK+NK8Mf6IcVTfqLbAXWP/jtnNh+dnOe0GqIsU9TDpesZHvLwkdvBEe2Vgxyr8UA53UJpTFsWn/n7raBjwqEOk4BJ/cHv+wkYFut6KswNzXOFIjngeU6R8ZbPSQGQAXCc9pmsJwMOe3bYlut3FQM0iv41T5/I1z2UpPXbNOWyp/6Mmwex4Dnle0PTXIPI4BzyeQDKfAWXt0KMpkJPGAE="}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":4778,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":4778,"ephemeral_1h_input_tokens":0},"output_tokens":43,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"ffc00268-eb30-45d5-9931-05a635d70be4"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01GEP5rgJKgZm5UKCtgUEVad","type":"message","role":"assistant","content":[{"type":"text","text":"I'll build a full-featured Tetris game in TypeScript. Let me start by setting up the project structure and then implement all the game logic."}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":4778,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":4778,"ephemeral_1h_input_tokens":0},"output_tokens":43,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"cfbf16cc-9941-417c-8567-d30c3506cb02"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01GEP5rgJKgZm5UKCtgUEVad","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01As2FzT5qcVhv6euuftxJqG","name":"Bash","input":{"command":"ls /tmp/loop-bench-gt1ikbl4","description":"List current directory contents"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":4778,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":4778,"ephemeral_1h_input_tokens":0},"output_tokens":43,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"14ee0184-475b-416b-96c4-e150d5d46084"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01As2FzT5qcVhv6euuftxJqG","type":"tool_result","content":"node_modules\npackage.json\npackage-lock.json","is_error":false}]},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"0eb1373a-6dcb-4e99-bfad-26082db720e0","timestamp":"2026-04-04T20:57:44.135Z","tool_use_result":{"stdout":"node_modules\npackage.json\npackage-lock.json","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_018A6B2Meg44cnf3KxKmW25v","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01TnKLfxaP7iCWbHopdNFoRr","name":"Read","input":{"file_path":"/tmp/loop-bench-gt1ikbl4/package.json"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":361,"cache_read_input_tokens":4778,"cache_creation":{"ephemeral_5m_input_tokens":361,"ephemeral_1h_input_tokens":0},"output_tokens":65,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"da64081a-2c92-46ab-9813-7ccb564b01b0"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01TnKLfxaP7iCWbHopdNFoRr","type":"tool_result","content":"1\t{\n2\t \"name\": \"loop-bench-gt1ikbl4\",\n3\t \"version\": \"1.0.0\",\n4\t \"description\": \"\",\n5\t \"main\": \"index.js\",\n6\t \"scripts\": {\n7\t \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n8\t },\n9\t \"keywords\": [],\n10\t \"author\": \"\",\n11\t \"license\": \"ISC\",\n12\t \"type\": \"commonjs\",\n13\t \"devDependencies\": {\n14\t \"@eslint/js\": \"^10.0.1\",\n15\t \"@playwright/test\": \"^1.59.1\",\n16\t \"@types/node\": \"^25.5.2\",\n17\t \"eslint\": \"^10.2.0\",\n18\t \"typescript\": \"^6.0.2\"\n19\t }\n20\t}\n21\t"}]},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"49867f6d-a3aa-443b-98cd-f96961aa4bd7","timestamp":"2026-04-04T20:57:46.762Z","tool_use_result":{"type":"text","file":{"filePath":"/tmp/loop-bench-gt1ikbl4/package.json","content":"{\n \"name\": \"loop-bench-gt1ikbl4\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },\n \"keywords\": [],\n \"author\": \"\",\n \"license\": \"ISC\",\n \"type\": \"commonjs\",\n \"devDependencies\": {\n \"@eslint/js\": \"^10.0.1\",\n \"@playwright/test\": \"^1.59.1\",\n \"@types/node\": \"^25.5.2\",\n \"eslint\": \"^10.2.0\",\n \"typescript\": \"^6.0.2\"\n }\n}\n","numLines":21,"startLine":1,"totalLines":21}}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_0144arGP9gYNHiVXjC8kHbHu","type":"message","role":"assistant","content":[{"type":"text","text":"Good, TypeScript is already available. Let me set up the project files."}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":674,"cache_read_input_tokens":4778,"cache_creation":{"ephemeral_5m_input_tokens":674,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"670e7651-12b1-42c1-96f8-9edf1e477b4d"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_0144arGP9gYNHiVXjC8kHbHu","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01V1h2FeoepgFtTDKirCwzHz","name":"Bash","input":{"command":"mkdir -p /tmp/loop-bench-gt1ikbl4/src","description":"Create src directory"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":674,"cache_read_input_tokens":4778,"cache_creation":{"ephemeral_5m_input_tokens":674,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"be12a23f-7b98-4d7f-b0cd-9540852b05b4"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01V1h2FeoepgFtTDKirCwzHz","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"0dda723c-8895-4534-a7b6-afd18a63c107","timestamp":"2026-04-04T20:57:50.010Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":true}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01SqsACCM3tGPN9fSPzUND3X","type":"message","role":"assistant","content":[{"type":"text","text":"Now let me create all the files:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":122,"cache_read_input_tokens":5452,"cache_creation":{"ephemeral_5m_input_tokens":122,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"848adf18-eaac-4f04-83e0-3fc016bc8a7d"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01SqsACCM3tGPN9fSPzUND3X","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01NMRCWzUSADd59ps5zYzPkP","name":"Write","input":{"file_path":"/tmp/loop-bench-gt1ikbl4/tsconfig.json","content":"{\n \"compilerOptions\": {\n \"target\": \"ES2020\",\n \"module\": \"ESNext\",\n \"moduleResolution\": \"bundler\",\n \"strict\": true,\n \"outDir\": \"./dist\",\n \"rootDir\": \"./src\",\n \"sourceMap\": true,\n \"esModuleInterop\": true,\n \"skipLibCheck\": true,\n \"forceConsistentCasingInFileNames\": true,\n \"lib\": [\"ES2020\", \"DOM\", \"DOM.Iterable\"]\n },\n \"include\": [\"src/**/*\"]\n}\n"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":122,"cache_read_input_tokens":5452,"cache_creation":{"ephemeral_5m_input_tokens":122,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"df7ac83e-e898-409f-92c2-44d96ae4e4f7"} +{"type":"user","message":{"role":"user","content":[{"type":"tool_result","content":"<tool_use_error>Error: No such tool available: Write</tool_use_error>","is_error":true,"tool_use_id":"toolu_01NMRCWzUSADd59ps5zYzPkP"}]},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"a6636a9c-1b06-4114-8fe7-64e2a80cb558","timestamp":"2026-04-04T20:57:54.128Z","tool_use_result":"Error: No such tool available: Write"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01Gh9CNcsRaCDitgKRBwV45Z","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01PPTZ1Npo2zArE35KxHYy6c","name":"Bash","input":{"command":"cat > /tmp/loop-bench-gt1ikbl4/tsconfig.json << 'TSEOF'\n{\n \"compilerOptions\": {\n \"target\": \"ES2020\",\n \"module\": \"ESNext\",\n \"moduleResolution\": \"bundler\",\n \"strict\": true,\n \"outDir\": \"./dist\",\n \"rootDir\": \"./src\",\n \"sourceMap\": true,\n \"esModuleInterop\": true,\n \"skipLibCheck\": true,\n \"forceConsistentCasingInFileNames\": true,\n \"lib\": [\"ES2020\", \"DOM\", \"DOM.Iterable\"]\n },\n \"include\": [\"src/**/*\"]\n}\nTSEOF","description":"Create tsconfig.json"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":279,"cache_read_input_tokens":5574,"cache_creation":{"ephemeral_5m_input_tokens":279,"ephemeral_1h_input_tokens":0},"output_tokens":54,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"01a628bd-a2b2-4760-b594-870ad2d99a0c"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01PPTZ1Npo2zArE35KxHYy6c","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"1899aa33-4a8d-4fb2-a49d-68970e3d0a8b","timestamp":"2026-04-04T20:57:57.765Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01Qk1viPVT7mXnWnArrDuLvp","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_015wAQAFi6w3zPQrwvRuek1H","name":"Bash","input":{"command":"cat > /tmp/loop-bench-gt1ikbl4/index.html << 'HTMLEOF'\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Tetris</title>\n <style>\n * {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n }\n\n body {\n background: #0a0a1a;\n color: #e0e0e0;\n font-family: 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 100vh;\n overflow: hidden;\n }\n\n #game-container {\n display: flex;\n gap: 24px;\n align-items: flex-start;\n }\n\n .panel {\n background: #12122a;\n border: 2px solid #2a2a4a;\n border-radius: 8px;\n padding: 16px;\n }\n\n #board-wrapper {\n position: relative;\n }\n\n #board-canvas {\n display: block;\n border: 2px solid #3a3a6a;\n border-radius: 4px;\n background: #0d0d20;\n }\n\n .side-panel {\n width: 160px;\n display: flex;\n flex-direction: column;\n gap: 16px;\n }\n\n .panel-title {\n font-size: 12px;\n text-transform: uppercase;\n letter-spacing: 2px;\n color: #8888aa;\n margin-bottom: 8px;\n text-align: center;\n }\n\n #next-canvas {\n display: block;\n margin: 0 auto;\n background: #0d0d20;\n border: 1px solid #2a2a4a;\n border-radius: 4px;\n }\n\n .stat-row {\n display: flex;\n justify-content: space-between;\n padding: 4px 0;\n border-bottom: 1px solid #1a1a3a;\n }\n\n .stat-label {\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 1px;\n color: #6666aa;\n }\n\n .stat-value {\n font-size: 16px;\n font-weight: bold;\n color: #ffffff;\n font-variant-numeric: tabular-nums;\n }\n\n #overlay {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n background: rgba(0, 0, 0, 0.75);\n border-radius: 4px;\n z-index: 10;\n }\n\n #overlay.hidden {\n display: none;\n }\n\n #overlay-title {\n font-size: 28px;\n font-weight: bold;\n margin-bottom: 8px;\n color: #fff;\n }\n\n #overlay-sub {\n font-size: 14px;\n color: #aaaacc;\n }\n\n .controls-help {\n font-size: 11px;\n color: #555577;\n line-height: 1.8;\n text-align: center;\n }\n\n .controls-help kbd {\n background: #1a1a3a;\n border: 1px solid #3a3a5a;\n border-radius: 3px;\n padding: 1px 6px;\n font-family: inherit;\n font-size: 11px;\n color: #aaaacc;\n }\n </style>\n</head>\n<body>\n <div id=\"game-container\">\n <div class=\"side-panel\">\n <div class=\"panel\" id=\"hold-panel\">\n <div class=\"panel-title\">Hold</div>\n <canvas id=\"hold-canvas\" width=\"100\" height=\"60\"></canvas>\n </div>\n\n <div class=\"panel\">\n <div class=\"panel-title\">Controls</div>\n <div class=\"controls-help\">\n <kbd>←</kbd> <kbd>→</kbd> Move<br/>\n <kbd>↓</kbd> Soft Drop<br/>\n <kbd>Space</kbd> Hard Drop<br/>\n <kbd>↑</kbd> / <kbd>X</kbd> Rotate CW<br/>\n <kbd>Z</kbd> Rotate CCW<br/>\n <kbd>C</kbd> Hold<br/>\n <kbd>P</kbd> Pause<br/>\n </div>\n </div>\n </div>\n\n <div class=\"panel\" id=\"board-wrapper\">\n <canvas id=\"board-canvas\" width=\"300\" height=\"600\"></canvas>\n <div id=\"overlay\">\n <div id=\"overlay-title\">TETRIS</div>\n <div id=\"overlay-sub\">Press any key to start</div>\n </div>\n </div>\n\n <div class=\"side-panel\">\n <div class=\"panel\">\n <div class=\"panel-title\">Next</div>\n <canvas id=\"next-canvas\" width=\"100\" height=\"240\"></canvas>\n </div>\n\n <div class=\"panel\">\n <div class=\"panel-title\">Stats</div>\n <div class=\"stat-row\">\n <span class=\"stat-label\">Score</span>\n <span class=\"stat-value\" id=\"score-value\">0</span>\n </div>\n <div class=\"stat-row\">\n <span class=\"stat-label\">Level</span>\n <span class=\"stat-value\" id=\"level-value\">1</span>\n </div>\n <div class=\"stat-row\">\n <span class=\"stat-label\">Lines</span>\n <span class=\"stat-value\" id=\"lines-value\">0</span>\n </div>\n </div>\n </div>\n </div>\n\n <script type=\"module\" src=\"./dist/game.js\"></script>\n</body>\n</html>\nHTMLEOF","description":"Create index.html"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":265,"cache_read_input_tokens":5853,"cache_creation":{"ephemeral_5m_input_tokens":265,"ephemeral_1h_input_tokens":0},"output_tokens":69,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"2b525f6e-e2d5-4b57-9206-419df3872806"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_015wAQAFi6w3zPQrwvRuek1H","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"4c4fd9ad-8951-446e-b62f-80d899222084","timestamp":"2026-04-04T20:58:17.355Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01Jn7D4vChwmZpPMj3ATosZY","type":"message","role":"assistant","content":[{"type":"text","text":"Now the main game TypeScript file:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":1722,"cache_read_input_tokens":6118,"cache_creation":{"ephemeral_5m_input_tokens":1722,"ephemeral_1h_input_tokens":0},"output_tokens":2,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"2fc8dcf1-5e89-46b5-8fb5-ca984b9b26da"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_011BJJH9S9332Kf3tVzqY95a","type":"message","role":"assistant","content":[{"type":"text","text":"Now let me create the main game TypeScript file:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":0,"cache_read_input_tokens":7840,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"376834e1-68ee-4be6-89b5-ed74b71db46b"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_011BJJH9S9332Kf3tVzqY95a","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01TuU1ZLmUyoiMkw5kKMFE7P","name":"Bash","input":{"command":"cat > /tmp/loop-bench-gt1ikbl4/src/game.ts << 'TSEOF'\n// ──────────────────────────────────────────────\n// Tetris – full browser implementation\n// ──────────────────────────────────────────────\n\n// ── Constants ────────────────────────────────\n\nconst COLS = 10;\nconst ROWS = 20;\nconst HIDDEN_ROWS = 2; // rows above visible area for spawning\nconst TOTAL_ROWS = ROWS + HIDDEN_ROWS;\nconst CELL = 30; // pixel size of each cell\nconst NEXT_CELL = 18;\nconst HOLD_CELL = 18;\nconst LOCK_DELAY = 500; // ms before piece locks after landing\nconst MAX_LOCK_RESETS = 15;\n\n// NES-inspired speed curve: frames per gravity tick at 60fps → converted to ms\nconst LEVEL_SPEEDS: number[] = [\n 800, 717, 633, 550, 467, 383, 300, 217, 133, 100,\n 83, 83, 83, 67, 67, 67, 50, 50, 50, 33,\n];\n\nconst SOFT_DROP_SPEED = 50; // ms\n\n// Scoring (guideline)\nconst LINE_SCORES = [0, 100, 300, 500, 800]; // 0,1,2,3,4 lines\nconst SOFT_DROP_SCORE = 1; // per cell\nconst HARD_DROP_SCORE = 2; // per cell\n\n// ── Piece Definitions (SRS) ─────────────────\n\ntype PieceType = 'I' | 'O' | 'T' | 'S' | 'Z' | 'J' | 'L';\n\n// Each rotation state is a list of [row, col] offsets from piece origin\nconst PIECE_SHAPES: Record<PieceType, number[][][]> = {\n I: [\n [[0,-1],[0,0],[0,1],[0,2]],\n [[-1,1],[0,1],[1,1],[2,1]],\n [[1,-1],[1,0],[1,1],[1,2]],\n [[-1,0],[0,0],[1,0],[2,0]],\n ],\n O: [\n [[0,0],[0,1],[1,0],[1,1]],\n [[0,0],[0,1],[1,0],[1,1]],\n [[0,0],[0,1],[1,0],[1,1]],\n [[0,0],[0,1],[1,0],[1,1]],\n ],\n T: [\n [[0,-1],[0,0],[0,1],[-1,0]],\n [[-1,0],[0,0],[1,0],[0,1]],\n [[0,-1],[0,0],[0,1],[1,0]],\n [[-1,0],[0,0],[1,0],[0,-1]],\n ],\n S: [\n [[0,-1],[0,0],[-1,0],[-1,1]],\n [[-1,0],[0,0],[0,1],[1,1]],\n [[1,-1],[1,0],[0,0],[0,1]],\n [[-1,-1],[0,-1],[0,0],[1,0]],\n ],\n Z: [\n [[-1,-1],[-1,0],[0,0],[0,1]],\n [[0,0],[1,0],[-1,1],[0,1]],\n [[0,-1],[0,0],[1,0],[1,1]],\n [[0,0],[0,-1],[1,-1],[-1,0]],\n ],\n J: [\n [[-1,-1],[0,-1],[0,0],[0,1]],\n [[-1,0],[0,0],[1,0],[-1,1]],\n [[0,-1],[0,0],[0,1],[1,1]],\n [[1,0],[0,0],[-1,0],[1,-1]],\n ],\n L: [\n [[-1,1],[0,-1],[0,0],[0,1]],\n [[-1,0],[0,0],[1,0],[1,1]],\n [[0,-1],[0,0],[0,1],[1,-1]],\n [[-1,-1],[-1,0],[0,0],[1,0]],\n ],\n};\n\n// SRS wall kick data (non-I pieces)\nconst WALL_KICKS: Record<string, number[][]> = {\n '0>1': [[0,0],[-1,0],[-1,-1],[0,2],[-1,2]],\n '1>0': [[0,0],[1,0],[1,1],[0,-2],[1,-2]],\n '1>2': [[0,0],[1,0],[1,1],[0,-2],[1,-2]],\n '2>1': [[0,0],[-1,0],[-1,-1],[0,2],[-1,2]],\n '2>3': [[0,0],[1,0],[1,-1],[0,2],[1,2]],\n '3>2': [[0,0],[-1,0],[-1,1],[0,-2],[-1,-2]],\n '3>0': [[0,0],[-1,0],[-1,1],[0,-2],[-1,-2]],\n '0>3': [[0,0],[1,0],[1,-1],[0,2],[1,2]],\n};\n\nconst I_WALL_KICKS: Record<string, number[][]> = {\n '0>1': [[0,0],[-2,0],[1,0],[-2,1],[1,-2]],\n '1>0': [[0,0],[2,0],[-1,0],[2,-1],[-1,2]],\n '1>2': [[0,0],[-1,0],[2,0],[-1,-2],[2,1]],\n '2>1': [[0,0],[1,0],[-2,0],[1,2],[-2,-1]],\n '2>3': [[0,0],[2,0],[-1,0],[2,-1],[-1,2]],\n '3>2': [[0,0],[-2,0],[1,0],[-2,1],[1,-2]],\n '3>0': [[0,0],[1,0],[-2,0],[1,2],[-2,-1]],\n '0>3': [[0,0],[-1,0],[2,0],[-1,-2],[2,1]],\n};\n\nconst PIECE_COLORS: Record<PieceType, string> = {\n I: '#00e5ff',\n O: '#ffea00',\n T: '#d500f9',\n S: '#00e676',\n Z: '#ff1744',\n J: '#2979ff',\n L: '#ff9100',\n};\n\nconst GHOST_ALPHA = 0.25;\n\nconst ALL_PIECES: PieceType[] = ['I', 'O', 'T', 'S', 'Z', 'J', 'L'];\n\n// ── Types ────────────────────────────────────\n\ntype Cell = PieceType | null;\n\ninterface Piece {\n type: PieceType;\n row: number; // position on the board (top-left of bounding box concept → actually the origin)\n col: number;\n rotation: number; // 0-3\n}\n\ntype GameState = 'idle' | 'playing' | 'paused' | 'gameover';\n\n// ── Bag Randomizer (7-bag) ───────────────────\n\nfunction shuffle<T>(arr: T[]): T[] {\n const a = [...arr];\n for (let i = a.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [a[i], a[j]] = [a[j], a[i]];\n }\n return a;\n}\n\nclass BagRandomizer {\n private queue: PieceType[] = [];\n\n next(): PieceType {\n if (this.queue.length < 7) {\n this.queue.push(...shuffle(ALL_PIECES));\n }\n return this.queue.shift()!;\n }\n\n peek(count: number): PieceType[] {\n while (this.queue.length < count + 7) {\n this.queue.push(...shuffle(ALL_PIECES));\n }\n return this.queue.slice(0, count);\n }\n\n reset(): void {\n this.queue = [];\n }\n}\n\n// ── Board helpers ────────────────────────────\n\nfunction createBoard(): Cell[][] {\n return Array.from({ length: TOTAL_ROWS }, () => Array<Cell>(COLS).fill(null));\n}\n\nfunction getBlocks(piece: Piece): [number, number][] {\n return PIECE_SHAPES[piece.type][piece.rotation].map(\n ([dr, dc]) => [piece.row + dr, piece.col + dc] as [number, number]\n );\n}\n\nfunction isValid(board: Cell[][], piece: Piece): boolean {\n for (const [r, c] of getBlocks(piece)) {\n if (r < 0 || r >= TOTAL_ROWS || c < 0 || c >= COLS) return false;\n if (board[r][c] !== null) return false;\n }\n return true;\n}\n\n// ── Main Game Class ──────────────────────────\n\nclass TetrisGame {\n private board: Cell[][] = createBoard();\n private bag = new BagRandomizer();\n private current: Piece | null = null;\n private holdPiece: PieceType | null = null;\n private holdUsed = false;\n private state: GameState = 'idle';\n\n // Stats\n private score = 0;\n private level = 1;\n private lines = 0;\n\n // Timing\n private lastDrop = 0;\n private lockTimer = 0;\n private lockResets = 0;\n private softDropping = false;\n\n // Line clear animation\n private clearingRows: number[] = [];\n private clearAnimStart = 0;\n private readonly CLEAR_ANIM_MS = 300;\n\n // Canvas refs\n private boardCtx: CanvasRenderingContext2D;\n private nextCtx: CanvasRenderingContext2D;\n private holdCtx: CanvasRenderingContext2D;\n\n // DOM refs\n private scoreEl: HTMLElement;\n private levelEl: HTMLElement;\n private linesEl: HTMLElement;\n private overlay: HTMLElement;\n private overlayTitle: HTMLElement;\n private overlaySub: HTMLElement;\n\n constructor() {\n const boardCanvas = document.getElementById('board-canvas') as HTMLCanvasElement;\n const nextCanvas = document.getElementById('next-canvas') as HTMLCanvasElement;\n const holdCanvas = document.getElementById('hold-canvas') as HTMLCanvasElement;\n\n this.boardCtx = boardCanvas.getContext('2d')!;\n this.nextCtx = nextCanvas.getContext('2d')!;\n this.holdCtx = holdCanvas.getContext('2d')!;\n\n this.scoreEl = document.getElementById('score-value')!;\n this.levelEl = document.getElementById('level-value')!;\n this.linesEl = document.getElementById('lines-value')!;\n this.overlay = document.getElementById('overlay')!;\n this.overlayTitle = document.getElementById('overlay-title')!;\n this.overlaySub = document.getElementById('overlay-sub')!;\n\n this.bindInput();\n this.showOverlay('TETRIS', 'Press any key to start');\n requestAnimationFrame(this.loop);\n }\n\n // ── Input ────────────────────────────────\n\n private bindInput(): void {\n document.addEventListener('keydown', (e) => {\n // Start game on any key from idle\n if (this.state === 'idle') {\n e.preventDefault();\n this.startGame();\n return;\n }\n\n // Restart on Enter from gameover\n if (this.state === 'gameover') {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n this.startGame();\n }\n return;\n }\n\n // Toggle pause\n if (e.key === 'p' || e.key === 'P') {\n e.preventDefault();\n this.togglePause();\n return;\n }\n\n if (this.state !== 'playing' || !this.current) return;\n if (this.clearingRows.length > 0) return; // animating\n\n switch (e.key) {\n case 'ArrowLeft':\n e.preventDefault();\n this.move(0, -1);\n break;\n case 'ArrowRight':\n e.preventDefault();\n this.move(0, 1);\n break;\n case 'ArrowDown':\n e.preventDefault();\n this.softDropping = true;\n this.softDrop();\n break;\n case 'ArrowUp':\n case 'x':\n case 'X':\n e.preventDefault();\n this.rotate(1);\n break;\n case 'z':\n case 'Z':\n e.preventDefault();\n this.rotate(-1);\n break;\n case ' ':\n e.preventDefault();\n this.hardDrop();\n break;\n case 'c':\n case 'C':\n e.preventDefault();\n this.hold();\n break;\n }\n });\n\n document.addEventListener('keyup', (e) => {\n if (e.key === 'ArrowDown') {\n this.softDropping = false;\n }\n });\n }\n\n // ── Game lifecycle ───────────────────────\n\n private startGame(): void {\n this.board = createBoard();\n this.bag.reset();\n this.current = null;\n this.holdPiece = null;\n this.holdUsed = false;\n this.score = 0;\n this.level = 1;\n this.lines = 0;\n this.clearingRows = [];\n this.softDropping = false;\n this.state = 'playing';\n this.hideOverlay();\n this.spawnPiece();\n this.lastDrop = performance.now();\n this.updateStats();\n }\n\n private gameOver(): void {\n this.state = 'gameover';\n this.showOverlay('GAME OVER', `Score: ${this.score} — Press Enter to restart`);\n }\n\n private togglePause(): void {\n if (this.state === 'playing') {\n this.state = 'paused';\n this.showOverlay('PAUSED', 'Press P to resume');\n } else if (this.state === 'paused') {\n this.state = 'playing';\n this.lastDrop = performance.now();\n this.hideOverlay();\n }\n }\n\n // ── Overlay helpers ──────────────────────\n\n private showOverlay(title: string, sub: string): void {\n this.overlayTitle.textContent = title;\n this.overlaySub.textContent = sub;\n this.overlay.classList.remove('hidden');\n }\n\n private hideOverlay(): void {\n this.overlay.classList.add('hidden');\n }\n\n // ── Piece spawning ──────────────────────\n\n private spawnPiece(): void {\n const type = this.bag.next();\n this.current = {\n type,\n row: HIDDEN_ROWS - 1, // spawn near top of hidden area\n col: Math.floor(COLS / 2) - 1,\n rotation: 0,\n };\n this.lockTimer = 0;\n this.lockResets = 0;\n this.holdUsed = false;\n\n // Try to push down into the visible area\n if (isValid(this.board, { ...this.current, row: this.current.row + 1 })) {\n this.current.row++;\n }\n\n if (!isValid(this.board, this.current)) {\n // Can't spawn → game over\n this.placePiece();\n this.gameOver();\n }\n }\n\n // ── Movement ─────────────────────────────\n\n private move(dr: number, dc: number): boolean {\n if (!this.current) return false;\n const test = { ...this.current, row: this.current.row + dr, col: this.current.col + dc };\n if (isValid(this.board, test)) {\n this.current = test;\n this.resetLockIfOnGround();\n return true;\n }\n return false;\n }\n\n private rotate(dir: 1 | -1): void {\n if (!this.current) return;\n const from = this.current.rotation;\n const to = (from + dir + 4) % 4;\n const kicks = this.current.type === 'I'\n ? I_WALL_KICKS[`${from}>${to}`]\n : WALL_KICKS[`${from}>${to}`];\n\n if (!kicks) return;\n\n for (const [dc, dr] of kicks) {\n const test: Piece = {\n ...this.current,\n rotation: to,\n row: this.current.row - dr, // SRS kick data: positive y = up, our row increases downward\n col: this.current.col + dc,\n };\n if (isValid(this.board, test)) {\n this.current = test;\n this.resetLockIfOnGround();\n return;\n }\n }\n }\n\n private softDrop(): void {\n if (this.move(1, 0)) {\n this.score += SOFT_DROP_SCORE;\n this.lastDrop = performance.now();\n this.updateStats();\n }\n }\n\n private hardDrop(): void {\n if (!this.current) return;\n let dist = 0;\n while (this.move(1, 0)) {\n dist++;\n }\n this.score += dist * HARD_DROP_SCORE;\n this.updateStats();\n this.placePiece();\n this.afterPlace();\n }\n\n private hold(): void {\n if (!this.current || this.holdUsed) return;\n const type = this.current.type;\n if (this.holdPiece) {\n // Spawn held piece\n this.current = {\n type: this.holdPiece,\n row: HIDDEN_ROWS,\n col: Math.floor(COLS / 2) - 1,\n rotation: 0,\n };\n this.holdPiece = type;\n this.lockTimer = 0;\n this.lockResets = 0;\n } else {\n this.holdPiece = type;\n this.spawnPiece();\n }\n this.holdUsed = true;\n }\n\n // ── Lock delay ───────────────────────────\n\n private isOnGround(): boolean {\n if (!this.current) return false;\n return !isValid(this.board, { ...this.current, row: this.current.row + 1 });\n }\n\n private resetLockIfOnGround(): void {\n if (this.isOnGround() && this.lockResets < MAX_LOCK_RESETS) {\n this.lockTimer = 0;\n this.lockResets++;\n }\n }\n\n // ── Place piece & clear lines ────────────\n\n private placePiece(): void {\n if (!this.current) return;\n for (const [r, c] of getBlocks(this.current)) {\n if (r >= 0 && r < TOTAL_ROWS && c >= 0 && c < COLS) {\n this.board[r][c] = this.current.type;\n }\n }\n this.current = null;\n }\n\n private afterPlace(): void {\n // Find full rows\n const fullRows: number[] = [];\n for (let r = 0; r < TOTAL_ROWS; r++) {\n if (this.board[r].every(cell => cell !== null)) {\n fullRows.push(r);\n }\n }\n\n if (fullRows.length > 0) {\n this.clearingRows = fullRows;\n this.clearAnimStart = performance.now();\n } else {\n this.spawnPiece();\n }\n }\n\n private finishClear(): void {\n const count = this.clearingRows.length;\n\n // Remove rows top-to-bottom (indices are sorted ascending)\n for (const r of this.clearingRows.sort((a, b) => a - b)) {\n this.board.splice(r, 1);\n this.board.unshift(Array<Cell>(COLS).fill(null));\n }\n\n this.lines += count;\n this.score += LINE_SCORES[count] * this.level;\n this.level = Math.floor(this.lines / 10) + 1;\n this.clearingRows = [];\n this.updateStats();\n this.spawnPiece();\n }\n\n // ── Ghost piece ──────────────────────────\n\n private ghostRow(): number {\n if (!this.current) return 0;\n let test = { ...this.current };\n while (isValid(this.board, { ...test, row: test.row + 1 })) {\n test.row++;\n }\n return test.row;\n }\n\n // ── Stats DOM ────────────────────────────\n\n private updateStats(): void {\n this.scoreEl.textContent = this.score.toLocaleString();\n this.levelEl.textContent = String(this.level);\n this.linesEl.textContent = String(this.lines);\n }\n\n // ── Gravity speed ────────────────────────\n\n private getSpeed(): number {\n if (this.softDropping) return SOFT_DROP_SPEED;\n const idx = Math.min(this.level - 1, LEVEL_SPEEDS.length - 1);\n return LEVEL_SPEEDS[idx];\n }\n\n // ── Main loop ────────────────────────────\n\n private loop = (now: number): void => {\n requestAnimationFrame(this.loop);\n\n if (this.state !== 'playing') {\n this.render(now);\n return;\n }\n\n // Line clear animation\n if (this.clearingRows.length > 0) {\n if (now - this.clearAnimStart >= this.CLEAR_ANIM_MS) {\n this.finishClear();\n }\n this.render(now);\n return;\n }\n\n if (!this.current) return;\n\n // Gravity\n const speed = this.getSpeed();\n if (now - this.lastDrop >= speed) {\n if (this.move(1, 0)) {\n this.lastDrop = now;\n } else {\n // On ground\n this.lastDrop = now; // prevent repeated triggers\n }\n }\n\n // Lock delay\n if (this.isOnGround()) {\n if (this.lockTimer === 0) {\n this.lockTimer = now;\n } else if (now - this.lockTimer >= LOCK_DELAY) {\n this.placePiece();\n this.afterPlace();\n }\n } else {\n this.lockTimer = 0;\n }\n\n this.render(now);\n };\n\n // ── Rendering ────────────────────────────\n\n private render(now: number): void {\n this.drawBoard(now);\n this.drawNext();\n this.drawHold();\n }\n\n private drawBoard(now: number): void {\n const ctx = this.boardCtx;\n ctx.clearRect(0, 0, COLS * CELL, ROWS * CELL);\n\n // Grid lines\n ctx.strokeStyle = '#1a1a30';\n ctx.lineWidth = 0.5;\n for (let r = 0; r <= ROWS; r++) {\n ctx.beginPath();\n ctx.moveTo(0, r * CELL);\n ctx.lineTo(COLS * CELL, r * CELL);\n ctx.stroke();\n }\n for (let c = 0; c <= COLS; c++) {\n ctx.beginPath();\n ctx.moveTo(c * CELL, 0);\n ctx.lineTo(c * CELL, ROWS * CELL);\n ctx.stroke();\n }\n\n // Board cells (skip hidden rows for drawing)\n for (let r = HIDDEN_ROWS; r < TOTAL_ROWS; r++) {\n for (let c = 0; c < COLS; c++) {\n const cell = this.board[r][c];\n if (cell) {\n const isClearing = this.clearingRows.includes(r);\n if (isClearing) {\n const progress = Math.min(1, (now - this.clearAnimStart) / this.CLEAR_ANIM_MS);\n const flash = Math.sin(progress * Math.PI * 3) * 0.5 + 0.5;\n ctx.globalAlpha = 1 - progress * 0.5;\n this.drawCell(ctx, c, r - HIDDEN_ROWS, cell, flash);\n ctx.globalAlpha = 1;\n } else {\n this.drawCell(ctx, c, r - HIDDEN_ROWS, cell);\n }\n }\n }\n }\n\n if (!this.current) return;\n\n // Ghost piece\n const ghostR = this.ghostRow();\n if (ghostR !== this.current.row) {\n const ghostPiece: Piece = { ...this.current, row: ghostR };\n for (const [r, c] of getBlocks(ghostPiece)) {\n if (r >= HIDDEN_ROWS) {\n ctx.globalAlpha = GHOST_ALPHA;\n this.drawCell(ctx, c, r - HIDDEN_ROWS, this.current.type);\n ctx.globalAlpha = 1;\n }\n }\n }\n\n // Current piece\n for (const [r, c] of getBlocks(this.current)) {\n if (r >= HIDDEN_ROWS) {\n this.drawCell(ctx, c, r - HIDDEN_ROWS, this.current.type);\n }\n }\n }\n\n private drawCell(\n ctx: CanvasRenderingContext2D,\n col: number,\n row: number,\n type: PieceType,\n flash = 0\n ): void {\n const x = col * CELL;\n const y = row * CELL;\n const color = PIECE_COLORS[type];\n const inset = 1;\n\n // Main fill\n ctx.fillStyle = color;\n ctx.fillRect(x + inset, y + inset, CELL - inset * 2, CELL - inset * 2);\n\n // Highlight (top-left bevel)\n ctx.fillStyle = `rgba(255,255,255,${0.25 + flash * 0.5})`;\n ctx.fillRect(x + inset, y + inset, CELL - inset * 2, 2);\n ctx.fillRect(x + inset, y + inset, 2, CELL - inset * 2);\n\n // Shadow (bottom-right bevel)\n ctx.fillStyle = 'rgba(0,0,0,0.3)';\n ctx.fillRect(x + inset, y + CELL - inset - 2, CELL - inset * 2, 2);\n ctx.fillRect(x + CELL - inset - 2, y + inset, 2, CELL - inset * 2);\n }\n\n private drawPiecePreview(\n ctx: CanvasRenderingContext2D,\n type: PieceType,\n offsetY: number,\n cellSize: number,\n canvasWidth: number\n ): void {\n const blocks = PIECE_SHAPES[type][0]; // rotation 0\n // Find bounding box\n let minC = Infinity, maxC = -Infinity, minR = Infinity, maxR = -Infinity;\n for (const [dr, dc] of blocks) {\n minR = Math.min(minR, dr);\n maxR = Math.max(maxR, dr);\n minC = Math.min(minC, dc);\n maxC = Math.max(maxC, dc);\n }\n const w = (maxC - minC + 1) * cellSize;\n const h = (maxR - minR + 1) * cellSize;\n const ox = (canvasWidth - w) / 2;\n const oy = offsetY + (3 * cellSize - h) / 2;\n\n for (const [dr, dc] of blocks) {\n const x = ox + (dc - minC) * cellSize;\n const y = oy + (dr - minR) * cellSize;\n const color = PIECE_COLORS[type];\n const inset = 1;\n ctx.fillStyle = color;\n ctx.fillRect(x + inset, y + inset, cellSize - inset * 2, cellSize - inset * 2);\n ctx.fillStyle = 'rgba(255,255,255,0.2)';\n ctx.fillRect(x + inset, y + inset, cellSize - inset * 2, 1);\n ctx.fillRect(x + inset, y + inset, 1, cellSize - inset * 2);\n }\n }\n\n private drawNext(): void {\n const ctx = this.nextCtx;\n const canvas = ctx.canvas;\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n const previews = this.bag.peek(4);\n for (let i = 0; i < previews.length; i++) {\n this.drawPiecePreview(ctx, previews[i], i * 60, NEXT_CELL, canvas.width);\n }\n }\n\n private drawHold(): void {\n const ctx = this.holdCtx;\n const canvas = ctx.canvas;\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n if (this.holdPiece) {\n ctx.globalAlpha = this.holdUsed ? 0.4 : 1;\n this.drawPiecePreview(ctx, this.holdPiece, 0, HOLD_CELL, canvas.width);\n ctx.globalAlpha = 1;\n }\n }\n}\n\n// ── Boot ─────────────────────────────────────\n\ndocument.addEventListener('DOMContentLoaded', () => {\n new TetrisGame();\n});\nTSEOF","description":"Create main game TypeScript source file"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":0,"cache_read_input_tokens":7840,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"5f82f7ab-55b8-4894-92fa-c7153221d1a9"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01TuU1ZLmUyoiMkw5kKMFE7P","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"6053f514-a2f4-4327-a64f-a6a21892bf13","timestamp":"2026-04-04T21:01:27.501Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01XcdmdHsLB6f1UgbkSeTvJF","type":"message","role":"assistant","content":[{"type":"text","text":"Now let's compile the TypeScript and verify it works:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":7989,"cache_read_input_tokens":7840,"cache_creation":{"ephemeral_5m_input_tokens":7989,"ephemeral_1h_input_tokens":0},"output_tokens":3,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"ddf538de-bff9-4fc4-97a9-e187cb2a8137"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01XcdmdHsLB6f1UgbkSeTvJF","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01PKDU9voxMEa6z52pTbRrnF","name":"Bash","input":{"command":"npx tsc 2>&1","description":"Compile TypeScript"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":7989,"cache_read_input_tokens":7840,"cache_creation":{"ephemeral_5m_input_tokens":7989,"ephemeral_1h_input_tokens":0},"output_tokens":3,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"fd8dcabd-9105-4ed7-a9fe-91638c7c7350"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01PKDU9voxMEa6z52pTbRrnF","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"1afaf2a5-bcda-43a9-9be8-1f5a7e6205b0","timestamp":"2026-04-04T21:01:31.404Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01Rh1GSdSEAFRpnkENuyE8qb","type":"message","role":"assistant","content":[{"type":"text","text":"Clean compilation. Let me verify the output:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":110,"cache_read_input_tokens":15829,"cache_creation":{"ephemeral_5m_input_tokens":110,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"9e95306b-b16c-452b-ac7a-0db9c9b67828"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01Rh1GSdSEAFRpnkENuyE8qb","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01Eoey3HJkcKWY1e2Zp9LnEu","name":"Bash","input":{"command":"ls -la /tmp/loop-bench-gt1ikbl4/dist/","description":"List compiled output files"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":110,"cache_read_input_tokens":15829,"cache_creation":{"ephemeral_5m_input_tokens":110,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"6e21e671-9bf4-4f3d-a01f-e7455e8005aa"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01Eoey3HJkcKWY1e2Zp9LnEu","type":"tool_result","content":"total 60\ndrwxr-xr-x 2 root root 4096 Apr 4 23:01 .\ndrwx------ 5 root root 4096 Apr 4 23:01 ..\n-rw-r--r-- 1 root root 23175 Apr 4 23:01 game.js\n-rw-r--r-- 1 root root 28074 Apr 4 23:01 game.js.map","is_error":false}]},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"8d600e2c-5324-4b5c-bd18-1b7356eef97d","timestamp":"2026-04-04T21:01:36.028Z","tool_use_result":{"stdout":"total 60\ndrwxr-xr-x 2 root root 4096 Apr 4 23:01 .\ndrwx------ 5 root root 4096 Apr 4 23:01 ..\n-rw-r--r-- 1 root root 23175 Apr 4 23:01 game.js\n-rw-r--r-- 1 root root 28074 Apr 4 23:01 game.js.map","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_017wzpUBjx7x3uYDnL2uscGV","type":"message","role":"assistant","content":[{"type":"text","text":"Let me also add a build script to package.json and run the linter to make sure there are no issues:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":226,"cache_read_input_tokens":15939,"cache_creation":{"ephemeral_5m_input_tokens":226,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"ad74bbea-c9a2-4d83-860b-63b6e6671695"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_017wzpUBjx7x3uYDnL2uscGV","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01MCwUt9y5bjQJEspRze1QVX","name":"Read","input":{"file_path":"/tmp/loop-bench-gt1ikbl4/package.json"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":226,"cache_read_input_tokens":15939,"cache_creation":{"ephemeral_5m_input_tokens":226,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"95947351-868b-442b-986c-57d3ddc6ac30"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01MCwUt9y5bjQJEspRze1QVX","type":"tool_result","content":"File unchanged since last read. The content from the earlier Read tool_result in this conversation is still current — refer to that instead of re-reading."}]},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"5363c7f4-ec20-4cd0-a6e6-988b524738ee","timestamp":"2026-04-04T21:01:39.365Z","tool_use_result":{"type":"file_unchanged","file":{"filePath":"/tmp/loop-bench-gt1ikbl4/package.json"}}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01WAvuKtmK3TKz6aXgP6KTLV","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01CdSUL1krHLW7QEj7HHHMkF","name":"Edit","input":{"replace_all":false,"file_path":"/tmp/loop-bench-gt1ikbl4/package.json","old_string":" \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"","new_string":" \"build\": \"tsc\",\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\""},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":361,"cache_read_input_tokens":15939,"cache_creation":{"ephemeral_5m_input_tokens":361,"ephemeral_1h_input_tokens":0},"output_tokens":63,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"31d4bb68-2ea2-4eb3-9fe4-7c1759dbc1a8"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01CdSUL1krHLW7QEj7HHHMkF","type":"tool_result","content":"The file /tmp/loop-bench-gt1ikbl4/package.json has been updated successfully."}]},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"b62aa554-335a-4b06-b144-dc93c47184f8","timestamp":"2026-04-04T21:01:45.039Z","tool_use_result":{"filePath":"/tmp/loop-bench-gt1ikbl4/package.json","oldString":" \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"","newString":" \"build\": \"tsc\",\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"","originalFile":"{\n \"name\": \"loop-bench-gt1ikbl4\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },\n \"keywords\": [],\n \"author\": \"\",\n \"license\": \"ISC\",\n \"type\": \"commonjs\",\n \"devDependencies\": {\n \"@eslint/js\": \"^10.0.1\",\n \"@playwright/test\": \"^1.59.1\",\n \"@types/node\": \"^25.5.2\",\n \"eslint\": \"^10.2.0\",\n \"typescript\": \"^6.0.2\"\n }\n}\n","structuredPatch":[{"oldStart":4,"oldLines":6,"newStart":4,"newLines":7,"lines":[" \"description\": \"\","," \"main\": \"index.js\","," \"scripts\": {","+ \"build\": \"tsc\","," \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\""," },"," \"keywords\": [],"]}],"userModified":false,"replaceAll":false}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01VukGUiAbx759bH56uWBLbQ","type":"message","role":"assistant","content":[{"type":"text","text":"Now let me run the Playwright tests to verify the game is functional. First, let me check if there are any existing test files:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":202,"cache_read_input_tokens":16300,"cache_creation":{"ephemeral_5m_input_tokens":202,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"bd1c4d52-ca11-4c0c-aae5-cd709f257fab"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01VukGUiAbx759bH56uWBLbQ","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01B6W6sfWdtXbs5SEECrDpg3","name":"Bash","input":{"command":"find /tmp/loop-bench-gt1ikbl4 -name \"*.test.*\" -o -name \"*.spec.*\" 2>/dev/null | head -20","description":"Find any existing test files"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":202,"cache_read_input_tokens":16300,"cache_creation":{"ephemeral_5m_input_tokens":202,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"98811b17-5aed-4858-8b2f-6a0a09665cb4"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01B6W6sfWdtXbs5SEECrDpg3","type":"tool_result","content":"/tmp/loop-bench-gt1ikbl4/node_modules/json-schema-traverse/spec/index.spec.js","is_error":false}]},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"8200dd9b-2b30-48d2-ba38-f6bf6e354100","timestamp":"2026-04-04T21:01:49.085Z","tool_use_result":{"stdout":"/tmp/loop-bench-gt1ikbl4/node_modules/json-schema-traverse/spec/index.spec.js","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01SspspuuhHk31fTMvjvfQuh","type":"message","role":"assistant","content":[{"type":"text","text":"No existing test files. Let me now review the game code once more end-to-end to make sure the logic is solid:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":182,"cache_read_input_tokens":16502,"cache_creation":{"ephemeral_5m_input_tokens":182,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"888875ae-8564-471a-954e-1e2e6d3961e3"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01SspspuuhHk31fTMvjvfQuh","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_015rB4zCbkMA9K27n7barEDD","name":"Read","input":{"file_path":"/tmp/loop-bench-gt1ikbl4/src/game.ts"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":182,"cache_read_input_tokens":16502,"cache_creation":{"ephemeral_5m_input_tokens":182,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"0ffb7e5a-c942-45a9-9cc4-9c4117582d5d"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_015rB4zCbkMA9K27n7barEDD","type":"tool_result","content":"1\t// ──────────────────────────────────────────────\n2\t// Tetris – full browser implementation\n3\t// ──────────────────────────────────────────────\n4\t\n5\t// ── Constants ────────────────────────────────\n6\t\n7\tconst COLS = 10;\n8\tconst ROWS = 20;\n9\tconst HIDDEN_ROWS = 2; // rows above visible area for spawning\n10\tconst TOTAL_ROWS = ROWS + HIDDEN_ROWS;\n11\tconst CELL = 30; // pixel size of each cell\n12\tconst NEXT_CELL = 18;\n13\tconst HOLD_CELL = 18;\n14\tconst LOCK_DELAY = 500; // ms before piece locks after landing\n15\tconst MAX_LOCK_RESETS = 15;\n16\t\n17\t// NES-inspired speed curve: frames per gravity tick at 60fps → converted to ms\n18\tconst LEVEL_SPEEDS: number[] = [\n19\t 800, 717, 633, 550, 467, 383, 300, 217, 133, 100,\n20\t 83, 83, 83, 67, 67, 67, 50, 50, 50, 33,\n21\t];\n22\t\n23\tconst SOFT_DROP_SPEED = 50; // ms\n24\t\n25\t// Scoring (guideline)\n26\tconst LINE_SCORES = [0, 100, 300, 500, 800]; // 0,1,2,3,4 lines\n27\tconst SOFT_DROP_SCORE = 1; // per cell\n28\tconst HARD_DROP_SCORE = 2; // per cell\n29\t\n30\t// ── Piece Definitions (SRS) ─────────────────\n31\t\n32\ttype PieceType = 'I' | 'O' | 'T' | 'S' | 'Z' | 'J' | 'L';\n33\t\n34\t// Each rotation state is a list of [row, col] offsets from piece origin\n35\tconst PIECE_SHAPES: Record<PieceType, number[][][]> = {\n36\t I: [\n37\t [[0,-1],[0,0],[0,1],[0,2]],\n38\t [[-1,1],[0,1],[1,1],[2,1]],\n39\t [[1,-1],[1,0],[1,1],[1,2]],\n40\t [[-1,0],[0,0],[1,0],[2,0]],\n41\t ],\n42\t O: [\n43\t [[0,0],[0,1],[1,0],[1,1]],\n44\t [[0,0],[0,1],[1,0],[1,1]],\n45\t [[0,0],[0,1],[1,0],[1,1]],\n46\t [[0,0],[0,1],[1,0],[1,1]],\n47\t ],\n48\t T: [\n49\t [[0,-1],[0,0],[0,1],[-1,0]],\n50\t [[-1,0],[0,0],[1,0],[0,1]],\n51\t [[0,-1],[0,0],[0,1],[1,0]],\n52\t [[-1,0],[0,0],[1,0],[0,-1]],\n53\t ],\n54\t S: [\n55\t [[0,-1],[0,0],[-1,0],[-1,1]],\n56\t [[-1,0],[0,0],[0,1],[1,1]],\n57\t [[1,-1],[1,0],[0,0],[0,1]],\n58\t [[-1,-1],[0,-1],[0,0],[1,0]],\n59\t ],\n60\t Z: [\n61\t [[-1,-1],[-1,0],[0,0],[0,1]],\n62\t [[0,0],[1,0],[-1,1],[0,1]],\n63\t [[0,-1],[0,0],[1,0],[1,1]],\n64\t [[0,0],[0,-1],[1,-1],[-1,0]],\n65\t ],\n66\t J: [\n67\t [[-1,-1],[0,-1],[0,0],[0,1]],\n68\t [[-1,0],[0,0],[1,0],[-1,1]],\n69\t [[0,-1],[0,0],[0,1],[1,1]],\n70\t [[1,0],[0,0],[-1,0],[1,-1]],\n71\t ],\n72\t L: [\n73\t [[-1,1],[0,-1],[0,0],[0,1]],\n74\t [[-1,0],[0,0],[1,0],[1,1]],\n75\t [[0,-1],[0,0],[0,1],[1,-1]],\n76\t [[-1,-1],[-1,0],[0,0],[1,0]],\n77\t ],\n78\t};\n79\t\n80\t// SRS wall kick data (non-I pieces)\n81\tconst WALL_KICKS: Record<string, number[][]> = {\n82\t '0>1': [[0,0],[-1,0],[-1,-1],[0,2],[-1,2]],\n83\t '1>0': [[0,0],[1,0],[1,1],[0,-2],[1,-2]],\n84\t '1>2': [[0,0],[1,0],[1,1],[0,-2],[1,-2]],\n85\t '2>1': [[0,0],[-1,0],[-1,-1],[0,2],[-1,2]],\n86\t '2>3': [[0,0],[1,0],[1,-1],[0,2],[1,2]],\n87\t '3>2': [[0,0],[-1,0],[-1,1],[0,-2],[-1,-2]],\n88\t '3>0': [[0,0],[-1,0],[-1,1],[0,-2],[-1,-2]],\n89\t '0>3': [[0,0],[1,0],[1,-1],[0,2],[1,2]],\n90\t};\n91\t\n92\tconst I_WALL_KICKS: Record<string, number[][]> = {\n93\t '0>1': [[0,0],[-2,0],[1,0],[-2,1],[1,-2]],\n94\t '1>0': [[0,0],[2,0],[-1,0],[2,-1],[-1,2]],\n95\t '1>2': [[0,0],[-1,0],[2,0],[-1,-2],[2,1]],\n96\t '2>1': [[0,0],[1,0],[-2,0],[1,2],[-2,-1]],\n97\t '2>3': [[0,0],[2,0],[-1,0],[2,-1],[-1,2]],\n98\t '3>2': [[0,0],[-2,0],[1,0],[-2,1],[1,-2]],\n99\t '3>0': [[0,0],[1,0],[-2,0],[1,2],[-2,-1]],\n100\t '0>3': [[0,0],[-1,0],[2,0],[-1,-2],[2,1]],\n101\t};\n102\t\n103\tconst PIECE_COLORS: Record<PieceType, string> = {\n104\t I: '#00e5ff',\n105\t O: '#ffea00',\n106\t T: '#d500f9',\n107\t S: '#00e676',\n108\t Z: '#ff1744',\n109\t J: '#2979ff',\n110\t L: '#ff9100',\n111\t};\n112\t\n113\tconst GHOST_ALPHA = 0.25;\n114\t\n115\tconst ALL_PIECES: PieceType[] = ['I', 'O', 'T', 'S', 'Z', 'J', 'L'];\n116\t\n117\t// ── Types ────────────────────────────────────\n118\t\n119\ttype Cell = PieceType | null;\n120\t\n121\tinterface Piece {\n122\t type: PieceType;\n123\t row: number; // position on the board (top-left of bounding box concept → actually the origin)\n124\t col: number;\n125\t rotation: number; // 0-3\n126\t}\n127\t\n128\ttype GameState = 'idle' | 'playing' | 'paused' | 'gameover';\n129\t\n130\t// ── Bag Randomizer (7-bag) ───────────────────\n131\t\n132\tfunction shuffle<T>(arr: T[]): T[] {\n133\t const a = [...arr];\n134\t for (let i = a.length - 1; i > 0; i--) {\n135\t const j = Math.floor(Math.random() * (i + 1));\n136\t [a[i], a[j]] = [a[j], a[i]];\n137\t }\n138\t return a;\n139\t}\n140\t\n141\tclass BagRandomizer {\n142\t private queue: PieceType[] = [];\n143\t\n144\t next(): PieceType {\n145\t if (this.queue.length < 7) {\n146\t this.queue.push(...shuffle(ALL_PIECES));\n147\t }\n148\t return this.queue.shift()!;\n149\t }\n150\t\n151\t peek(count: number): PieceType[] {\n152\t while (this.queue.length < count + 7) {\n153\t this.queue.push(...shuffle(ALL_PIECES));\n154\t }\n155\t return this.queue.slice(0, count);\n156\t }\n157\t\n158\t reset(): void {\n159\t this.queue = [];\n160\t }\n161\t}\n162\t\n163\t// ── Board helpers ────────────────────────────\n164\t\n165\tfunction createBoard(): Cell[][] {\n166\t return Array.from({ length: TOTAL_ROWS }, () => Array<Cell>(COLS).fill(null));\n167\t}\n168\t\n169\tfunction getBlocks(piece: Piece): [number, number][] {\n170\t return PIECE_SHAPES[piece.type][piece.rotation].map(\n171\t ([dr, dc]) => [piece.row + dr, piece.col + dc] as [number, number]\n172\t );\n173\t}\n174\t\n175\tfunction isValid(board: Cell[][], piece: Piece): boolean {\n176\t for (const [r, c] of getBlocks(piece)) {\n177\t if (r < 0 || r >= TOTAL_ROWS || c < 0 || c >= COLS) return false;\n178\t if (board[r][c] !== null) return false;\n179\t }\n180\t return true;\n181\t}\n182\t\n183\t// ── Main Game Class ──────────────────────────\n184\t\n185\tclass TetrisGame {\n186\t private board: Cell[][] = createBoard();\n187\t private bag = new BagRandomizer();\n188\t private current: Piece | null = null;\n189\t private holdPiece: PieceType | null = null;\n190\t private holdUsed = false;\n191\t private state: GameState = 'idle';\n192\t\n193\t // Stats\n194\t private score = 0;\n195\t private level = 1;\n196\t private lines = 0;\n197\t\n198\t // Timing\n199\t private lastDrop = 0;\n200\t private lockTimer = 0;\n201\t private lockResets = 0;\n202\t private softDropping = false;\n203\t\n204\t // Line clear animation\n205\t private clearingRows: number[] = [];\n206\t private clearAnimStart = 0;\n207\t private readonly CLEAR_ANIM_MS = 300;\n208\t\n209\t // Canvas refs\n210\t private boardCtx: CanvasRenderingContext2D;\n211\t private nextCtx: CanvasRenderingContext2D;\n212\t private holdCtx: CanvasRenderingContext2D;\n213\t\n214\t // DOM refs\n215\t private scoreEl: HTMLElement;\n216\t private levelEl: HTMLElement;\n217\t private linesEl: HTMLElement;\n218\t private overlay: HTMLElement;\n219\t private overlayTitle: HTMLElement;\n220\t private overlaySub: HTMLElement;\n221\t\n222\t constructor() {\n223\t const boardCanvas = document.getElementById('board-canvas') as HTMLCanvasElement;\n224\t const nextCanvas = document.getElementById('next-canvas') as HTMLCanvasElement;\n225\t const holdCanvas = document.getElementById('hold-canvas') as HTMLCanvasElement;\n226\t\n227\t this.boardCtx = boardCanvas.getContext('2d')!;\n228\t this.nextCtx = nextCanvas.getContext('2d')!;\n229\t this.holdCtx = holdCanvas.getContext('2d')!;\n230\t\n231\t this.scoreEl = document.getElementById('score-value')!;\n232\t this.levelEl = document.getElementById('level-value')!;\n233\t this.linesEl = document.getElementById('lines-value')!;\n234\t this.overlay = document.getElementById('overlay')!;\n235\t this.overlayTitle = document.getElementById('overlay-title')!;\n236\t this.overlaySub = document.getElementById('overlay-sub')!;\n237\t\n238\t this.bindInput();\n239\t this.showOverlay('TETRIS', 'Press any key to start');\n240\t requestAnimationFrame(this.loop);\n241\t }\n242\t\n243\t // ── Input ────────────────────────────────\n244\t\n245\t private bindInput(): void {\n246\t document.addEventListener('keydown', (e) => {\n247\t // Start game on any key from idle\n248\t if (this.state === 'idle') {\n249\t e.preventDefault();\n250\t this.startGame();\n251\t return;\n252\t }\n253\t\n254\t // Restart on Enter from gameover\n255\t if (this.state === 'gameover') {\n256\t if (e.key === 'Enter' || e.key === ' ') {\n257\t e.preventDefault();\n258\t this.startGame();\n259\t }\n260\t return;\n261\t }\n262\t\n263\t // Toggle pause\n264\t if (e.key === 'p' || e.key === 'P') {\n265\t e.preventDefault();\n266\t this.togglePause();\n267\t return;\n268\t }\n269\t\n270\t if (this.state !== 'playing' || !this.current) return;\n271\t if (this.clearingRows.length > 0) return; // animating\n272\t\n273\t switch (e.key) {\n274\t case 'ArrowLeft':\n275\t e.preventDefault();\n276\t this.move(0, -1);\n277\t break;\n278\t case 'ArrowRight':\n279\t e.preventDefault();\n280\t this.move(0, 1);\n281\t break;\n282\t case 'ArrowDown':\n283\t e.preventDefault();\n284\t this.softDropping = true;\n285\t this.softDrop();\n286\t break;\n287\t case 'ArrowUp':\n288\t case 'x':\n289\t case 'X':\n290\t e.preventDefault();\n291\t this.rotate(1);\n292\t break;\n293\t case 'z':\n294\t case 'Z':\n295\t e.preventDefault();\n296\t this.rotate(-1);\n297\t break;\n298\t case ' ':\n299\t e.preventDefault();\n300\t this.hardDrop();\n301\t break;\n302\t case 'c':\n303\t case 'C':\n304\t e.preventDefault();\n305\t this.hold();\n306\t break;\n307\t }\n308\t });\n309\t\n310\t document.addEventListener('keyup', (e) => {\n311\t if (e.key === 'ArrowDown') {\n312\t this.softDropping = false;\n313\t }\n314\t });\n315\t }\n316\t\n317\t // ── Game lifecycle ───────────────────────\n318\t\n319\t private startGame(): void {\n320\t this.board = createBoard();\n321\t this.bag.reset();\n322\t this.current = null;\n323\t this.holdPiece = null;\n324\t this.holdUsed = false;\n325\t this.score = 0;\n326\t this.level = 1;\n327\t this.lines = 0;\n328\t this.clearingRows = [];\n329\t this.softDropping = false;\n330\t this.state = 'playing';\n331\t this.hideOverlay();\n332\t this.spawnPiece();\n333\t this.lastDrop = performance.now();\n334\t this.updateStats();\n335\t }\n336\t\n337\t private gameOver(): void {\n338\t this.state = 'gameover';\n339\t this.showOverlay('GAME OVER', `Score: ${this.score} — Press Enter to restart`);\n340\t }\n341\t\n342\t private togglePause(): void {\n343\t if (this.state === 'playing') {\n344\t this.state = 'paused';\n345\t this.showOverlay('PAUSED', 'Press P to resume');\n346\t } else if (this.state === 'paused') {\n347\t this.state = 'playing';\n348\t this.lastDrop = performance.now();\n349\t this.hideOverlay();\n350\t }\n351\t }\n352\t\n353\t // ── Overlay helpers ──────────────────────\n354\t\n355\t private showOverlay(title: string, sub: string): void {\n356\t this.overlayTitle.textContent = title;\n357\t this.overlaySub.textContent = sub;\n358\t this.overlay.classList.remove('hidden');\n359\t }\n360\t\n361\t private hideOverlay(): void {\n362\t this.overlay.classList.add('hidden');\n363\t }\n364\t\n365\t // ── Piece spawning ──────────────────────\n366\t\n367\t private spawnPiece(): void {\n368\t const type = this.bag.next();\n369\t this.current = {\n370\t type,\n371\t row: HIDDEN_ROWS - 1, // spawn near top of hidden area\n372\t col: Math.floor(COLS / 2) - 1,\n373\t rotation: 0,\n374\t };\n375\t this.lockTimer = 0;\n376\t this.lockResets = 0;\n377\t this.holdUsed = false;\n378\t\n379\t // Try to push down into the visible area\n380\t if (isValid(this.board, { ...this.current, row: this.current.row + 1 })) {\n381\t this.current.row++;\n382\t }\n383\t\n384\t if (!isValid(this.board, this.current)) {\n385\t // Can't spawn → game over\n386\t this.placePiece();\n387\t this.gameOver();\n388\t }\n389\t }\n390\t\n391\t // ── Movement ─────────────────────────────\n392\t\n393\t private move(dr: number, dc: number): boolean {\n394\t if (!this.current) return false;\n395\t const test = { ...this.current, row: this.current.row + dr, col: this.current.col + dc };\n396\t if (isValid(this.board, test)) {\n397\t this.current = test;\n398\t this.resetLockIfOnGround();\n399\t return true;\n400\t }\n401\t return false;\n402\t }\n403\t\n404\t private rotate(dir: 1 | -1): void {\n405\t if (!this.current) return;\n406\t const from = this.current.rotation;\n407\t const to = (from + dir + 4) % 4;\n408\t const kicks = this.current.type === 'I'\n409\t ? I_WALL_KICKS[`${from}>${to}`]\n410\t : WALL_KICKS[`${from}>${to}`];\n411\t\n412\t if (!kicks) return;\n413\t\n414\t for (const [dc, dr] of kicks) {\n415\t const test: Piece = {\n416\t ...this.current,\n417\t rotation: to,\n418\t row: this.current.row - dr, // SRS kick data: positive y = up, our row increases downward\n419\t col: this.current.col + dc,\n420\t };\n421\t if (isValid(this.board, test)) {\n422\t this.current = test;\n423\t this.resetLockIfOnGround();\n424\t return;\n425\t }\n426\t }\n427\t }\n428\t\n429\t private softDrop(): void {\n430\t if (this.move(1, 0)) {\n431\t this.score += SOFT_DROP_SCORE;\n432\t this.lastDrop = performance.now();\n433\t this.updateStats();\n434\t }\n435\t }\n436\t\n437\t private hardDrop(): void {\n438\t if (!this.current) return;\n439\t let dist = 0;\n440\t while (this.move(1, 0)) {\n441\t dist++;\n442\t }\n443\t this.score += dist * HARD_DROP_SCORE;\n444\t this.updateStats();\n445\t this.placePiece();\n446\t this.afterPlace();\n447\t }\n448\t\n449\t private hold(): void {\n450\t if (!this.current || this.holdUsed) return;\n451\t const type = this.current.type;\n452\t if (this.holdPiece) {\n453\t // Spawn held piece\n454\t this.current = {\n455\t type: this.holdPiece,\n456\t row: HIDDEN_ROWS,\n457\t col: Math.floor(COLS / 2) - 1,\n458\t rotation: 0,\n459\t };\n460\t this.holdPiece = type;\n461\t this.lockTimer = 0;\n462\t this.lockResets = 0;\n463\t } else {\n464\t this.holdPiece = type;\n465\t this.spawnPiece();\n466\t }\n467\t this.holdUsed = true;\n468\t }\n469\t\n470\t // ── Lock delay ───────────────────────────\n471\t\n472\t private isOnGround(): boolean {\n473\t if (!this.current) return false;\n474\t return !isValid(this.board, { ...this.current, row: this.current.row + 1 });\n475\t }\n476\t\n477\t private resetLockIfOnGround(): void {\n478\t if (this.isOnGround() && this.lockResets < MAX_LOCK_RESETS) {\n479\t this.lockTimer = 0;\n480\t this.lockResets++;\n481\t }\n482\t }\n483\t\n484\t // ── Place piece & clear lines ────────────\n485\t\n486\t private placePiece(): void {\n487\t if (!this.current) return;\n488\t for (const [r, c] of getBlocks(this.current)) {\n489\t if (r >= 0 && r < TOTAL_ROWS && c >= 0 && c < COLS) {\n490\t this.board[r][c] = this.current.type;\n491\t }\n492\t }\n493\t this.current = null;\n494\t }\n495\t\n496\t private afterPlace(): void {\n497\t // Find full rows\n498\t const fullRows: number[] = [];\n499\t for (let r = 0; r < TOTAL_ROWS; r++) {\n500\t if (this.board[r].every(cell => cell !== null)) {\n501\t fullRows.push(r);\n502\t }\n503\t }\n504\t\n505\t if (fullRows.length > 0) {\n506\t this.clearingRows = fullRows;\n507\t this.clearAnimStart = performance.now();\n508\t } else {\n509\t this.spawnPiece();\n510\t }\n511\t }\n512\t\n513\t private finishClear(): void {\n514\t const count = this.clearingRows.length;\n515\t\n516\t // Remove rows top-to-bottom (indices are sorted ascending)\n517\t for (const r of this.clearingRows.sort((a, b) => a - b)) {\n518\t this.board.splice(r, 1);\n519\t this.board.unshift(Array<Cell>(COLS).fill(null));\n520\t }\n521\t\n522\t this.lines += count;\n523\t this.score += LINE_SCORES[count] * this.level;\n524\t this.level = Math.floor(this.lines / 10) + 1;\n525\t this.clearingRows = [];\n526\t this.updateStats();\n527\t this.spawnPiece();\n528\t }\n529\t\n530\t // ── Ghost piece ──────────────────────────\n531\t\n532\t private ghostRow(): number {\n533\t if (!this.current) return 0;\n534\t let test = { ...this.current };\n535\t while (isValid(this.board, { ...test, row: test.row + 1 })) {\n536\t test.row++;\n537\t }\n538\t return test.row;\n539\t }\n540\t\n541\t // ── Stats DOM ────────────────────────────\n542\t\n543\t private updateStats(): void {\n544\t this.scoreEl.textContent = this.score.toLocaleString();\n545\t this.levelEl.textContent = String(this.level);\n546\t this.linesEl.textContent = String(this.lines);\n547\t }\n548\t\n549\t // ── Gravity speed ────────────────────────\n550\t\n551\t private getSpeed(): number {\n552\t if (this.softDropping) return SOFT_DROP_SPEED;\n553\t const idx = Math.min(this.level - 1, LEVEL_SPEEDS.length - 1);\n554\t return LEVEL_SPEEDS[idx];\n555\t }\n556\t\n557\t // ── Main loop ────────────────────────────\n558\t\n559\t private loop = (now: number): void => {\n560\t requestAnimationFrame(this.loop);\n561\t\n562\t if (this.state !== 'playing') {\n563\t this.render(now);\n564\t return;\n565\t }\n566\t\n567\t // Line clear animation\n568\t if (this.clearingRows.length > 0) {\n569\t if (now - this.clearAnimStart >= this.CLEAR_ANIM_MS) {\n570\t this.finishClear();\n571\t }\n572\t this.render(now);\n573\t return;\n574\t }\n575\t\n576\t if (!this.current) return;\n577\t\n578\t // Gravity\n579\t const speed = this.getSpeed();\n580\t if (now - this.lastDrop >= speed) {\n581\t if (this.move(1, 0)) {\n582\t this.lastDrop = now;\n583\t } else {\n584\t // On ground\n585\t this.lastDrop = now; // prevent repeated triggers\n586\t }\n587\t }\n588\t\n589\t // Lock delay\n590\t if (this.isOnGround()) {\n591\t if (this.lockTimer === 0) {\n592\t this.lockTimer = now;\n593\t } else if (now - this.lockTimer >= LOCK_DELAY) {\n594\t this.placePiece();\n595\t this.afterPlace();\n596\t }\n597\t } else {\n598\t this.lockTimer = 0;\n599\t }\n600\t\n601\t this.render(now);\n602\t };\n603\t\n604\t // ── Rendering ────────────────────────────\n605\t\n606\t private render(now: number): void {\n607\t this.drawBoard(now);\n608\t this.drawNext();\n609\t this.drawHold();\n610\t }\n611\t\n612\t private drawBoard(now: number): void {\n613\t const ctx = this.boardCtx;\n614\t ctx.clearRect(0, 0, COLS * CELL, ROWS * CELL);\n615\t\n616\t // Grid lines\n617\t ctx.strokeStyle = '#1a1a30';\n618\t ctx.lineWidth = 0.5;\n619\t for (let r = 0; r <= ROWS; r++) {\n620\t ctx.beginPath();\n621\t ctx.moveTo(0, r * CELL);\n622\t ctx.lineTo(COLS * CELL, r * CELL);\n623\t ctx.stroke();\n624\t }\n625\t for (let c = 0; c <= COLS; c++) {\n626\t ctx.beginPath();\n627\t ctx.moveTo(c * CELL, 0);\n628\t ctx.lineTo(c * CELL, ROWS * CELL);\n629\t ctx.stroke();\n630\t }\n631\t\n632\t // Board cells (skip hidden rows for drawing)\n633\t for (let r = HIDDEN_ROWS; r < TOTAL_ROWS; r++) {\n634\t for (let c = 0; c < COLS; c++) {\n635\t const cell = this.board[r][c];\n636\t if (cell) {\n637\t const isClearing = this.clearingRows.includes(r);\n638\t if (isClearing) {\n639\t const progress = Math.min(1, (now - this.clearAnimStart) / this.CLEAR_ANIM_MS);\n640\t const flash = Math.sin(progress * Math.PI * 3) * 0.5 + 0.5;\n641\t ctx.globalAlpha = 1 - progress * 0.5;\n642\t this.drawCell(ctx, c, r - HIDDEN_ROWS, cell, flash);\n643\t ctx.globalAlpha = 1;\n644\t } else {\n645\t this.drawCell(ctx, c, r - HIDDEN_ROWS, cell);\n646\t }\n647\t }\n648\t }\n649\t }\n650\t\n651\t if (!this.current) return;\n652\t\n653\t // Ghost piece\n654\t const ghostR = this.ghostRow();\n655\t if (ghostR !== this.current.row) {\n656\t const ghostPiece: Piece = { ...this.current, row: ghostR };\n657\t for (const [r, c] of getBlocks(ghostPiece)) {\n658\t if (r >= HIDDEN_ROWS) {\n659\t ctx.globalAlpha = GHOST_ALPHA;\n660\t this.drawCell(ctx, c, r - HIDDEN_ROWS, this.current.type);\n661\t ctx.globalAlpha = 1;\n662\t }\n663\t }\n664\t }\n665\t\n666\t // Current piece\n667\t for (const [r, c] of getBlocks(this.current)) {\n668\t if (r >= HIDDEN_ROWS) {\n669\t this.drawCell(ctx, c, r - HIDDEN_ROWS, this.current.type);\n670\t }\n671\t }\n672\t }\n673\t\n674\t private drawCell(\n675\t ctx: CanvasRenderingContext2D,\n676\t col: number,\n677\t row: number,\n678\t type: PieceType,\n679\t flash = 0\n680\t ): void {\n681\t const x = col * CELL;\n682\t const y = row * CELL;\n683\t const color = PIECE_COLORS[type];\n684\t const inset = 1;\n685\t\n686\t // Main fill\n687\t ctx.fillStyle = color;\n688\t ctx.fillRect(x + inset, y + inset, CELL - inset * 2, CELL - inset * 2);\n689\t\n690\t // Highlight (top-left bevel)\n691\t ctx.fillStyle = `rgba(255,255,255,${0.25 + flash * 0.5})`;\n692\t ctx.fillRect(x + inset, y + inset, CELL - inset * 2, 2);\n693\t ctx.fillRect(x + inset, y + inset, 2, CELL - inset * 2);\n694\t\n695\t // Shadow (bottom-right bevel)\n696\t ctx.fillStyle = 'rgba(0,0,0,0.3)';\n697\t ctx.fillRect(x + inset, y + CELL - inset - 2, CELL - inset * 2, 2);\n698\t ctx.fillRect(x + CELL - inset - 2, y + inset, 2, CELL - inset * 2);\n699\t }\n700\t\n701\t private drawPiecePreview(\n702\t ctx: CanvasRenderingContext2D,\n703\t type: PieceType,\n704\t offsetY: number,\n705\t cellSize: number,\n706\t canvasWidth: number\n707\t ): void {\n708\t const blocks = PIECE_SHAPES[type][0]; // rotation 0\n709\t // Find bounding box\n710\t let minC = Infinity, maxC = -Infinity, minR = Infinity, maxR = -Infinity;\n711\t for (const [dr, dc] of blocks) {\n712\t minR = Math.min(minR, dr);\n713\t maxR = Math.max(maxR, dr);\n714\t minC = Math.min(minC, dc);\n715\t maxC = Math.max(maxC, dc);\n716\t }\n717\t const w = (maxC - minC + 1) * cellSize;\n718\t const h = (maxR - minR + 1) * cellSize;\n719\t const ox = (canvasWidth - w) / 2;\n720\t const oy = offsetY + (3 * cellSize - h) / 2;\n721\t\n722\t for (const [dr, dc] of blocks) {\n723\t const x = ox + (dc - minC) * cellSize;\n724\t const y = oy + (dr - minR) * cellSize;\n725\t const color = PIECE_COLORS[type];\n726\t const inset = 1;\n727\t ctx.fillStyle = color;\n728\t ctx.fillRect(x + inset, y + inset, cellSize - inset * 2, cellSize - inset * 2);\n729\t ctx.fillStyle = 'rgba(255,255,255,0.2)';\n730\t ctx.fillRect(x + inset, y + inset, cellSize - inset * 2, 1);\n731\t ctx.fillRect(x + inset, y + inset, 1, cellSize - inset * 2);\n732\t }\n733\t }\n734\t\n735\t private drawNext(): void {\n736\t const ctx = this.nextCtx;\n737\t const canvas = ctx.canvas;\n738\t ctx.clearRect(0, 0, canvas.width, canvas.height);\n739\t\n740\t const previews = this.bag.peek(4);\n741\t for (let i = 0; i < previews.length; i++) {\n742\t this.drawPiecePreview(ctx, previews[i], i * 60, NEXT_CELL, canvas.width);\n743\t }\n744\t }\n745\t\n746\t private drawHold(): void {\n747\t const ctx = this.holdCtx;\n748\t const canvas = ctx.canvas;\n749\t ctx.clearRect(0, 0, canvas.width, canvas.height);\n750\t\n751\t if (this.holdPiece) {\n752\t ctx.globalAlpha = this.holdUsed ? 0.4 : 1;\n753\t this.drawPiecePreview(ctx, this.holdPiece, 0, HOLD_CELL, canvas.width);\n754\t ctx.globalAlpha = 1;\n755\t }\n756\t }\n757\t}\n758\t\n759\t// ── Boot ─────────────────────────────────────\n760\t\n761\tdocument.addEventListener('DOMContentLoaded', () => {\n762\t new TetrisGame();\n763\t});\n764\t"}]},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"6ba018ff-d7a7-4f14-8004-72eac229e4c5","timestamp":"2026-04-04T21:01:52.483Z","tool_use_result":{"type":"text","file":{"filePath":"/tmp/loop-bench-gt1ikbl4/src/game.ts","content":"// ──────────────────────────────────────────────\n// Tetris – full browser implementation\n// ──────────────────────────────────────────────\n\n// ── Constants ────────────────────────────────\n\nconst COLS = 10;\nconst ROWS = 20;\nconst HIDDEN_ROWS = 2; // rows above visible area for spawning\nconst TOTAL_ROWS = ROWS + HIDDEN_ROWS;\nconst CELL = 30; // pixel size of each cell\nconst NEXT_CELL = 18;\nconst HOLD_CELL = 18;\nconst LOCK_DELAY = 500; // ms before piece locks after landing\nconst MAX_LOCK_RESETS = 15;\n\n// NES-inspired speed curve: frames per gravity tick at 60fps → converted to ms\nconst LEVEL_SPEEDS: number[] = [\n 800, 717, 633, 550, 467, 383, 300, 217, 133, 100,\n 83, 83, 83, 67, 67, 67, 50, 50, 50, 33,\n];\n\nconst SOFT_DROP_SPEED = 50; // ms\n\n// Scoring (guideline)\nconst LINE_SCORES = [0, 100, 300, 500, 800]; // 0,1,2,3,4 lines\nconst SOFT_DROP_SCORE = 1; // per cell\nconst HARD_DROP_SCORE = 2; // per cell\n\n// ── Piece Definitions (SRS) ─────────────────\n\ntype PieceType = 'I' | 'O' | 'T' | 'S' | 'Z' | 'J' | 'L';\n\n// Each rotation state is a list of [row, col] offsets from piece origin\nconst PIECE_SHAPES: Record<PieceType, number[][][]> = {\n I: [\n [[0,-1],[0,0],[0,1],[0,2]],\n [[-1,1],[0,1],[1,1],[2,1]],\n [[1,-1],[1,0],[1,1],[1,2]],\n [[-1,0],[0,0],[1,0],[2,0]],\n ],\n O: [\n [[0,0],[0,1],[1,0],[1,1]],\n [[0,0],[0,1],[1,0],[1,1]],\n [[0,0],[0,1],[1,0],[1,1]],\n [[0,0],[0,1],[1,0],[1,1]],\n ],\n T: [\n [[0,-1],[0,0],[0,1],[-1,0]],\n [[-1,0],[0,0],[1,0],[0,1]],\n [[0,-1],[0,0],[0,1],[1,0]],\n [[-1,0],[0,0],[1,0],[0,-1]],\n ],\n S: [\n [[0,-1],[0,0],[-1,0],[-1,1]],\n [[-1,0],[0,0],[0,1],[1,1]],\n [[1,-1],[1,0],[0,0],[0,1]],\n [[-1,-1],[0,-1],[0,0],[1,0]],\n ],\n Z: [\n [[-1,-1],[-1,0],[0,0],[0,1]],\n [[0,0],[1,0],[-1,1],[0,1]],\n [[0,-1],[0,0],[1,0],[1,1]],\n [[0,0],[0,-1],[1,-1],[-1,0]],\n ],\n J: [\n [[-1,-1],[0,-1],[0,0],[0,1]],\n [[-1,0],[0,0],[1,0],[-1,1]],\n [[0,-1],[0,0],[0,1],[1,1]],\n [[1,0],[0,0],[-1,0],[1,-1]],\n ],\n L: [\n [[-1,1],[0,-1],[0,0],[0,1]],\n [[-1,0],[0,0],[1,0],[1,1]],\n [[0,-1],[0,0],[0,1],[1,-1]],\n [[-1,-1],[-1,0],[0,0],[1,0]],\n ],\n};\n\n// SRS wall kick data (non-I pieces)\nconst WALL_KICKS: Record<string, number[][]> = {\n '0>1': [[0,0],[-1,0],[-1,-1],[0,2],[-1,2]],\n '1>0': [[0,0],[1,0],[1,1],[0,-2],[1,-2]],\n '1>2': [[0,0],[1,0],[1,1],[0,-2],[1,-2]],\n '2>1': [[0,0],[-1,0],[-1,-1],[0,2],[-1,2]],\n '2>3': [[0,0],[1,0],[1,-1],[0,2],[1,2]],\n '3>2': [[0,0],[-1,0],[-1,1],[0,-2],[-1,-2]],\n '3>0': [[0,0],[-1,0],[-1,1],[0,-2],[-1,-2]],\n '0>3': [[0,0],[1,0],[1,-1],[0,2],[1,2]],\n};\n\nconst I_WALL_KICKS: Record<string, number[][]> = {\n '0>1': [[0,0],[-2,0],[1,0],[-2,1],[1,-2]],\n '1>0': [[0,0],[2,0],[-1,0],[2,-1],[-1,2]],\n '1>2': [[0,0],[-1,0],[2,0],[-1,-2],[2,1]],\n '2>1': [[0,0],[1,0],[-2,0],[1,2],[-2,-1]],\n '2>3': [[0,0],[2,0],[-1,0],[2,-1],[-1,2]],\n '3>2': [[0,0],[-2,0],[1,0],[-2,1],[1,-2]],\n '3>0': [[0,0],[1,0],[-2,0],[1,2],[-2,-1]],\n '0>3': [[0,0],[-1,0],[2,0],[-1,-2],[2,1]],\n};\n\nconst PIECE_COLORS: Record<PieceType, string> = {\n I: '#00e5ff',\n O: '#ffea00',\n T: '#d500f9',\n S: '#00e676',\n Z: '#ff1744',\n J: '#2979ff',\n L: '#ff9100',\n};\n\nconst GHOST_ALPHA = 0.25;\n\nconst ALL_PIECES: PieceType[] = ['I', 'O', 'T', 'S', 'Z', 'J', 'L'];\n\n// ── Types ────────────────────────────────────\n\ntype Cell = PieceType | null;\n\ninterface Piece {\n type: PieceType;\n row: number; // position on the board (top-left of bounding box concept → actually the origin)\n col: number;\n rotation: number; // 0-3\n}\n\ntype GameState = 'idle' | 'playing' | 'paused' | 'gameover';\n\n// ── Bag Randomizer (7-bag) ───────────────────\n\nfunction shuffle<T>(arr: T[]): T[] {\n const a = [...arr];\n for (let i = a.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [a[i], a[j]] = [a[j], a[i]];\n }\n return a;\n}\n\nclass BagRandomizer {\n private queue: PieceType[] = [];\n\n next(): PieceType {\n if (this.queue.length < 7) {\n this.queue.push(...shuffle(ALL_PIECES));\n }\n return this.queue.shift()!;\n }\n\n peek(count: number): PieceType[] {\n while (this.queue.length < count + 7) {\n this.queue.push(...shuffle(ALL_PIECES));\n }\n return this.queue.slice(0, count);\n }\n\n reset(): void {\n this.queue = [];\n }\n}\n\n// ── Board helpers ────────────────────────────\n\nfunction createBoard(): Cell[][] {\n return Array.from({ length: TOTAL_ROWS }, () => Array<Cell>(COLS).fill(null));\n}\n\nfunction getBlocks(piece: Piece): [number, number][] {\n return PIECE_SHAPES[piece.type][piece.rotation].map(\n ([dr, dc]) => [piece.row + dr, piece.col + dc] as [number, number]\n );\n}\n\nfunction isValid(board: Cell[][], piece: Piece): boolean {\n for (const [r, c] of getBlocks(piece)) {\n if (r < 0 || r >= TOTAL_ROWS || c < 0 || c >= COLS) return false;\n if (board[r][c] !== null) return false;\n }\n return true;\n}\n\n// ── Main Game Class ──────────────────────────\n\nclass TetrisGame {\n private board: Cell[][] = createBoard();\n private bag = new BagRandomizer();\n private current: Piece | null = null;\n private holdPiece: PieceType | null = null;\n private holdUsed = false;\n private state: GameState = 'idle';\n\n // Stats\n private score = 0;\n private level = 1;\n private lines = 0;\n\n // Timing\n private lastDrop = 0;\n private lockTimer = 0;\n private lockResets = 0;\n private softDropping = false;\n\n // Line clear animation\n private clearingRows: number[] = [];\n private clearAnimStart = 0;\n private readonly CLEAR_ANIM_MS = 300;\n\n // Canvas refs\n private boardCtx: CanvasRenderingContext2D;\n private nextCtx: CanvasRenderingContext2D;\n private holdCtx: CanvasRenderingContext2D;\n\n // DOM refs\n private scoreEl: HTMLElement;\n private levelEl: HTMLElement;\n private linesEl: HTMLElement;\n private overlay: HTMLElement;\n private overlayTitle: HTMLElement;\n private overlaySub: HTMLElement;\n\n constructor() {\n const boardCanvas = document.getElementById('board-canvas') as HTMLCanvasElement;\n const nextCanvas = document.getElementById('next-canvas') as HTMLCanvasElement;\n const holdCanvas = document.getElementById('hold-canvas') as HTMLCanvasElement;\n\n this.boardCtx = boardCanvas.getContext('2d')!;\n this.nextCtx = nextCanvas.getContext('2d')!;\n this.holdCtx = holdCanvas.getContext('2d')!;\n\n this.scoreEl = document.getElementById('score-value')!;\n this.levelEl = document.getElementById('level-value')!;\n this.linesEl = document.getElementById('lines-value')!;\n this.overlay = document.getElementById('overlay')!;\n this.overlayTitle = document.getElementById('overlay-title')!;\n this.overlaySub = document.getElementById('overlay-sub')!;\n\n this.bindInput();\n this.showOverlay('TETRIS', 'Press any key to start');\n requestAnimationFrame(this.loop);\n }\n\n // ── Input ────────────────────────────────\n\n private bindInput(): void {\n document.addEventListener('keydown', (e) => {\n // Start game on any key from idle\n if (this.state === 'idle') {\n e.preventDefault();\n this.startGame();\n return;\n }\n\n // Restart on Enter from gameover\n if (this.state === 'gameover') {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n this.startGame();\n }\n return;\n }\n\n // Toggle pause\n if (e.key === 'p' || e.key === 'P') {\n e.preventDefault();\n this.togglePause();\n return;\n }\n\n if (this.state !== 'playing' || !this.current) return;\n if (this.clearingRows.length > 0) return; // animating\n\n switch (e.key) {\n case 'ArrowLeft':\n e.preventDefault();\n this.move(0, -1);\n break;\n case 'ArrowRight':\n e.preventDefault();\n this.move(0, 1);\n break;\n case 'ArrowDown':\n e.preventDefault();\n this.softDropping = true;\n this.softDrop();\n break;\n case 'ArrowUp':\n case 'x':\n case 'X':\n e.preventDefault();\n this.rotate(1);\n break;\n case 'z':\n case 'Z':\n e.preventDefault();\n this.rotate(-1);\n break;\n case ' ':\n e.preventDefault();\n this.hardDrop();\n break;\n case 'c':\n case 'C':\n e.preventDefault();\n this.hold();\n break;\n }\n });\n\n document.addEventListener('keyup', (e) => {\n if (e.key === 'ArrowDown') {\n this.softDropping = false;\n }\n });\n }\n\n // ── Game lifecycle ───────────────────────\n\n private startGame(): void {\n this.board = createBoard();\n this.bag.reset();\n this.current = null;\n this.holdPiece = null;\n this.holdUsed = false;\n this.score = 0;\n this.level = 1;\n this.lines = 0;\n this.clearingRows = [];\n this.softDropping = false;\n this.state = 'playing';\n this.hideOverlay();\n this.spawnPiece();\n this.lastDrop = performance.now();\n this.updateStats();\n }\n\n private gameOver(): void {\n this.state = 'gameover';\n this.showOverlay('GAME OVER', `Score: ${this.score} — Press Enter to restart`);\n }\n\n private togglePause(): void {\n if (this.state === 'playing') {\n this.state = 'paused';\n this.showOverlay('PAUSED', 'Press P to resume');\n } else if (this.state === 'paused') {\n this.state = 'playing';\n this.lastDrop = performance.now();\n this.hideOverlay();\n }\n }\n\n // ── Overlay helpers ──────────────────────\n\n private showOverlay(title: string, sub: string): void {\n this.overlayTitle.textContent = title;\n this.overlaySub.textContent = sub;\n this.overlay.classList.remove('hidden');\n }\n\n private hideOverlay(): void {\n this.overlay.classList.add('hidden');\n }\n\n // ── Piece spawning ──────────────────────\n\n private spawnPiece(): void {\n const type = this.bag.next();\n this.current = {\n type,\n row: HIDDEN_ROWS - 1, // spawn near top of hidden area\n col: Math.floor(COLS / 2) - 1,\n rotation: 0,\n };\n this.lockTimer = 0;\n this.lockResets = 0;\n this.holdUsed = false;\n\n // Try to push down into the visible area\n if (isValid(this.board, { ...this.current, row: this.current.row + 1 })) {\n this.current.row++;\n }\n\n if (!isValid(this.board, this.current)) {\n // Can't spawn → game over\n this.placePiece();\n this.gameOver();\n }\n }\n\n // ── Movement ─────────────────────────────\n\n private move(dr: number, dc: number): boolean {\n if (!this.current) return false;\n const test = { ...this.current, row: this.current.row + dr, col: this.current.col + dc };\n if (isValid(this.board, test)) {\n this.current = test;\n this.resetLockIfOnGround();\n return true;\n }\n return false;\n }\n\n private rotate(dir: 1 | -1): void {\n if (!this.current) return;\n const from = this.current.rotation;\n const to = (from + dir + 4) % 4;\n const kicks = this.current.type === 'I'\n ? I_WALL_KICKS[`${from}>${to}`]\n : WALL_KICKS[`${from}>${to}`];\n\n if (!kicks) return;\n\n for (const [dc, dr] of kicks) {\n const test: Piece = {\n ...this.current,\n rotation: to,\n row: this.current.row - dr, // SRS kick data: positive y = up, our row increases downward\n col: this.current.col + dc,\n };\n if (isValid(this.board, test)) {\n this.current = test;\n this.resetLockIfOnGround();\n return;\n }\n }\n }\n\n private softDrop(): void {\n if (this.move(1, 0)) {\n this.score += SOFT_DROP_SCORE;\n this.lastDrop = performance.now();\n this.updateStats();\n }\n }\n\n private hardDrop(): void {\n if (!this.current) return;\n let dist = 0;\n while (this.move(1, 0)) {\n dist++;\n }\n this.score += dist * HARD_DROP_SCORE;\n this.updateStats();\n this.placePiece();\n this.afterPlace();\n }\n\n private hold(): void {\n if (!this.current || this.holdUsed) return;\n const type = this.current.type;\n if (this.holdPiece) {\n // Spawn held piece\n this.current = {\n type: this.holdPiece,\n row: HIDDEN_ROWS,\n col: Math.floor(COLS / 2) - 1,\n rotation: 0,\n };\n this.holdPiece = type;\n this.lockTimer = 0;\n this.lockResets = 0;\n } else {\n this.holdPiece = type;\n this.spawnPiece();\n }\n this.holdUsed = true;\n }\n\n // ── Lock delay ───────────────────────────\n\n private isOnGround(): boolean {\n if (!this.current) return false;\n return !isValid(this.board, { ...this.current, row: this.current.row + 1 });\n }\n\n private resetLockIfOnGround(): void {\n if (this.isOnGround() && this.lockResets < MAX_LOCK_RESETS) {\n this.lockTimer = 0;\n this.lockResets++;\n }\n }\n\n // ── Place piece & clear lines ────────────\n\n private placePiece(): void {\n if (!this.current) return;\n for (const [r, c] of getBlocks(this.current)) {\n if (r >= 0 && r < TOTAL_ROWS && c >= 0 && c < COLS) {\n this.board[r][c] = this.current.type;\n }\n }\n this.current = null;\n }\n\n private afterPlace(): void {\n // Find full rows\n const fullRows: number[] = [];\n for (let r = 0; r < TOTAL_ROWS; r++) {\n if (this.board[r].every(cell => cell !== null)) {\n fullRows.push(r);\n }\n }\n\n if (fullRows.length > 0) {\n this.clearingRows = fullRows;\n this.clearAnimStart = performance.now();\n } else {\n this.spawnPiece();\n }\n }\n\n private finishClear(): void {\n const count = this.clearingRows.length;\n\n // Remove rows top-to-bottom (indices are sorted ascending)\n for (const r of this.clearingRows.sort((a, b) => a - b)) {\n this.board.splice(r, 1);\n this.board.unshift(Array<Cell>(COLS).fill(null));\n }\n\n this.lines += count;\n this.score += LINE_SCORES[count] * this.level;\n this.level = Math.floor(this.lines / 10) + 1;\n this.clearingRows = [];\n this.updateStats();\n this.spawnPiece();\n }\n\n // ── Ghost piece ──────────────────────────\n\n private ghostRow(): number {\n if (!this.current) return 0;\n let test = { ...this.current };\n while (isValid(this.board, { ...test, row: test.row + 1 })) {\n test.row++;\n }\n return test.row;\n }\n\n // ── Stats DOM ────────────────────────────\n\n private updateStats(): void {\n this.scoreEl.textContent = this.score.toLocaleString();\n this.levelEl.textContent = String(this.level);\n this.linesEl.textContent = String(this.lines);\n }\n\n // ── Gravity speed ────────────────────────\n\n private getSpeed(): number {\n if (this.softDropping) return SOFT_DROP_SPEED;\n const idx = Math.min(this.level - 1, LEVEL_SPEEDS.length - 1);\n return LEVEL_SPEEDS[idx];\n }\n\n // ── Main loop ────────────────────────────\n\n private loop = (now: number): void => {\n requestAnimationFrame(this.loop);\n\n if (this.state !== 'playing') {\n this.render(now);\n return;\n }\n\n // Line clear animation\n if (this.clearingRows.length > 0) {\n if (now - this.clearAnimStart >= this.CLEAR_ANIM_MS) {\n this.finishClear();\n }\n this.render(now);\n return;\n }\n\n if (!this.current) return;\n\n // Gravity\n const speed = this.getSpeed();\n if (now - this.lastDrop >= speed) {\n if (this.move(1, 0)) {\n this.lastDrop = now;\n } else {\n // On ground\n this.lastDrop = now; // prevent repeated triggers\n }\n }\n\n // Lock delay\n if (this.isOnGround()) {\n if (this.lockTimer === 0) {\n this.lockTimer = now;\n } else if (now - this.lockTimer >= LOCK_DELAY) {\n this.placePiece();\n this.afterPlace();\n }\n } else {\n this.lockTimer = 0;\n }\n\n this.render(now);\n };\n\n // ── Rendering ────────────────────────────\n\n private render(now: number): void {\n this.drawBoard(now);\n this.drawNext();\n this.drawHold();\n }\n\n private drawBoard(now: number): void {\n const ctx = this.boardCtx;\n ctx.clearRect(0, 0, COLS * CELL, ROWS * CELL);\n\n // Grid lines\n ctx.strokeStyle = '#1a1a30';\n ctx.lineWidth = 0.5;\n for (let r = 0; r <= ROWS; r++) {\n ctx.beginPath();\n ctx.moveTo(0, r * CELL);\n ctx.lineTo(COLS * CELL, r * CELL);\n ctx.stroke();\n }\n for (let c = 0; c <= COLS; c++) {\n ctx.beginPath();\n ctx.moveTo(c * CELL, 0);\n ctx.lineTo(c * CELL, ROWS * CELL);\n ctx.stroke();\n }\n\n // Board cells (skip hidden rows for drawing)\n for (let r = HIDDEN_ROWS; r < TOTAL_ROWS; r++) {\n for (let c = 0; c < COLS; c++) {\n const cell = this.board[r][c];\n if (cell) {\n const isClearing = this.clearingRows.includes(r);\n if (isClearing) {\n const progress = Math.min(1, (now - this.clearAnimStart) / this.CLEAR_ANIM_MS);\n const flash = Math.sin(progress * Math.PI * 3) * 0.5 + 0.5;\n ctx.globalAlpha = 1 - progress * 0.5;\n this.drawCell(ctx, c, r - HIDDEN_ROWS, cell, flash);\n ctx.globalAlpha = 1;\n } else {\n this.drawCell(ctx, c, r - HIDDEN_ROWS, cell);\n }\n }\n }\n }\n\n if (!this.current) return;\n\n // Ghost piece\n const ghostR = this.ghostRow();\n if (ghostR !== this.current.row) {\n const ghostPiece: Piece = { ...this.current, row: ghostR };\n for (const [r, c] of getBlocks(ghostPiece)) {\n if (r >= HIDDEN_ROWS) {\n ctx.globalAlpha = GHOST_ALPHA;\n this.drawCell(ctx, c, r - HIDDEN_ROWS, this.current.type);\n ctx.globalAlpha = 1;\n }\n }\n }\n\n // Current piece\n for (const [r, c] of getBlocks(this.current)) {\n if (r >= HIDDEN_ROWS) {\n this.drawCell(ctx, c, r - HIDDEN_ROWS, this.current.type);\n }\n }\n }\n\n private drawCell(\n ctx: CanvasRenderingContext2D,\n col: number,\n row: number,\n type: PieceType,\n flash = 0\n ): void {\n const x = col * CELL;\n const y = row * CELL;\n const color = PIECE_COLORS[type];\n const inset = 1;\n\n // Main fill\n ctx.fillStyle = color;\n ctx.fillRect(x + inset, y + inset, CELL - inset * 2, CELL - inset * 2);\n\n // Highlight (top-left bevel)\n ctx.fillStyle = `rgba(255,255,255,${0.25 + flash * 0.5})`;\n ctx.fillRect(x + inset, y + inset, CELL - inset * 2, 2);\n ctx.fillRect(x + inset, y + inset, 2, CELL - inset * 2);\n\n // Shadow (bottom-right bevel)\n ctx.fillStyle = 'rgba(0,0,0,0.3)';\n ctx.fillRect(x + inset, y + CELL - inset - 2, CELL - inset * 2, 2);\n ctx.fillRect(x + CELL - inset - 2, y + inset, 2, CELL - inset * 2);\n }\n\n private drawPiecePreview(\n ctx: CanvasRenderingContext2D,\n type: PieceType,\n offsetY: number,\n cellSize: number,\n canvasWidth: number\n ): void {\n const blocks = PIECE_SHAPES[type][0]; // rotation 0\n // Find bounding box\n let minC = Infinity, maxC = -Infinity, minR = Infinity, maxR = -Infinity;\n for (const [dr, dc] of blocks) {\n minR = Math.min(minR, dr);\n maxR = Math.max(maxR, dr);\n minC = Math.min(minC, dc);\n maxC = Math.max(maxC, dc);\n }\n const w = (maxC - minC + 1) * cellSize;\n const h = (maxR - minR + 1) * cellSize;\n const ox = (canvasWidth - w) / 2;\n const oy = offsetY + (3 * cellSize - h) / 2;\n\n for (const [dr, dc] of blocks) {\n const x = ox + (dc - minC) * cellSize;\n const y = oy + (dr - minR) * cellSize;\n const color = PIECE_COLORS[type];\n const inset = 1;\n ctx.fillStyle = color;\n ctx.fillRect(x + inset, y + inset, cellSize - inset * 2, cellSize - inset * 2);\n ctx.fillStyle = 'rgba(255,255,255,0.2)';\n ctx.fillRect(x + inset, y + inset, cellSize - inset * 2, 1);\n ctx.fillRect(x + inset, y + inset, 1, cellSize - inset * 2);\n }\n }\n\n private drawNext(): void {\n const ctx = this.nextCtx;\n const canvas = ctx.canvas;\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n const previews = this.bag.peek(4);\n for (let i = 0; i < previews.length; i++) {\n this.drawPiecePreview(ctx, previews[i], i * 60, NEXT_CELL, canvas.width);\n }\n }\n\n private drawHold(): void {\n const ctx = this.holdCtx;\n const canvas = ctx.canvas;\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n if (this.holdPiece) {\n ctx.globalAlpha = this.holdUsed ? 0.4 : 1;\n this.drawPiecePreview(ctx, this.holdPiece, 0, HOLD_CELL, canvas.width);\n ctx.globalAlpha = 1;\n }\n }\n}\n\n// ── Boot ─────────────────────────────────────\n\ndocument.addEventListener('DOMContentLoaded', () => {\n new TetrisGame();\n});\n","numLines":764,"startLine":1,"totalLines":764}}} +{"type":"assistant","message":{"id":"d6f61da8-3f8c-42d0-ad3e-c36d76c98d9f","container":null,"model":"<synthetic>","role":"assistant","stop_reason":"stop_sequence","stop_sequence":"","type":"message","usage":{"input_tokens":0,"output_tokens":0,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":null,"cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":0},"inference_geo":null,"iterations":null,"speed":null},"content":[{"type":"text","text":"Invalid API key · Fix external API key"}],"context_management":null},"parent_tool_use_id":null,"session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","uuid":"70376077-f1c8-44b2-b47d-a093c20d1a34","error":"authentication_failed"} +{"type":"result","subtype":"success","is_error":true,"duration_ms":256423,"duration_api_ms":254300,"num_turns":14,"result":"Invalid API key · Fix external API key","stop_reason":"stop_sequence","session_id":"e1a7addf-6403-4c8f-9e0b-e47fc65be908","total_cost_usd":0.6562947500000003,"usage":{"input_tokens":16,"cache_creation_input_tokens":17271,"cache_read_input_tokens":128742,"output_tokens":19356,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":17271},"inference_geo":"","iterations":[],"speed":"standard"},"modelUsage":{"claude-opus-4-6":{"inputTokens":16,"outputTokens":19356,"cacheReadInputTokens":128742,"cacheCreationInputTokens":17271,"webSearchRequests":0,"costUSD":0.6562947500000003,"contextWindow":200000,"maxOutputTokens":64000}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","uuid":"85cd0423-1af4-44f9-8216-9c369268123c"} diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/claude_output.json b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/claude_output.json @@ -0,0 +1 @@ +{"type":"result","subtype":"success","is_error":false,"duration_ms":153497,"duration_api_ms":151572,"num_turns":15,"result":"Everything compiles cleanly. Here's a summary of the complete Tetris game:\n\n## Files Created\n\n### `src/tetris.ts` — Game engine (544 lines)\nThe full game logic in TypeScript, including:\n\n- **7 standard Tetrominos** (I, O, T, S, Z, J, L) with correct SRS rotation shapes\n- **SRS Wall Kicks** — full Super Rotation System wall-kick tables for both I-pieces and standard pieces, so rotations work near walls and in tight spaces\n- **Collision detection** — validates moves against board boundaries and locked blocks\n- **Line clearing** — detects and removes full rows, shifts board down\n- **Scoring** — NES-style point system (100/300/500/800 × level multiplier for 1/2/3/4 lines), plus soft drop (+1/row) and hard drop (+2/row) bonuses\n- **Leveling** — level increases every 10 lines; gravity speeds up from 1000ms to a minimum of 50ms\n- **Lock delay** — 500ms grace period when a piece lands, allowing slides and rotations before locking\n- **Ghost piece** — translucent preview showing where the piece will land\n- **Next piece preview** — shown in a sidebar canvas\n- **Pause / Game Over** states with overlays\n\n### `index.html` — UI shell\nDark-themed layout with:\n- 320×640 game canvas (10×20 grid of 32px blocks)\n- Sidebar showing next piece, score, lines, level, and control reference\n\n### Keyboard Controls\n| Key | Action |\n|---|---|\n| `←` / `→` or `A` / `D` | Move left/right |\n| `↓` or `S` | Soft drop |\n| `↑` or `W` | Rotate clockwise |\n| `Z` | Rotate counter-clockwise |\n| `Space` | Hard drop |\n| `P` / `Esc` | Pause |\n| `R` | Restart (after game over) |\n\n### How to play\nOpen `index.html` in a browser. Since it uses `type=\"module\"`, you'll need to serve it over HTTP (e.g., `npx serve .` or `python3 -m http.server`) rather than opening as a `file://` URL.","stop_reason":"end_turn","session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","total_cost_usd":0.4540989999999999,"usage":{"input_tokens":17,"cache_creation_input_tokens":22938,"cache_read_input_tokens":152753,"output_tokens":9371,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":22938},"inference_geo":"","iterations":[],"speed":"standard"},"modelUsage":{"claude-opus-4-6":{"inputTokens":17,"outputTokens":9371,"cacheReadInputTokens":152753,"cacheCreationInputTokens":22938,"webSearchRequests":0,"costUSD":0.4540989999999999,"contextWindow":200000,"maxOutputTokens":64000}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","uuid":"6255903a-c924-47ed-ae02-d7e35cb68ec0"} +\ No newline at end of file diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/claude_stderr.log b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/claude_stderr.log diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/eval_results.json b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/eval_results.json @@ -0,0 +1,261 @@ +{ + "structural": { + "pass": true, + "checks": [ + { + "name": "entry_point_exists", + "pass": true, + "detail": "index.html found" + }, + { + "name": "package_json_exists", + "pass": true, + "detail": "package.json found" + }, + { + "name": "build_succeeds", + "pass": true, + "detail": "npm run build completed successfully" + }, + { + "name": "typescript_compiles", + "pass": true, + "detail": "tsc --noEmit passed" + } + ], + "score": 1.0 + }, + "functional": { + "pass": false, + "error": "playwright eval not yet wired", + "score": 0 + }, + "quality": { + "lint": { + "pass": true, + "errors": 0, + "warnings": 0 + }, + "typecheck": { + "pass": true + }, + "performance": { + "bundle_size_bytes": 36714, + "size_under_512kb": true + }, + "score": 0.67 + }, + "code_analysis": { + "files": { + "total": 7, + "code": 3, + "docs": 0, + "unnecessary": 0, + "unnecessary_list": [] + }, + "lines_of_code": 1134, + "dependencies": { + "production": 0, + "dev": 5, + "total": 5 + }, + "complexity": "moderate", + "console_logs": 0, + "magic_numbers": { + "count": 20, + "excessive": false + }, + "function_length": { + "count": 56, + "average": 7.4, + "max": 43, + "long_functions": 0 + }, + "max_nesting_depth": 12, + "global_declarations": 66, + "naming": { + "dominant_style": "camelCase", + "consistency_pct": 100.0, + "camel_case": 385, + "snake_case": 0 + }, + "error_handling": { + "try_catch_blocks": 0, + "has_error_handling": false + }, + "comments": { + "comment_lines": 55, + "source_lines": 899, + "ratio_pct": 6.1 + }, + "separation_of_concerns": { + "verdict": "mixed", + "files_with_rendering": 2, + "files_with_logic": 2, + "files_with_both": 2 + }, + "html_validation": { + "valid": false, + "errors": 8 + }, + "duplication_percentage": 0.0, + "score": 0.9 + }, + "transcript_analysis": { + "total_events": 43, + "tool_calls": { + "total": 14, + "bash": 8, + "write": 1, + "edit": 1, + "read": 4 + }, + "wasted_turns": { + "total": 0, + "docs": 0, + "ascii_art": 0, + "server_starts": 0 + }, + "errors_encountered": 0, + "thinking_blocks": 2, + "text_blocks": 9, + "productivity_ratio": 1.0, + "self_tested": false, + "score": 1.0 + }, + "gameplay_bot": { + "pass": false, + "score": 0.88, + "total": 16, + "passed": 14, + "failed": 2, + "report": { + "implementation": { + "renderer": "canvas", + "grid_detected": true, + "grid_bounds": { + "x": 0, + "y": 0, + "width": 320, + "height": 640 + }, + "controls": { + "left": "ArrowLeft", + "right": "ArrowRight", + "down": "ArrowDown", + "rotate": "z", + "drop": "Space" + }, + "start_mechanism": "auto", + "score_element_found": true + }, + "tests": [ + { + "name": "game_loads", + "pass": true, + "detail": "no console errors" + }, + { + "name": "game_starts", + "pass": true, + "detail": "started via auto" + }, + { + "name": "auto_drop", + "pass": true, + "detail": "grid state changed after 5s with no input" + }, + { + "name": "move_left", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "move_right", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "move_down", + "pass": false, + "detail": "no change detected after key press" + }, + { + "name": "rotate", + "pass": true, + "detail": "piece shape changed after rotate key" + }, + { + "name": "all_pieces_rotate", + "pass": true, + "detail": "rotated: [other] failed: [] (tested 1 piece types in 60 attempts)" + }, + { + "name": "hard_drop", + "pass": true, + "detail": "piece immediately dropped and new piece appeared" + }, + { + "name": "piece_locks", + "pass": true, + "detail": "filled cells persist at bottom" + }, + { + "name": "new_piece_spawns", + "pass": true, + "detail": "new piece detected at top of grid" + }, + { + "name": "multiple_pieces", + "pass": true, + "detail": "grid accumulated cells: 20 -> 42" + }, + { + "name": "line_clear", + "pass": true, + "detail": "1 line(s) cleared during AI play" + }, + { + "name": "score_changes", + "pass": false, + "detail": "score did not increase: [212] -> no change after polling" + }, + { + "name": "game_over", + "pass": true, + "detail": "game stopped after stacking to top" + }, + { + "name": "playable_30s", + "pass": true, + "detail": "played for 30s, placed 80 pieces, no crashes" + } + ], + "summary": { + "total": 16, + "passed": 14, + "failed": 2, + "score": 0.88 + }, + "gameplay": { + "pieces_placed": 121, + "lines_cleared": 2, + "max_score_observed": 252, + "play_duration_seconds": 30, + "errors_during_play": 0 + }, + "performance": { + "load_time_ms": 24 + }, + "accessibility": { + "issues": [ + "canvas without aria-label or role", + "canvas without aria-label or role" + ], + "issue_count": 2, + "pass": false + } + } + }, + "score": 0.747 +} +\ No newline at end of file diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/gameplay-bot-report.json b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/gameplay-bot-report.json @@ -0,0 +1,127 @@ +{ + "implementation": { + "renderer": "canvas", + "grid_detected": true, + "grid_bounds": { + "x": 0, + "y": 0, + "width": 320, + "height": 640 + }, + "controls": { + "left": "ArrowLeft", + "right": "ArrowRight", + "down": "ArrowDown", + "rotate": "z", + "drop": "Space" + }, + "start_mechanism": "auto", + "score_element_found": true + }, + "tests": [ + { + "name": "game_loads", + "pass": true, + "detail": "no console errors" + }, + { + "name": "game_starts", + "pass": true, + "detail": "started via auto" + }, + { + "name": "auto_drop", + "pass": true, + "detail": "grid state changed after 5s with no input" + }, + { + "name": "move_left", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "move_right", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "move_down", + "pass": false, + "detail": "no change detected after key press" + }, + { + "name": "rotate", + "pass": true, + "detail": "piece shape changed after rotate key" + }, + { + "name": "all_pieces_rotate", + "pass": true, + "detail": "rotated: [other] failed: [] (tested 1 piece types in 60 attempts)" + }, + { + "name": "hard_drop", + "pass": true, + "detail": "piece immediately dropped and new piece appeared" + }, + { + "name": "piece_locks", + "pass": true, + "detail": "filled cells persist at bottom" + }, + { + "name": "new_piece_spawns", + "pass": true, + "detail": "new piece detected at top of grid" + }, + { + "name": "multiple_pieces", + "pass": true, + "detail": "grid accumulated cells: 20 -> 42" + }, + { + "name": "line_clear", + "pass": true, + "detail": "1 line(s) cleared during AI play" + }, + { + "name": "score_changes", + "pass": false, + "detail": "score did not increase: [212] -> no change after polling" + }, + { + "name": "game_over", + "pass": true, + "detail": "game stopped after stacking to top" + }, + { + "name": "playable_30s", + "pass": true, + "detail": "played for 30s, placed 80 pieces, no crashes" + } + ], + "summary": { + "total": 16, + "passed": 14, + "failed": 2, + "score": 0.88 + }, + "gameplay": { + "pieces_placed": 121, + "lines_cleared": 2, + "max_score_observed": 252, + "play_duration_seconds": 30, + "errors_during_play": 0 + }, + "performance": { + "load_time_ms": 24 + }, + "accessibility": { + "issues": [ + "canvas without aria-label or role", + "canvas without aria-label or role" + ], + "issue_count": 2, + "pass": false + } +} +\ No newline at end of file diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/meta.json b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/meta.json @@ -0,0 +1,30 @@ +{ + "model": "opus", + "effort": "high", + "prompt_style": "simple", + "language": "typescript", + "human_language": "en", + "tool_read": "on", + "tool_write": "on", + "tool_edit": "on", + "tool_glob": "on", + "tool_grep": "on", + "linter": "on", + "playwright": "on", + "context_file": "none", + "sub_agents": "on", + "web_search": "on", + "max_budget": "low", + "task": "tetris", + "cell_id": "tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on", + "runs_per_cell": 3, + "timeout_seconds": 1200, + "max_budget_usd": 2.0, + "run_id": "tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3", + "run_number": 3, + "claude_version": "2.1.92 (Claude Code)", + "started_at": "2026-04-04T20:57:28.505320+00:00", + "wall_time_seconds": 154, + "exit_code": 0, + "completed_at": "2026-04-04T21:00:10.672556+00:00" +} +\ No newline at end of file diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/transcript.jsonl b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=opus_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/transcript.jsonl @@ -0,0 +1,43 @@ +{"type": "harness", "subtype": "config", "model": "opus", "effort": "high", "tools": ["Bash", "Read", "Write", "Edit", "Glob", "Grep", "Agent", "WebSearch", "WebFetch"], "max_budget_usd": 2.0, "timeout_seconds": 1200, "task": "tetris", "language": "typescript", "prompt_style": "simple"} +{"type": "user", "subtype": "prompt", "message": {"role": "user", "content": "Build a playable Tetris game that runs in a browser. It should have all the standard mechanics: piece rotation, line clearing, scoring, and increasing speed. Use keyboard controls for movement and rotation.\n\n\nUse TypeScript."}} +{"type":"system","subtype":"init","cwd":"/tmp/loop-bench-amzysv_v","session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","tools":["Bash","Edit","Read"],"mcp_servers":[],"model":"claude-opus-4-6","permissionMode":"dontAsk","slash_commands":["update-config","debug","simplify","batch","loop","schedule","claude-api","compact","context","cost","heapdump","init","review","security-review","insights"],"apiKeySource":"apiKeyHelper","claude_code_version":"2.1.92","output_style":"default","agents":["general-purpose","statusline-setup","Explore","Plan"],"skills":["update-config","debug","simplify","batch","loop","schedule","claude-api"],"plugins":[{"name":"claude-hud","path":"/root/.claude/plugins/marketplaces/claude-hud/","source":"claude-hud@claude-hud"},{"name":"rust-analyzer-lsp","path":"/root/.claude/plugins/marketplaces/claude-plugins-official/plugins/rust-analyzer-lsp","source":"rust-analyzer-lsp@claude-plugins-official"}],"uuid":"d50246b2-1399-4d35-b370-c74bc18e4e75","fast_mode_state":"off"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01TQdwvLf9wFBR3t6vpezyyH","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"The user wants me to build a playable Tetris game that runs in a browser using TypeScript. Let me create a complete implementation with all the standard mechanics.\n\nLet me first check what's in the current working directory.","signature":"EpwDClkIDBgCKkBvB14hDDirtVI5WeeU3vcBJ3uuXQsxSs6kRT/8hdV71TnYeuoMiCzAocvAYJCDX1Sds2S2TyODc+nfVxRZtf4LMg9jbGF1ZGUtb3B1cy00LTY4ABIMWT6IqItwKNZ/dS8iGgxjKCsL5G39/cQqIrQiMAfee1oC2RYxddxR0K3Y9yeQ3Bego5WDnLUXL7OMyqEEXpRxgPnQ5aTyzuIkj/d/birwAU4tBoRUeZvtgFJ1KO2EIxfcGMEFNi/NIzthA2S2z2gno6Allzia8+eCDogCf8tju1x1IxuU+a9f70a+rGsaG/H6iBWXTbZwyh/0s/Wuzre0Rfr6O/NHbQ66k/6Gb+KxsW0EDQV7iTdNQDcc9iiZm3q7nVdnEJDPU0kNTFeEIw/xacCs/CiOaDijRUX3QG8pB6A3p1Ud/Av2OmnwSdeUcYbabG7HWRK3TOwntmu+2PdSt288iVrSLd6uxEGDCfZ+eLVMyJukSi/ITx+UhRdVPk8U4JkCRWCdbgRRXGngr9mggJTfVPsNojYAx9TzFURFbxgB"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":4779,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":4779,"ephemeral_1h_input_tokens":0},"output_tokens":38,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"b0fe57da-db61-42fc-821b-c3bef78044f5"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01TQdwvLf9wFBR3t6vpezyyH","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01FmqgdRv78tN7UPZ7ySLM57","name":"Bash","input":{"command":"ls /tmp/loop-bench-amzysv_v","description":"List files in working directory"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":4779,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":4779,"ephemeral_1h_input_tokens":0},"output_tokens":38,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"7fe20979-93ed-4a8f-8223-92844a5ed5d8"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01FmqgdRv78tN7UPZ7ySLM57","type":"tool_result","content":"node_modules\npackage.json\npackage-lock.json","is_error":false}]},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"1e635c66-9630-4aac-a2e5-339df946c661","timestamp":"2026-04-04T20:57:41.738Z","tool_use_result":{"stdout":"node_modules\npackage.json\npackage-lock.json","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_017upQEWVtXYj9gLZ1q4FhRQ","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01CWZQca491beGg4yTQqsD1r","name":"Read","input":{"file_path":"/tmp/loop-bench-amzysv_v/package.json"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":175,"cache_read_input_tokens":4779,"cache_creation":{"ephemeral_5m_input_tokens":175,"ephemeral_1h_input_tokens":0},"output_tokens":60,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"d0d22107-3f14-4d73-9125-876487d97cb6"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01CWZQca491beGg4yTQqsD1r","type":"tool_result","content":"1\t{\n2\t \"name\": \"loop-bench-amzysv_v\",\n3\t \"version\": \"1.0.0\",\n4\t \"description\": \"\",\n5\t \"main\": \"index.js\",\n6\t \"scripts\": {\n7\t \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n8\t },\n9\t \"keywords\": [],\n10\t \"author\": \"\",\n11\t \"license\": \"ISC\",\n12\t \"type\": \"commonjs\",\n13\t \"devDependencies\": {\n14\t \"@eslint/js\": \"^10.0.1\",\n15\t \"@playwright/test\": \"^1.59.1\",\n16\t \"@types/node\": \"^25.5.2\",\n17\t \"eslint\": \"^10.2.0\",\n18\t \"typescript\": \"^6.0.2\"\n19\t }\n20\t}\n21\t"}]},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"27cf3411-0bf9-45ca-aa99-09debd3127e0","timestamp":"2026-04-04T20:57:44.955Z","tool_use_result":{"type":"text","file":{"filePath":"/tmp/loop-bench-amzysv_v/package.json","content":"{\n \"name\": \"loop-bench-amzysv_v\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },\n \"keywords\": [],\n \"author\": \"\",\n \"license\": \"ISC\",\n \"type\": \"commonjs\",\n \"devDependencies\": {\n \"@eslint/js\": \"^10.0.1\",\n \"@playwright/test\": \"^1.59.1\",\n \"@types/node\": \"^25.5.2\",\n \"eslint\": \"^10.2.0\",\n \"typescript\": \"^6.0.2\"\n }\n}\n","numLines":21,"startLine":1,"totalLines":21}}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01V4G3TvdK2r8XoUUD3Ykbh3","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01MNkuzgp4CkvfLYcAb4q4oD","name":"Bash","input":{"command":"ls /tmp/loop-bench-amzysv_v/node_modules/.bin/tsc 2>/dev/null && echo \"tsc exists\" || echo \"tsc not found\"","description":"Check if tsc is available"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":490,"cache_read_input_tokens":4779,"cache_creation":{"ephemeral_5m_input_tokens":490,"ephemeral_1h_input_tokens":0},"output_tokens":48,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"6e78b4b6-1f59-48ab-a841-977ec6d1afce"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01MNkuzgp4CkvfLYcAb4q4oD","type":"tool_result","content":"/tmp/loop-bench-amzysv_v/node_modules/.bin/tsc\ntsc exists","is_error":false}]},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"ab102783-e06d-4641-8c3a-1348d27a5fa7","timestamp":"2026-04-04T20:57:49.635Z","tool_use_result":{"stdout":"/tmp/loop-bench-amzysv_v/node_modules/.bin/tsc\ntsc exists","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01K8teG9RwoYT3koy3uPymnc","type":"message","role":"assistant","content":[{"type":"text","text":"Now let me build the full Tetris game. I'll create a `tsconfig.json`, the TypeScript source, and an HTML file."}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":154,"cache_read_input_tokens":5269,"cache_creation":{"ephemeral_5m_input_tokens":154,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"121d247c-827b-4f26-9896-52a6c5443d0e"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01K8teG9RwoYT3koy3uPymnc","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01EgjyDE7Jdqgn5rMRcmn2dJ","name":"Write","input":{"file_path":"/tmp/loop-bench-amzysv_v/tsconfig.json","content":"{\n \"compilerOptions\": {\n \"target\": \"ES2020\",\n \"module\": \"ES2020\",\n \"strict\": true,\n \"outDir\": \"./dist\",\n \"rootDir\": \"./src\",\n \"sourceMap\": true,\n \"esModuleInterop\": true\n },\n \"include\": [\"src/**/*\"]\n}\n"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":154,"cache_read_input_tokens":5269,"cache_creation":{"ephemeral_5m_input_tokens":154,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"fdaac790-9637-4d14-9549-e5ff2d7b5b0d"} +{"type":"user","message":{"role":"user","content":[{"type":"tool_result","content":"<tool_use_error>Error: No such tool available: Write</tool_use_error>","is_error":true,"tool_use_id":"toolu_01EgjyDE7Jdqgn5rMRcmn2dJ"}]},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"3d77d36e-3516-4493-a51a-65942ae400f6","timestamp":"2026-04-04T20:57:53.847Z","tool_use_result":"Error: No such tool available: Write"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01QDc1AuP1nTYBagLVPHcE7j","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01EAPyn9TJXwHRXRzYivd929","name":"Bash","input":{"command":"mkdir -p /tmp/loop-bench-amzysv_v/src /tmp/loop-bench-amzysv_v/dist","description":"Create src and dist directories"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":246,"cache_read_input_tokens":5423,"cache_creation":{"ephemeral_5m_input_tokens":246,"ephemeral_1h_input_tokens":0},"output_tokens":64,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"cdbac973-327a-4ceb-a125-4ba4793b093e"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01EAPyn9TJXwHRXRzYivd929","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"40f7d85d-ea97-43ef-9132-b45026bc8a07","timestamp":"2026-04-04T20:57:57.240Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":true}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_011e12Ui8LSGR97g2FBWCg7F","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01MogocgvjfuoZcrzJ3WHQu1","name":"Bash","input":{"command":"cat > /tmp/loop-bench-amzysv_v/tsconfig.json << 'TSEOF'\n{\n \"compilerOptions\": {\n \"target\": \"ES2020\",\n \"module\": \"ES2020\",\n \"strict\": true,\n \"outDir\": \"./dist\",\n \"rootDir\": \"./src\",\n \"sourceMap\": true,\n \"esModuleInterop\": true\n },\n \"include\": [\"src/**/*\"]\n}\nTSEOF","description":"Create tsconfig.json"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":369,"cache_read_input_tokens":5423,"cache_creation":{"ephemeral_5m_input_tokens":369,"ephemeral_1h_input_tokens":0},"output_tokens":63,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"59ccc1e3-1c73-4823-ada4-010eb9503773"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01MogocgvjfuoZcrzJ3WHQu1","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"728bcd4d-1e12-4835-9cc2-602a9d9c734e","timestamp":"2026-04-04T20:58:01.666Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01Hcg4rD1Y9SHfx5BT3Q1qdG","type":"message","role":"assistant","content":[{"type":"text","text":"Now let me create the main TypeScript game file:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":332,"cache_read_input_tokens":5669,"cache_creation":{"ephemeral_5m_input_tokens":332,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"42ad606c-663c-42fb-8d12-1bfb4d5962b1"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01Hcg4rD1Y9SHfx5BT3Q1qdG","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_016pfbLKzoLExLiEjRixDcCH","name":"Bash","input":{"command":"cat > /tmp/loop-bench-amzysv_v/src/tetris.ts << 'TSEOF'\n// ── Types ──────────────────────────────────────────────────────────────────\n\ntype Cell = string | null;\ntype Board = Cell[][];\n\ninterface Position {\n x: number;\n y: number;\n}\n\ninterface Piece {\n shape: number[][];\n color: string;\n pos: Position;\n}\n\n// ── Constants ──────────────────────────────────────────────────────────────\n\nconst COLS = 10;\nconst ROWS = 20;\nconst BLOCK_SIZE = 32;\nconst PREVIEW_BLOCK = 20;\n\nconst COLORS: Record<string, string> = {\n I: \"#00f0f0\",\n O: \"#f0f000\",\n T: \"#a000f0\",\n S: \"#00f000\",\n Z: \"#f00000\",\n J: \"#0000f0\",\n L: \"#f0a000\",\n};\n\nconst SHAPES: Record<string, number[][]> = {\n I: [\n [0, 0, 0, 0],\n [1, 1, 1, 1],\n [0, 0, 0, 0],\n [0, 0, 0, 0],\n ],\n O: [\n [1, 1],\n [1, 1],\n ],\n T: [\n [0, 1, 0],\n [1, 1, 1],\n [0, 0, 0],\n ],\n S: [\n [0, 1, 1],\n [1, 1, 0],\n [0, 0, 0],\n ],\n Z: [\n [1, 1, 0],\n [0, 1, 1],\n [0, 0, 0],\n ],\n J: [\n [1, 0, 0],\n [1, 1, 1],\n [0, 0, 0],\n ],\n L: [\n [0, 0, 1],\n [1, 1, 1],\n [0, 0, 0],\n ],\n};\n\nconst PIECE_NAMES = Object.keys(SHAPES);\n\n// SRS wall-kick data (non-I pieces)\nconst WALL_KICKS: Record<string, Position[]> = {\n \"0>1\": [{ x: -1, y: 0 }, { x: -1, y: -1 }, { x: 0, y: 2 }, { x: -1, y: 2 }],\n \"1>0\": [{ x: 1, y: 0 }, { x: 1, y: 1 }, { x: 0, y: -2 }, { x: 1, y: -2 }],\n \"1>2\": [{ x: 1, y: 0 }, { x: 1, y: 1 }, { x: 0, y: -2 }, { x: 1, y: -2 }],\n \"2>1\": [{ x: -1, y: 0 }, { x: -1, y: -1 }, { x: 0, y: 2 }, { x: -1, y: 2 }],\n \"2>3\": [{ x: 1, y: 0 }, { x: 1, y: -1 }, { x: 0, y: 2 }, { x: 1, y: 2 }],\n \"3>2\": [{ x: -1, y: 0 }, { x: -1, y: 1 }, { x: 0, y: -2 }, { x: -1, y: -2 }],\n \"3>0\": [{ x: -1, y: 0 }, { x: -1, y: -1 }, { x: 0, y: 2 }, { x: -1, y: 2 }],\n \"0>3\": [{ x: 1, y: 0 }, { x: 1, y: 1 }, { x: 0, y: -2 }, { x: 1, y: -2 }],\n};\n\n// SRS wall-kick data (I piece)\nconst WALL_KICKS_I: Record<string, Position[]> = {\n \"0>1\": [{ x: -2, y: 0 }, { x: 1, y: 0 }, { x: -2, y: 1 }, { x: 1, y: -2 }],\n \"1>0\": [{ x: 2, y: 0 }, { x: -1, y: 0 }, { x: 2, y: -1 }, { x: -1, y: 2 }],\n \"1>2\": [{ x: -1, y: 0 }, { x: 2, y: 0 }, { x: -1, y: -2 }, { x: 2, y: 1 }],\n \"2>1\": [{ x: 1, y: 0 }, { x: -2, y: 0 }, { x: 1, y: 2 }, { x: -2, y: -1 }],\n \"2>3\": [{ x: 2, y: 0 }, { x: -1, y: 0 }, { x: 2, y: -1 }, { x: -1, y: 2 }],\n \"3>2\": [{ x: -2, y: 0 }, { x: 1, y: 0 }, { x: -2, y: 1 }, { x: 1, y: -2 }],\n \"3>0\": [{ x: 1, y: 0 }, { x: -2, y: 0 }, { x: 1, y: 2 }, { x: -2, y: -1 }],\n \"0>3\": [{ x: -1, y: 0 }, { x: 2, y: 0 }, { x: -1, y: -2 }, { x: 2, y: 1 }],\n};\n\nconst POINTS = [0, 100, 300, 500, 800];\n\n// ── Game State ─────────────────────────────────────────────────────────────\n\nlet board: Board;\nlet current: Piece;\nlet currentType: string;\nlet currentRotation: number;\nlet next: { type: string; shape: number[][]; color: string };\nlet score: number;\nlet lines: number;\nlet level: number;\nlet gameOver: boolean;\nlet paused: boolean;\nlet dropInterval: number;\nlet lastDrop: number;\nlet animationId: number;\nlet lockDelay: number;\nlet lockTimer: number;\nlet softDropping: boolean;\n\n// ── Canvas Setup ───────────────────────────────────────────────────────────\n\nconst canvas = document.getElementById(\"game-canvas\") as HTMLCanvasElement;\nconst ctx = canvas.getContext(\"2d\")!;\ncanvas.width = COLS * BLOCK_SIZE;\ncanvas.height = ROWS * BLOCK_SIZE;\n\nconst previewCanvas = document.getElementById(\"preview-canvas\") as HTMLCanvasElement;\nconst previewCtx = previewCanvas.getContext(\"2d\")!;\npreviewCanvas.width = 6 * PREVIEW_BLOCK;\npreviewCanvas.height = 6 * PREVIEW_BLOCK;\n\nconst scoreEl = document.getElementById(\"score\")!;\nconst linesEl = document.getElementById(\"lines\")!;\nconst levelEl = document.getElementById(\"level\")!;\n\n// ── Helper Functions ───────────────────────────────────────────────────────\n\nfunction createBoard(): Board {\n return Array.from({ length: ROWS }, () => Array(COLS).fill(null));\n}\n\nfunction randomPiece(): { type: string; shape: number[][]; color: string } {\n const type = PIECE_NAMES[Math.floor(Math.random() * PIECE_NAMES.length)];\n return { type, shape: SHAPES[type].map((r) => [...r]), color: COLORS[type] };\n}\n\nfunction rotateCW(matrix: number[][]): number[][] {\n const n = matrix.length;\n const result: number[][] = Array.from({ length: n }, () => Array(n).fill(0));\n for (let r = 0; r < n; r++) {\n for (let c = 0; c < n; c++) {\n result[c][n - 1 - r] = matrix[r][c];\n }\n }\n return result;\n}\n\nfunction rotateCCW(matrix: number[][]): number[][] {\n const n = matrix.length;\n const result: number[][] = Array.from({ length: n }, () => Array(n).fill(0));\n for (let r = 0; r < n; r++) {\n for (let c = 0; c < n; c++) {\n result[n - 1 - c][r] = matrix[r][c];\n }\n }\n return result;\n}\n\nfunction collides(shape: number[][], pos: Position): boolean {\n for (let r = 0; r < shape.length; r++) {\n for (let c = 0; c < shape[r].length; c++) {\n if (shape[r][c]) {\n const nx = pos.x + c;\n const ny = pos.y + r;\n if (nx < 0 || nx >= COLS || ny >= ROWS) return true;\n if (ny >= 0 && board[ny][nx] !== null) return true;\n }\n }\n }\n return false;\n}\n\nfunction lock(): void {\n for (let r = 0; r < current.shape.length; r++) {\n for (let c = 0; c < current.shape[r].length; c++) {\n if (current.shape[r][c]) {\n const ny = current.pos.y + r;\n const nx = current.pos.x + c;\n if (ny < 0) {\n gameOver = true;\n return;\n }\n board[ny][nx] = current.color;\n }\n }\n }\n clearLines();\n spawn();\n}\n\nfunction clearLines(): void {\n let cleared = 0;\n for (let r = ROWS - 1; r >= 0; r--) {\n if (board[r].every((cell) => cell !== null)) {\n board.splice(r, 1);\n board.unshift(Array(COLS).fill(null));\n cleared++;\n r++; // recheck this row\n }\n }\n if (cleared > 0) {\n lines += cleared;\n score += POINTS[cleared] * (level + 1);\n const newLevel = Math.floor(lines / 10);\n if (newLevel > level) {\n level = newLevel;\n dropInterval = Math.max(50, 1000 - level * 80);\n }\n updateHUD();\n }\n}\n\nfunction spawn(): void {\n currentType = next.type;\n currentRotation = 0;\n current = {\n shape: next.shape,\n color: next.color,\n pos: { x: Math.floor((COLS - next.shape[0].length) / 2), y: -1 },\n };\n next = randomPiece();\n\n // If spawn position collides, game over\n if (collides(current.shape, current.pos)) {\n // Try one row up\n current.pos.y = -2;\n if (collides(current.shape, current.pos)) {\n gameOver = true;\n }\n }\n lockTimer = 0;\n}\n\nfunction ghostY(): number {\n let gy = current.pos.y;\n while (!collides(current.shape, { x: current.pos.x, y: gy + 1 })) {\n gy++;\n }\n return gy;\n}\n\nfunction tryRotate(direction: 1 | -1): void {\n const oldShape = current.shape;\n const oldRotation = currentRotation;\n const newShape = direction === 1 ? rotateCW(oldShape) : rotateCCW(oldShape);\n const newRotation = (oldRotation + direction + 4) % 4;\n\n // Try basic rotation first\n if (!collides(newShape, current.pos)) {\n current.shape = newShape;\n currentRotation = newRotation;\n resetLockTimer();\n return;\n }\n\n // Wall kicks\n const kickKey = `${oldRotation}>${newRotation}`;\n const kicks = currentType === \"I\" ? WALL_KICKS_I[kickKey] : WALL_KICKS[kickKey];\n if (kicks) {\n for (const kick of kicks) {\n const testPos = { x: current.pos.x + kick.x, y: current.pos.y - kick.y };\n if (!collides(newShape, testPos)) {\n current.shape = newShape;\n current.pos = testPos;\n currentRotation = newRotation;\n resetLockTimer();\n return;\n }\n }\n }\n}\n\nfunction resetLockTimer(): void {\n if (collides(current.shape, { x: current.pos.x, y: current.pos.y + 1 })) {\n lockTimer = performance.now();\n }\n}\n\n// ── Drawing ────────────────────────────────────────────────────────────────\n\nfunction drawBlock(\n context: CanvasRenderingContext2D,\n x: number,\n y: number,\n color: string,\n size: number,\n alpha: number = 1\n): void {\n context.globalAlpha = alpha;\n context.fillStyle = color;\n context.fillRect(x * size, y * size, size, size);\n\n // Highlight\n context.fillStyle = \"rgba(255,255,255,0.3)\";\n context.fillRect(x * size, y * size, size, 2);\n context.fillRect(x * size, y * size, 2, size);\n\n // Shadow\n context.fillStyle = \"rgba(0,0,0,0.3)\";\n context.fillRect(x * size + size - 2, y * size, 2, size);\n context.fillRect(x * size, y * size + size - 2, size, 2);\n\n // Outline\n context.strokeStyle = \"rgba(0,0,0,0.5)\";\n context.strokeRect(x * size, y * size, size, size);\n context.globalAlpha = 1;\n}\n\nfunction drawBoard(): void {\n // Background\n ctx.fillStyle = \"#111\";\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n\n // Grid lines\n ctx.strokeStyle = \"rgba(255,255,255,0.04)\";\n ctx.lineWidth = 1;\n for (let r = 0; r < ROWS; r++) {\n for (let c = 0; c < COLS; c++) {\n ctx.strokeRect(c * BLOCK_SIZE, r * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);\n }\n }\n\n // Locked cells\n for (let r = 0; r < ROWS; r++) {\n for (let c = 0; c < COLS; c++) {\n if (board[r][c]) {\n drawBlock(ctx, c, r, board[r][c]!, BLOCK_SIZE);\n }\n }\n }\n\n // Ghost piece\n if (!gameOver && !paused) {\n const gy = ghostY();\n for (let r = 0; r < current.shape.length; r++) {\n for (let c = 0; c < current.shape[r].length; c++) {\n if (current.shape[r][c]) {\n const py = gy + r;\n const px = current.pos.x + c;\n if (py >= 0) {\n drawBlock(ctx, px, py, current.color, BLOCK_SIZE, 0.25);\n }\n }\n }\n }\n }\n\n // Current piece\n if (!gameOver && !paused) {\n for (let r = 0; r < current.shape.length; r++) {\n for (let c = 0; c < current.shape[r].length; c++) {\n if (current.shape[r][c]) {\n const py = current.pos.y + r;\n const px = current.pos.x + c;\n if (py >= 0) {\n drawBlock(ctx, px, py, current.color, BLOCK_SIZE);\n }\n }\n }\n }\n }\n\n // Overlay for pause / game over\n if (paused && !gameOver) {\n ctx.fillStyle = \"rgba(0,0,0,0.6)\";\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n ctx.fillStyle = \"#fff\";\n ctx.font = \"bold 28px monospace\";\n ctx.textAlign = \"center\";\n ctx.fillText(\"PAUSED\", canvas.width / 2, canvas.height / 2);\n }\n if (gameOver) {\n ctx.fillStyle = \"rgba(0,0,0,0.7)\";\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n ctx.fillStyle = \"#f44\";\n ctx.font = \"bold 32px monospace\";\n ctx.textAlign = \"center\";\n ctx.fillText(\"GAME OVER\", canvas.width / 2, canvas.height / 2 - 20);\n ctx.fillStyle = \"#fff\";\n ctx.font = \"18px monospace\";\n ctx.fillText(\"Press R to restart\", canvas.width / 2, canvas.height / 2 + 20);\n }\n}\n\nfunction drawPreview(): void {\n previewCtx.fillStyle = \"#1a1a2e\";\n previewCtx.fillRect(0, 0, previewCanvas.width, previewCanvas.height);\n\n const shape = next.shape;\n const offsetX = (6 - shape[0].length) / 2;\n const offsetY = (6 - shape.length) / 2;\n for (let r = 0; r < shape.length; r++) {\n for (let c = 0; c < shape[r].length; c++) {\n if (shape[r][c]) {\n drawBlock(previewCtx, offsetX + c, offsetY + r, next.color, PREVIEW_BLOCK);\n }\n }\n }\n}\n\nfunction updateHUD(): void {\n scoreEl.textContent = score.toString();\n linesEl.textContent = lines.toString();\n levelEl.textContent = level.toString();\n}\n\n// ── Game Loop ──────────────────────────────────────────────────────────────\n\nfunction gameLoop(now: number): void {\n animationId = requestAnimationFrame(gameLoop);\n\n if (gameOver || paused) {\n drawBoard();\n drawPreview();\n return;\n }\n\n // Gravity\n const effectiveInterval = softDropping ? Math.max(30, dropInterval / 15) : dropInterval;\n if (now - lastDrop > effectiveInterval) {\n if (!collides(current.shape, { x: current.pos.x, y: current.pos.y + 1 })) {\n current.pos.y++;\n if (softDropping) score += 1;\n lastDrop = now;\n lockTimer = 0;\n } else {\n // Piece is resting on something — start lock delay\n if (lockTimer === 0) {\n lockTimer = now;\n } else if (now - lockTimer > lockDelay) {\n lock();\n lastDrop = now;\n }\n }\n }\n\n drawBoard();\n drawPreview();\n updateHUD();\n}\n\n// ── Input ──────────────────────────────────────────────────────────────────\n\ndocument.addEventListener(\"keydown\", (e: KeyboardEvent) => {\n if (gameOver) {\n if (e.key === \"r\" || e.key === \"R\") {\n init();\n }\n return;\n }\n\n if (e.key === \"p\" || e.key === \"P\" || e.key === \"Escape\") {\n paused = !paused;\n if (!paused) lastDrop = performance.now();\n return;\n }\n\n if (paused) return;\n\n switch (e.key) {\n case \"ArrowLeft\":\n case \"a\":\n if (!collides(current.shape, { x: current.pos.x - 1, y: current.pos.y })) {\n current.pos.x--;\n resetLockTimer();\n }\n e.preventDefault();\n break;\n case \"ArrowRight\":\n case \"d\":\n if (!collides(current.shape, { x: current.pos.x + 1, y: current.pos.y })) {\n current.pos.x++;\n resetLockTimer();\n }\n e.preventDefault();\n break;\n case \"ArrowDown\":\n case \"s\":\n softDropping = true;\n e.preventDefault();\n break;\n case \"ArrowUp\":\n case \"w\":\n tryRotate(1);\n e.preventDefault();\n break;\n case \"z\":\n case \"Z\":\n tryRotate(-1);\n e.preventDefault();\n break;\n case \" \":\n // Hard drop\n while (!collides(current.shape, { x: current.pos.x, y: current.pos.y + 1 })) {\n current.pos.y++;\n score += 2;\n }\n lock();\n lastDrop = performance.now();\n e.preventDefault();\n break;\n }\n});\n\ndocument.addEventListener(\"keyup\", (e: KeyboardEvent) => {\n if (e.key === \"ArrowDown\" || e.key === \"s\") {\n softDropping = false;\n }\n});\n\n// ── Initialization ─────────────────────────────────────────────────────────\n\nfunction init(): void {\n board = createBoard();\n score = 0;\n lines = 0;\n level = 0;\n gameOver = false;\n paused = false;\n dropInterval = 1000;\n lastDrop = performance.now();\n lockDelay = 500;\n lockTimer = 0;\n softDropping = false;\n\n next = randomPiece();\n spawn();\n updateHUD();\n\n if (animationId) cancelAnimationFrame(animationId);\n animationId = requestAnimationFrame(gameLoop);\n}\n\ninit();\nTSEOF","description":"Create src/tetris.ts with full Tetris game implementation"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":332,"cache_read_input_tokens":5669,"cache_creation":{"ephemeral_5m_input_tokens":332,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"77cace4f-ed94-4947-84b5-a1830067813d"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_016pfbLKzoLExLiEjRixDcCH","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"8db90936-159c-46d5-b087-88880c15d93a","timestamp":"2026-04-04T20:59:05.263Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_011AqcRLwF6WWbkmXbfj2nxr","type":"message","role":"assistant","content":[{"type":"text","text":"Now create the HTML file:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":5901,"cache_read_input_tokens":6001,"cache_creation":{"ephemeral_5m_input_tokens":5901,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"31a56c19-e393-4622-aca3-9b34a752e9d2"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_011AqcRLwF6WWbkmXbfj2nxr","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01CFQcpXmqLAKDuDvoWkqSvD","name":"Bash","input":{"command":"cat > /tmp/loop-bench-amzysv_v/index.html << 'HTMLEOF'\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Tetris</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n\n body {\n background: #0a0a1a;\n color: #e0e0e0;\n font-family: 'Segoe UI', 'Consolas', monospace;\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 100vh;\n overflow: hidden;\n }\n\n .game-wrapper {\n display: flex;\n gap: 24px;\n align-items: flex-start;\n }\n\n #game-canvas {\n border: 3px solid #444;\n border-radius: 4px;\n box-shadow: 0 0 30px rgba(80, 80, 255, 0.15);\n }\n\n .sidebar {\n display: flex;\n flex-direction: column;\n gap: 20px;\n min-width: 160px;\n }\n\n .panel {\n background: #1a1a2e;\n border: 2px solid #333;\n border-radius: 8px;\n padding: 16px;\n }\n\n .panel h3 {\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 2px;\n color: #888;\n margin-bottom: 10px;\n }\n\n .panel .value {\n font-size: 26px;\n font-weight: bold;\n color: #fff;\n font-variant-numeric: tabular-nums;\n }\n\n #preview-canvas {\n display: block;\n margin: 0 auto;\n border-radius: 4px;\n }\n\n .controls {\n font-size: 12px;\n line-height: 1.9;\n color: #777;\n }\n\n .controls kbd {\n background: #2a2a3e;\n border: 1px solid #444;\n border-radius: 3px;\n padding: 1px 6px;\n font-size: 11px;\n color: #ccc;\n }\n </style>\n</head>\n<body>\n <div class=\"game-wrapper\">\n <canvas id=\"game-canvas\"></canvas>\n <div class=\"sidebar\">\n <div class=\"panel\">\n <h3>Next</h3>\n <canvas id=\"preview-canvas\"></canvas>\n </div>\n <div class=\"panel\">\n <h3>Score</h3>\n <div class=\"value\" id=\"score\">0</div>\n </div>\n <div class=\"panel\">\n <h3>Lines</h3>\n <div class=\"value\" id=\"lines\">0</div>\n </div>\n <div class=\"panel\">\n <h3>Level</h3>\n <div class=\"value\" id=\"level\">0</div>\n </div>\n <div class=\"panel controls\">\n <h3>Controls</h3>\n <kbd>&larr;</kbd> <kbd>&rarr;</kbd> Move<br/>\n <kbd>&darr;</kbd> Soft drop<br/>\n <kbd>&uarr;</kbd> Rotate CW<br/>\n <kbd>Z</kbd> Rotate CCW<br/>\n <kbd>Space</kbd> Hard drop<br/>\n <kbd>P</kbd> / <kbd>Esc</kbd> Pause<br/>\n <kbd>R</kbd> Restart\n </div>\n </div>\n </div>\n <script type=\"module\" src=\"dist/tetris.js\"></script>\n</body>\n</html>\nHTMLEOF","description":"Create index.html with game layout and styles"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":5901,"cache_read_input_tokens":6001,"cache_creation":{"ephemeral_5m_input_tokens":5901,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"c03c2eb4-27a4-4f60-859f-4b942b93bf60"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01CFQcpXmqLAKDuDvoWkqSvD","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"b8b57618-59ec-4699-8ee2-7e30da97b11d","timestamp":"2026-04-04T20:59:20.344Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01W8YiV8HPMQEz9MJrPrvq24","type":"message","role":"assistant","content":[{"type":"text","text":"Now compile the TypeScript:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":1123,"cache_read_input_tokens":11902,"cache_creation":{"ephemeral_5m_input_tokens":1123,"ephemeral_1h_input_tokens":0},"output_tokens":2,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"ebc7af6e-9c51-4f32-9f65-88ee932665bc"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01W8YiV8HPMQEz9MJrPrvq24","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_019kCTH5undBtuc6N4szU5mu","name":"Bash","input":{"command":"npx tsc 2>&1","description":"Compile TypeScript"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":1123,"cache_read_input_tokens":11902,"cache_creation":{"ephemeral_5m_input_tokens":1123,"ephemeral_1h_input_tokens":0},"output_tokens":2,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"b4ef16e0-8778-4a2f-9753-ca1431a32ac1"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_019kCTH5undBtuc6N4szU5mu","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"7219ca28-8929-4c8d-8e59-a3e3fc9fcd1d","timestamp":"2026-04-04T20:59:25.292Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_017pLWSQzQ8E26J521kjrst9","type":"message","role":"assistant","content":[{"type":"text","text":"Clean compile. Let me verify the output exists and do a quick sanity check:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":104,"cache_read_input_tokens":13025,"cache_creation":{"ephemeral_5m_input_tokens":104,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"bebcc75e-6b23-42a6-83ba-c43cfea388b6"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_017pLWSQzQ8E26J521kjrst9","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01Wcyw2dh7M3F9bWCta2si8k","name":"Bash","input":{"command":"ls -la /tmp/loop-bench-amzysv_v/dist/","description":"Verify compiled output exists"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":104,"cache_read_input_tokens":13025,"cache_creation":{"ephemeral_5m_input_tokens":104,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"74d1e6a0-a257-4915-a250-3d90eb8526fe"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01Wcyw2dh7M3F9bWCta2si8k","type":"tool_result","content":"total 48\ndrwxr-xr-x 2 root root 4096 Apr 4 22:59 .\ndrwx------ 5 root root 4096 Apr 4 22:59 ..\n-rw-r--r-- 1 root root 16019 Apr 4 22:59 tetris.js\n-rw-r--r-- 1 root root 20695 Apr 4 22:59 tetris.js.map","is_error":false}]},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"beb012e2-1606-4e94-93e2-4080ac6665a1","timestamp":"2026-04-04T20:59:28.684Z","tool_use_result":{"stdout":"total 48\ndrwxr-xr-x 2 root root 4096 Apr 4 22:59 .\ndrwx------ 5 root root 4096 Apr 4 22:59 ..\n-rw-r--r-- 1 root root 16019 Apr 4 22:59 tetris.js\n-rw-r--r-- 1 root root 20695 Apr 4 22:59 tetris.js.map","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01K9xeVXvDXanuPVZvSUYDKZ","type":"message","role":"assistant","content":[{"type":"text","text":"Let me verify the source looks correct:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":238,"cache_read_input_tokens":13129,"cache_creation":{"ephemeral_5m_input_tokens":238,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"69056358-b5fd-43c5-a070-c654125489ab"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01K9xeVXvDXanuPVZvSUYDKZ","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01WfanWbTqxkc6NB77DkofYB","name":"Read","input":{"file_path":"/tmp/loop-bench-amzysv_v/src/tetris.ts"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":238,"cache_read_input_tokens":13129,"cache_creation":{"ephemeral_5m_input_tokens":238,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"012bf703-40c6-4f3c-9ab0-ea87e33cb38b"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01WfanWbTqxkc6NB77DkofYB","type":"tool_result","content":"1\t// ── Types ──────────────────────────────────────────────────────────────────\n2\t\n3\ttype Cell = string | null;\n4\ttype Board = Cell[][];\n5\t\n6\tinterface Position {\n7\t x: number;\n8\t y: number;\n9\t}\n10\t\n11\tinterface Piece {\n12\t shape: number[][];\n13\t color: string;\n14\t pos: Position;\n15\t}\n16\t\n17\t// ── Constants ──────────────────────────────────────────────────────────────\n18\t\n19\tconst COLS = 10;\n20\tconst ROWS = 20;\n21\tconst BLOCK_SIZE = 32;\n22\tconst PREVIEW_BLOCK = 20;\n23\t\n24\tconst COLORS: Record<string, string> = {\n25\t I: \"#00f0f0\",\n26\t O: \"#f0f000\",\n27\t T: \"#a000f0\",\n28\t S: \"#00f000\",\n29\t Z: \"#f00000\",\n30\t J: \"#0000f0\",\n31\t L: \"#f0a000\",\n32\t};\n33\t\n34\tconst SHAPES: Record<string, number[][]> = {\n35\t I: [\n36\t [0, 0, 0, 0],\n37\t [1, 1, 1, 1],\n38\t [0, 0, 0, 0],\n39\t [0, 0, 0, 0],\n40\t ],\n41\t O: [\n42\t [1, 1],\n43\t [1, 1],\n44\t ],\n45\t T: [\n46\t [0, 1, 0],\n47\t [1, 1, 1],\n48\t [0, 0, 0],\n49\t ],\n50\t S: [\n51\t [0, 1, 1],\n52\t [1, 1, 0],\n53\t [0, 0, 0],\n54\t ],\n55\t Z: [\n56\t [1, 1, 0],\n57\t [0, 1, 1],\n58\t [0, 0, 0],\n59\t ],\n60\t J: [\n61\t [1, 0, 0],\n62\t [1, 1, 1],\n63\t [0, 0, 0],\n64\t ],\n65\t L: [\n66\t [0, 0, 1],\n67\t [1, 1, 1],\n68\t [0, 0, 0],\n69\t ],\n70\t};\n71\t\n72\tconst PIECE_NAMES = Object.keys(SHAPES);\n73\t\n74\t// SRS wall-kick data (non-I pieces)\n75\tconst WALL_KICKS: Record<string, Position[]> = {\n76\t \"0>1\": [{ x: -1, y: 0 }, { x: -1, y: -1 }, { x: 0, y: 2 }, { x: -1, y: 2 }],\n77\t \"1>0\": [{ x: 1, y: 0 }, { x: 1, y: 1 }, { x: 0, y: -2 }, { x: 1, y: -2 }],\n78\t \"1>2\": [{ x: 1, y: 0 }, { x: 1, y: 1 }, { x: 0, y: -2 }, { x: 1, y: -2 }],\n79\t \"2>1\": [{ x: -1, y: 0 }, { x: -1, y: -1 }, { x: 0, y: 2 }, { x: -1, y: 2 }],\n80\t \"2>3\": [{ x: 1, y: 0 }, { x: 1, y: -1 }, { x: 0, y: 2 }, { x: 1, y: 2 }],\n81\t \"3>2\": [{ x: -1, y: 0 }, { x: -1, y: 1 }, { x: 0, y: -2 }, { x: -1, y: -2 }],\n82\t \"3>0\": [{ x: -1, y: 0 }, { x: -1, y: -1 }, { x: 0, y: 2 }, { x: -1, y: 2 }],\n83\t \"0>3\": [{ x: 1, y: 0 }, { x: 1, y: 1 }, { x: 0, y: -2 }, { x: 1, y: -2 }],\n84\t};\n85\t\n86\t// SRS wall-kick data (I piece)\n87\tconst WALL_KICKS_I: Record<string, Position[]> = {\n88\t \"0>1\": [{ x: -2, y: 0 }, { x: 1, y: 0 }, { x: -2, y: 1 }, { x: 1, y: -2 }],\n89\t \"1>0\": [{ x: 2, y: 0 }, { x: -1, y: 0 }, { x: 2, y: -1 }, { x: -1, y: 2 }],\n90\t \"1>2\": [{ x: -1, y: 0 }, { x: 2, y: 0 }, { x: -1, y: -2 }, { x: 2, y: 1 }],\n91\t \"2>1\": [{ x: 1, y: 0 }, { x: -2, y: 0 }, { x: 1, y: 2 }, { x: -2, y: -1 }],\n92\t \"2>3\": [{ x: 2, y: 0 }, { x: -1, y: 0 }, { x: 2, y: -1 }, { x: -1, y: 2 }],\n93\t \"3>2\": [{ x: -2, y: 0 }, { x: 1, y: 0 }, { x: -2, y: 1 }, { x: 1, y: -2 }],\n94\t \"3>0\": [{ x: 1, y: 0 }, { x: -2, y: 0 }, { x: 1, y: 2 }, { x: -2, y: -1 }],\n95\t \"0>3\": [{ x: -1, y: 0 }, { x: 2, y: 0 }, { x: -1, y: -2 }, { x: 2, y: 1 }],\n96\t};\n97\t\n98\tconst POINTS = [0, 100, 300, 500, 800];\n99\t\n100\t// ── Game State ─────────────────────────────────────────────────────────────\n101\t\n102\tlet board: Board;\n103\tlet current: Piece;\n104\tlet currentType: string;\n105\tlet currentRotation: number;\n106\tlet next: { type: string; shape: number[][]; color: string };\n107\tlet score: number;\n108\tlet lines: number;\n109\tlet level: number;\n110\tlet gameOver: boolean;\n111\tlet paused: boolean;\n112\tlet dropInterval: number;\n113\tlet lastDrop: number;\n114\tlet animationId: number;\n115\tlet lockDelay: number;\n116\tlet lockTimer: number;\n117\tlet softDropping: boolean;\n118\t\n119\t// ── Canvas Setup ───────────────────────────────────────────────────────────\n120\t\n121\tconst canvas = document.getElementById(\"game-canvas\") as HTMLCanvasElement;\n122\tconst ctx = canvas.getContext(\"2d\")!;\n123\tcanvas.width = COLS * BLOCK_SIZE;\n124\tcanvas.height = ROWS * BLOCK_SIZE;\n125\t\n126\tconst previewCanvas = document.getElementById(\"preview-canvas\") as HTMLCanvasElement;\n127\tconst previewCtx = previewCanvas.getContext(\"2d\")!;\n128\tpreviewCanvas.width = 6 * PREVIEW_BLOCK;\n129\tpreviewCanvas.height = 6 * PREVIEW_BLOCK;\n130\t\n131\tconst scoreEl = document.getElementById(\"score\")!;\n132\tconst linesEl = document.getElementById(\"lines\")!;\n133\tconst levelEl = document.getElementById(\"level\")!;\n134\t\n135\t// ── Helper Functions ───────────────────────────────────────────────────────\n136\t\n137\tfunction createBoard(): Board {\n138\t return Array.from({ length: ROWS }, () => Array(COLS).fill(null));\n139\t}\n140\t\n141\tfunction randomPiece(): { type: string; shape: number[][]; color: string } {\n142\t const type = PIECE_NAMES[Math.floor(Math.random() * PIECE_NAMES.length)];\n143\t return { type, shape: SHAPES[type].map((r) => [...r]), color: COLORS[type] };\n144\t}\n145\t\n146\tfunction rotateCW(matrix: number[][]): number[][] {\n147\t const n = matrix.length;\n148\t const result: number[][] = Array.from({ length: n }, () => Array(n).fill(0));\n149\t for (let r = 0; r < n; r++) {\n150\t for (let c = 0; c < n; c++) {\n151\t result[c][n - 1 - r] = matrix[r][c];\n152\t }\n153\t }\n154\t return result;\n155\t}\n156\t\n157\tfunction rotateCCW(matrix: number[][]): number[][] {\n158\t const n = matrix.length;\n159\t const result: number[][] = Array.from({ length: n }, () => Array(n).fill(0));\n160\t for (let r = 0; r < n; r++) {\n161\t for (let c = 0; c < n; c++) {\n162\t result[n - 1 - c][r] = matrix[r][c];\n163\t }\n164\t }\n165\t return result;\n166\t}\n167\t\n168\tfunction collides(shape: number[][], pos: Position): boolean {\n169\t for (let r = 0; r < shape.length; r++) {\n170\t for (let c = 0; c < shape[r].length; c++) {\n171\t if (shape[r][c]) {\n172\t const nx = pos.x + c;\n173\t const ny = pos.y + r;\n174\t if (nx < 0 || nx >= COLS || ny >= ROWS) return true;\n175\t if (ny >= 0 && board[ny][nx] !== null) return true;\n176\t }\n177\t }\n178\t }\n179\t return false;\n180\t}\n181\t\n182\tfunction lock(): void {\n183\t for (let r = 0; r < current.shape.length; r++) {\n184\t for (let c = 0; c < current.shape[r].length; c++) {\n185\t if (current.shape[r][c]) {\n186\t const ny = current.pos.y + r;\n187\t const nx = current.pos.x + c;\n188\t if (ny < 0) {\n189\t gameOver = true;\n190\t return;\n191\t }\n192\t board[ny][nx] = current.color;\n193\t }\n194\t }\n195\t }\n196\t clearLines();\n197\t spawn();\n198\t}\n199\t\n200\tfunction clearLines(): void {\n201\t let cleared = 0;\n202\t for (let r = ROWS - 1; r >= 0; r--) {\n203\t if (board[r].every((cell) => cell !== null)) {\n204\t board.splice(r, 1);\n205\t board.unshift(Array(COLS).fill(null));\n206\t cleared++;\n207\t r++; // recheck this row\n208\t }\n209\t }\n210\t if (cleared > 0) {\n211\t lines += cleared;\n212\t score += POINTS[cleared] * (level + 1);\n213\t const newLevel = Math.floor(lines / 10);\n214\t if (newLevel > level) {\n215\t level = newLevel;\n216\t dropInterval = Math.max(50, 1000 - level * 80);\n217\t }\n218\t updateHUD();\n219\t }\n220\t}\n221\t\n222\tfunction spawn(): void {\n223\t currentType = next.type;\n224\t currentRotation = 0;\n225\t current = {\n226\t shape: next.shape,\n227\t color: next.color,\n228\t pos: { x: Math.floor((COLS - next.shape[0].length) / 2), y: -1 },\n229\t };\n230\t next = randomPiece();\n231\t\n232\t // If spawn position collides, game over\n233\t if (collides(current.shape, current.pos)) {\n234\t // Try one row up\n235\t current.pos.y = -2;\n236\t if (collides(current.shape, current.pos)) {\n237\t gameOver = true;\n238\t }\n239\t }\n240\t lockTimer = 0;\n241\t}\n242\t\n243\tfunction ghostY(): number {\n244\t let gy = current.pos.y;\n245\t while (!collides(current.shape, { x: current.pos.x, y: gy + 1 })) {\n246\t gy++;\n247\t }\n248\t return gy;\n249\t}\n250\t\n251\tfunction tryRotate(direction: 1 | -1): void {\n252\t const oldShape = current.shape;\n253\t const oldRotation = currentRotation;\n254\t const newShape = direction === 1 ? rotateCW(oldShape) : rotateCCW(oldShape);\n255\t const newRotation = (oldRotation + direction + 4) % 4;\n256\t\n257\t // Try basic rotation first\n258\t if (!collides(newShape, current.pos)) {\n259\t current.shape = newShape;\n260\t currentRotation = newRotation;\n261\t resetLockTimer();\n262\t return;\n263\t }\n264\t\n265\t // Wall kicks\n266\t const kickKey = `${oldRotation}>${newRotation}`;\n267\t const kicks = currentType === \"I\" ? WALL_KICKS_I[kickKey] : WALL_KICKS[kickKey];\n268\t if (kicks) {\n269\t for (const kick of kicks) {\n270\t const testPos = { x: current.pos.x + kick.x, y: current.pos.y - kick.y };\n271\t if (!collides(newShape, testPos)) {\n272\t current.shape = newShape;\n273\t current.pos = testPos;\n274\t currentRotation = newRotation;\n275\t resetLockTimer();\n276\t return;\n277\t }\n278\t }\n279\t }\n280\t}\n281\t\n282\tfunction resetLockTimer(): void {\n283\t if (collides(current.shape, { x: current.pos.x, y: current.pos.y + 1 })) {\n284\t lockTimer = performance.now();\n285\t }\n286\t}\n287\t\n288\t// ── Drawing ────────────────────────────────────────────────────────────────\n289\t\n290\tfunction drawBlock(\n291\t context: CanvasRenderingContext2D,\n292\t x: number,\n293\t y: number,\n294\t color: string,\n295\t size: number,\n296\t alpha: number = 1\n297\t): void {\n298\t context.globalAlpha = alpha;\n299\t context.fillStyle = color;\n300\t context.fillRect(x * size, y * size, size, size);\n301\t\n302\t // Highlight\n303\t context.fillStyle = \"rgba(255,255,255,0.3)\";\n304\t context.fillRect(x * size, y * size, size, 2);\n305\t context.fillRect(x * size, y * size, 2, size);\n306\t\n307\t // Shadow\n308\t context.fillStyle = \"rgba(0,0,0,0.3)\";\n309\t context.fillRect(x * size + size - 2, y * size, 2, size);\n310\t context.fillRect(x * size, y * size + size - 2, size, 2);\n311\t\n312\t // Outline\n313\t context.strokeStyle = \"rgba(0,0,0,0.5)\";\n314\t context.strokeRect(x * size, y * size, size, size);\n315\t context.globalAlpha = 1;\n316\t}\n317\t\n318\tfunction drawBoard(): void {\n319\t // Background\n320\t ctx.fillStyle = \"#111\";\n321\t ctx.fillRect(0, 0, canvas.width, canvas.height);\n322\t\n323\t // Grid lines\n324\t ctx.strokeStyle = \"rgba(255,255,255,0.04)\";\n325\t ctx.lineWidth = 1;\n326\t for (let r = 0; r < ROWS; r++) {\n327\t for (let c = 0; c < COLS; c++) {\n328\t ctx.strokeRect(c * BLOCK_SIZE, r * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);\n329\t }\n330\t }\n331\t\n332\t // Locked cells\n333\t for (let r = 0; r < ROWS; r++) {\n334\t for (let c = 0; c < COLS; c++) {\n335\t if (board[r][c]) {\n336\t drawBlock(ctx, c, r, board[r][c]!, BLOCK_SIZE);\n337\t }\n338\t }\n339\t }\n340\t\n341\t // Ghost piece\n342\t if (!gameOver && !paused) {\n343\t const gy = ghostY();\n344\t for (let r = 0; r < current.shape.length; r++) {\n345\t for (let c = 0; c < current.shape[r].length; c++) {\n346\t if (current.shape[r][c]) {\n347\t const py = gy + r;\n348\t const px = current.pos.x + c;\n349\t if (py >= 0) {\n350\t drawBlock(ctx, px, py, current.color, BLOCK_SIZE, 0.25);\n351\t }\n352\t }\n353\t }\n354\t }\n355\t }\n356\t\n357\t // Current piece\n358\t if (!gameOver && !paused) {\n359\t for (let r = 0; r < current.shape.length; r++) {\n360\t for (let c = 0; c < current.shape[r].length; c++) {\n361\t if (current.shape[r][c]) {\n362\t const py = current.pos.y + r;\n363\t const px = current.pos.x + c;\n364\t if (py >= 0) {\n365\t drawBlock(ctx, px, py, current.color, BLOCK_SIZE);\n366\t }\n367\t }\n368\t }\n369\t }\n370\t }\n371\t\n372\t // Overlay for pause / game over\n373\t if (paused && !gameOver) {\n374\t ctx.fillStyle = \"rgba(0,0,0,0.6)\";\n375\t ctx.fillRect(0, 0, canvas.width, canvas.height);\n376\t ctx.fillStyle = \"#fff\";\n377\t ctx.font = \"bold 28px monospace\";\n378\t ctx.textAlign = \"center\";\n379\t ctx.fillText(\"PAUSED\", canvas.width / 2, canvas.height / 2);\n380\t }\n381\t if (gameOver) {\n382\t ctx.fillStyle = \"rgba(0,0,0,0.7)\";\n383\t ctx.fillRect(0, 0, canvas.width, canvas.height);\n384\t ctx.fillStyle = \"#f44\";\n385\t ctx.font = \"bold 32px monospace\";\n386\t ctx.textAlign = \"center\";\n387\t ctx.fillText(\"GAME OVER\", canvas.width / 2, canvas.height / 2 - 20);\n388\t ctx.fillStyle = \"#fff\";\n389\t ctx.font = \"18px monospace\";\n390\t ctx.fillText(\"Press R to restart\", canvas.width / 2, canvas.height / 2 + 20);\n391\t }\n392\t}\n393\t\n394\tfunction drawPreview(): void {\n395\t previewCtx.fillStyle = \"#1a1a2e\";\n396\t previewCtx.fillRect(0, 0, previewCanvas.width, previewCanvas.height);\n397\t\n398\t const shape = next.shape;\n399\t const offsetX = (6 - shape[0].length) / 2;\n400\t const offsetY = (6 - shape.length) / 2;\n401\t for (let r = 0; r < shape.length; r++) {\n402\t for (let c = 0; c < shape[r].length; c++) {\n403\t if (shape[r][c]) {\n404\t drawBlock(previewCtx, offsetX + c, offsetY + r, next.color, PREVIEW_BLOCK);\n405\t }\n406\t }\n407\t }\n408\t}\n409\t\n410\tfunction updateHUD(): void {\n411\t scoreEl.textContent = score.toString();\n412\t linesEl.textContent = lines.toString();\n413\t levelEl.textContent = level.toString();\n414\t}\n415\t\n416\t// ── Game Loop ──────────────────────────────────────────────────────────────\n417\t\n418\tfunction gameLoop(now: number): void {\n419\t animationId = requestAnimationFrame(gameLoop);\n420\t\n421\t if (gameOver || paused) {\n422\t drawBoard();\n423\t drawPreview();\n424\t return;\n425\t }\n426\t\n427\t // Gravity\n428\t const effectiveInterval = softDropping ? Math.max(30, dropInterval / 15) : dropInterval;\n429\t if (now - lastDrop > effectiveInterval) {\n430\t if (!collides(current.shape, { x: current.pos.x, y: current.pos.y + 1 })) {\n431\t current.pos.y++;\n432\t if (softDropping) score += 1;\n433\t lastDrop = now;\n434\t lockTimer = 0;\n435\t } else {\n436\t // Piece is resting on something — start lock delay\n437\t if (lockTimer === 0) {\n438\t lockTimer = now;\n439\t } else if (now - lockTimer > lockDelay) {\n440\t lock();\n441\t lastDrop = now;\n442\t }\n443\t }\n444\t }\n445\t\n446\t drawBoard();\n447\t drawPreview();\n448\t updateHUD();\n449\t}\n450\t\n451\t// ── Input ──────────────────────────────────────────────────────────────────\n452\t\n453\tdocument.addEventListener(\"keydown\", (e: KeyboardEvent) => {\n454\t if (gameOver) {\n455\t if (e.key === \"r\" || e.key === \"R\") {\n456\t init();\n457\t }\n458\t return;\n459\t }\n460\t\n461\t if (e.key === \"p\" || e.key === \"P\" || e.key === \"Escape\") {\n462\t paused = !paused;\n463\t if (!paused) lastDrop = performance.now();\n464\t return;\n465\t }\n466\t\n467\t if (paused) return;\n468\t\n469\t switch (e.key) {\n470\t case \"ArrowLeft\":\n471\t case \"a\":\n472\t if (!collides(current.shape, { x: current.pos.x - 1, y: current.pos.y })) {\n473\t current.pos.x--;\n474\t resetLockTimer();\n475\t }\n476\t e.preventDefault();\n477\t break;\n478\t case \"ArrowRight\":\n479\t case \"d\":\n480\t if (!collides(current.shape, { x: current.pos.x + 1, y: current.pos.y })) {\n481\t current.pos.x++;\n482\t resetLockTimer();\n483\t }\n484\t e.preventDefault();\n485\t break;\n486\t case \"ArrowDown\":\n487\t case \"s\":\n488\t softDropping = true;\n489\t e.preventDefault();\n490\t break;\n491\t case \"ArrowUp\":\n492\t case \"w\":\n493\t tryRotate(1);\n494\t e.preventDefault();\n495\t break;\n496\t case \"z\":\n497\t case \"Z\":\n498\t tryRotate(-1);\n499\t e.preventDefault();\n500\t break;\n501\t case \" \":\n502\t // Hard drop\n503\t while (!collides(current.shape, { x: current.pos.x, y: current.pos.y + 1 })) {\n504\t current.pos.y++;\n505\t score += 2;\n506\t }\n507\t lock();\n508\t lastDrop = performance.now();\n509\t e.preventDefault();\n510\t break;\n511\t }\n512\t});\n513\t\n514\tdocument.addEventListener(\"keyup\", (e: KeyboardEvent) => {\n515\t if (e.key === \"ArrowDown\" || e.key === \"s\") {\n516\t softDropping = false;\n517\t }\n518\t});\n519\t\n520\t// ── Initialization ─────────────────────────────────────────────────────────\n521\t\n522\tfunction init(): void {\n523\t board = createBoard();\n524\t score = 0;\n525\t lines = 0;\n526\t level = 0;\n527\t gameOver = false;\n528\t paused = false;\n529\t dropInterval = 1000;\n530\t lastDrop = performance.now();\n531\t lockDelay = 500;\n532\t lockTimer = 0;\n533\t softDropping = false;\n534\t\n535\t next = randomPiece();\n536\t spawn();\n537\t updateHUD();\n538\t\n539\t if (animationId) cancelAnimationFrame(animationId);\n540\t animationId = requestAnimationFrame(gameLoop);\n541\t}\n542\t\n543\tinit();\n544\t"}]},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"3b193d04-0e75-4799-a1c8-5e9bbcfebf07","timestamp":"2026-04-04T20:59:34.263Z","tool_use_result":{"type":"text","file":{"filePath":"/tmp/loop-bench-amzysv_v/src/tetris.ts","content":"// ── Types ──────────────────────────────────────────────────────────────────\n\ntype Cell = string | null;\ntype Board = Cell[][];\n\ninterface Position {\n x: number;\n y: number;\n}\n\ninterface Piece {\n shape: number[][];\n color: string;\n pos: Position;\n}\n\n// ── Constants ──────────────────────────────────────────────────────────────\n\nconst COLS = 10;\nconst ROWS = 20;\nconst BLOCK_SIZE = 32;\nconst PREVIEW_BLOCK = 20;\n\nconst COLORS: Record<string, string> = {\n I: \"#00f0f0\",\n O: \"#f0f000\",\n T: \"#a000f0\",\n S: \"#00f000\",\n Z: \"#f00000\",\n J: \"#0000f0\",\n L: \"#f0a000\",\n};\n\nconst SHAPES: Record<string, number[][]> = {\n I: [\n [0, 0, 0, 0],\n [1, 1, 1, 1],\n [0, 0, 0, 0],\n [0, 0, 0, 0],\n ],\n O: [\n [1, 1],\n [1, 1],\n ],\n T: [\n [0, 1, 0],\n [1, 1, 1],\n [0, 0, 0],\n ],\n S: [\n [0, 1, 1],\n [1, 1, 0],\n [0, 0, 0],\n ],\n Z: [\n [1, 1, 0],\n [0, 1, 1],\n [0, 0, 0],\n ],\n J: [\n [1, 0, 0],\n [1, 1, 1],\n [0, 0, 0],\n ],\n L: [\n [0, 0, 1],\n [1, 1, 1],\n [0, 0, 0],\n ],\n};\n\nconst PIECE_NAMES = Object.keys(SHAPES);\n\n// SRS wall-kick data (non-I pieces)\nconst WALL_KICKS: Record<string, Position[]> = {\n \"0>1\": [{ x: -1, y: 0 }, { x: -1, y: -1 }, { x: 0, y: 2 }, { x: -1, y: 2 }],\n \"1>0\": [{ x: 1, y: 0 }, { x: 1, y: 1 }, { x: 0, y: -2 }, { x: 1, y: -2 }],\n \"1>2\": [{ x: 1, y: 0 }, { x: 1, y: 1 }, { x: 0, y: -2 }, { x: 1, y: -2 }],\n \"2>1\": [{ x: -1, y: 0 }, { x: -1, y: -1 }, { x: 0, y: 2 }, { x: -1, y: 2 }],\n \"2>3\": [{ x: 1, y: 0 }, { x: 1, y: -1 }, { x: 0, y: 2 }, { x: 1, y: 2 }],\n \"3>2\": [{ x: -1, y: 0 }, { x: -1, y: 1 }, { x: 0, y: -2 }, { x: -1, y: -2 }],\n \"3>0\": [{ x: -1, y: 0 }, { x: -1, y: -1 }, { x: 0, y: 2 }, { x: -1, y: 2 }],\n \"0>3\": [{ x: 1, y: 0 }, { x: 1, y: 1 }, { x: 0, y: -2 }, { x: 1, y: -2 }],\n};\n\n// SRS wall-kick data (I piece)\nconst WALL_KICKS_I: Record<string, Position[]> = {\n \"0>1\": [{ x: -2, y: 0 }, { x: 1, y: 0 }, { x: -2, y: 1 }, { x: 1, y: -2 }],\n \"1>0\": [{ x: 2, y: 0 }, { x: -1, y: 0 }, { x: 2, y: -1 }, { x: -1, y: 2 }],\n \"1>2\": [{ x: -1, y: 0 }, { x: 2, y: 0 }, { x: -1, y: -2 }, { x: 2, y: 1 }],\n \"2>1\": [{ x: 1, y: 0 }, { x: -2, y: 0 }, { x: 1, y: 2 }, { x: -2, y: -1 }],\n \"2>3\": [{ x: 2, y: 0 }, { x: -1, y: 0 }, { x: 2, y: -1 }, { x: -1, y: 2 }],\n \"3>2\": [{ x: -2, y: 0 }, { x: 1, y: 0 }, { x: -2, y: 1 }, { x: 1, y: -2 }],\n \"3>0\": [{ x: 1, y: 0 }, { x: -2, y: 0 }, { x: 1, y: 2 }, { x: -2, y: -1 }],\n \"0>3\": [{ x: -1, y: 0 }, { x: 2, y: 0 }, { x: -1, y: -2 }, { x: 2, y: 1 }],\n};\n\nconst POINTS = [0, 100, 300, 500, 800];\n\n// ── Game State ─────────────────────────────────────────────────────────────\n\nlet board: Board;\nlet current: Piece;\nlet currentType: string;\nlet currentRotation: number;\nlet next: { type: string; shape: number[][]; color: string };\nlet score: number;\nlet lines: number;\nlet level: number;\nlet gameOver: boolean;\nlet paused: boolean;\nlet dropInterval: number;\nlet lastDrop: number;\nlet animationId: number;\nlet lockDelay: number;\nlet lockTimer: number;\nlet softDropping: boolean;\n\n// ── Canvas Setup ───────────────────────────────────────────────────────────\n\nconst canvas = document.getElementById(\"game-canvas\") as HTMLCanvasElement;\nconst ctx = canvas.getContext(\"2d\")!;\ncanvas.width = COLS * BLOCK_SIZE;\ncanvas.height = ROWS * BLOCK_SIZE;\n\nconst previewCanvas = document.getElementById(\"preview-canvas\") as HTMLCanvasElement;\nconst previewCtx = previewCanvas.getContext(\"2d\")!;\npreviewCanvas.width = 6 * PREVIEW_BLOCK;\npreviewCanvas.height = 6 * PREVIEW_BLOCK;\n\nconst scoreEl = document.getElementById(\"score\")!;\nconst linesEl = document.getElementById(\"lines\")!;\nconst levelEl = document.getElementById(\"level\")!;\n\n// ── Helper Functions ───────────────────────────────────────────────────────\n\nfunction createBoard(): Board {\n return Array.from({ length: ROWS }, () => Array(COLS).fill(null));\n}\n\nfunction randomPiece(): { type: string; shape: number[][]; color: string } {\n const type = PIECE_NAMES[Math.floor(Math.random() * PIECE_NAMES.length)];\n return { type, shape: SHAPES[type].map((r) => [...r]), color: COLORS[type] };\n}\n\nfunction rotateCW(matrix: number[][]): number[][] {\n const n = matrix.length;\n const result: number[][] = Array.from({ length: n }, () => Array(n).fill(0));\n for (let r = 0; r < n; r++) {\n for (let c = 0; c < n; c++) {\n result[c][n - 1 - r] = matrix[r][c];\n }\n }\n return result;\n}\n\nfunction rotateCCW(matrix: number[][]): number[][] {\n const n = matrix.length;\n const result: number[][] = Array.from({ length: n }, () => Array(n).fill(0));\n for (let r = 0; r < n; r++) {\n for (let c = 0; c < n; c++) {\n result[n - 1 - c][r] = matrix[r][c];\n }\n }\n return result;\n}\n\nfunction collides(shape: number[][], pos: Position): boolean {\n for (let r = 0; r < shape.length; r++) {\n for (let c = 0; c < shape[r].length; c++) {\n if (shape[r][c]) {\n const nx = pos.x + c;\n const ny = pos.y + r;\n if (nx < 0 || nx >= COLS || ny >= ROWS) return true;\n if (ny >= 0 && board[ny][nx] !== null) return true;\n }\n }\n }\n return false;\n}\n\nfunction lock(): void {\n for (let r = 0; r < current.shape.length; r++) {\n for (let c = 0; c < current.shape[r].length; c++) {\n if (current.shape[r][c]) {\n const ny = current.pos.y + r;\n const nx = current.pos.x + c;\n if (ny < 0) {\n gameOver = true;\n return;\n }\n board[ny][nx] = current.color;\n }\n }\n }\n clearLines();\n spawn();\n}\n\nfunction clearLines(): void {\n let cleared = 0;\n for (let r = ROWS - 1; r >= 0; r--) {\n if (board[r].every((cell) => cell !== null)) {\n board.splice(r, 1);\n board.unshift(Array(COLS).fill(null));\n cleared++;\n r++; // recheck this row\n }\n }\n if (cleared > 0) {\n lines += cleared;\n score += POINTS[cleared] * (level + 1);\n const newLevel = Math.floor(lines / 10);\n if (newLevel > level) {\n level = newLevel;\n dropInterval = Math.max(50, 1000 - level * 80);\n }\n updateHUD();\n }\n}\n\nfunction spawn(): void {\n currentType = next.type;\n currentRotation = 0;\n current = {\n shape: next.shape,\n color: next.color,\n pos: { x: Math.floor((COLS - next.shape[0].length) / 2), y: -1 },\n };\n next = randomPiece();\n\n // If spawn position collides, game over\n if (collides(current.shape, current.pos)) {\n // Try one row up\n current.pos.y = -2;\n if (collides(current.shape, current.pos)) {\n gameOver = true;\n }\n }\n lockTimer = 0;\n}\n\nfunction ghostY(): number {\n let gy = current.pos.y;\n while (!collides(current.shape, { x: current.pos.x, y: gy + 1 })) {\n gy++;\n }\n return gy;\n}\n\nfunction tryRotate(direction: 1 | -1): void {\n const oldShape = current.shape;\n const oldRotation = currentRotation;\n const newShape = direction === 1 ? rotateCW(oldShape) : rotateCCW(oldShape);\n const newRotation = (oldRotation + direction + 4) % 4;\n\n // Try basic rotation first\n if (!collides(newShape, current.pos)) {\n current.shape = newShape;\n currentRotation = newRotation;\n resetLockTimer();\n return;\n }\n\n // Wall kicks\n const kickKey = `${oldRotation}>${newRotation}`;\n const kicks = currentType === \"I\" ? WALL_KICKS_I[kickKey] : WALL_KICKS[kickKey];\n if (kicks) {\n for (const kick of kicks) {\n const testPos = { x: current.pos.x + kick.x, y: current.pos.y - kick.y };\n if (!collides(newShape, testPos)) {\n current.shape = newShape;\n current.pos = testPos;\n currentRotation = newRotation;\n resetLockTimer();\n return;\n }\n }\n }\n}\n\nfunction resetLockTimer(): void {\n if (collides(current.shape, { x: current.pos.x, y: current.pos.y + 1 })) {\n lockTimer = performance.now();\n }\n}\n\n// ── Drawing ────────────────────────────────────────────────────────────────\n\nfunction drawBlock(\n context: CanvasRenderingContext2D,\n x: number,\n y: number,\n color: string,\n size: number,\n alpha: number = 1\n): void {\n context.globalAlpha = alpha;\n context.fillStyle = color;\n context.fillRect(x * size, y * size, size, size);\n\n // Highlight\n context.fillStyle = \"rgba(255,255,255,0.3)\";\n context.fillRect(x * size, y * size, size, 2);\n context.fillRect(x * size, y * size, 2, size);\n\n // Shadow\n context.fillStyle = \"rgba(0,0,0,0.3)\";\n context.fillRect(x * size + size - 2, y * size, 2, size);\n context.fillRect(x * size, y * size + size - 2, size, 2);\n\n // Outline\n context.strokeStyle = \"rgba(0,0,0,0.5)\";\n context.strokeRect(x * size, y * size, size, size);\n context.globalAlpha = 1;\n}\n\nfunction drawBoard(): void {\n // Background\n ctx.fillStyle = \"#111\";\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n\n // Grid lines\n ctx.strokeStyle = \"rgba(255,255,255,0.04)\";\n ctx.lineWidth = 1;\n for (let r = 0; r < ROWS; r++) {\n for (let c = 0; c < COLS; c++) {\n ctx.strokeRect(c * BLOCK_SIZE, r * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);\n }\n }\n\n // Locked cells\n for (let r = 0; r < ROWS; r++) {\n for (let c = 0; c < COLS; c++) {\n if (board[r][c]) {\n drawBlock(ctx, c, r, board[r][c]!, BLOCK_SIZE);\n }\n }\n }\n\n // Ghost piece\n if (!gameOver && !paused) {\n const gy = ghostY();\n for (let r = 0; r < current.shape.length; r++) {\n for (let c = 0; c < current.shape[r].length; c++) {\n if (current.shape[r][c]) {\n const py = gy + r;\n const px = current.pos.x + c;\n if (py >= 0) {\n drawBlock(ctx, px, py, current.color, BLOCK_SIZE, 0.25);\n }\n }\n }\n }\n }\n\n // Current piece\n if (!gameOver && !paused) {\n for (let r = 0; r < current.shape.length; r++) {\n for (let c = 0; c < current.shape[r].length; c++) {\n if (current.shape[r][c]) {\n const py = current.pos.y + r;\n const px = current.pos.x + c;\n if (py >= 0) {\n drawBlock(ctx, px, py, current.color, BLOCK_SIZE);\n }\n }\n }\n }\n }\n\n // Overlay for pause / game over\n if (paused && !gameOver) {\n ctx.fillStyle = \"rgba(0,0,0,0.6)\";\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n ctx.fillStyle = \"#fff\";\n ctx.font = \"bold 28px monospace\";\n ctx.textAlign = \"center\";\n ctx.fillText(\"PAUSED\", canvas.width / 2, canvas.height / 2);\n }\n if (gameOver) {\n ctx.fillStyle = \"rgba(0,0,0,0.7)\";\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n ctx.fillStyle = \"#f44\";\n ctx.font = \"bold 32px monospace\";\n ctx.textAlign = \"center\";\n ctx.fillText(\"GAME OVER\", canvas.width / 2, canvas.height / 2 - 20);\n ctx.fillStyle = \"#fff\";\n ctx.font = \"18px monospace\";\n ctx.fillText(\"Press R to restart\", canvas.width / 2, canvas.height / 2 + 20);\n }\n}\n\nfunction drawPreview(): void {\n previewCtx.fillStyle = \"#1a1a2e\";\n previewCtx.fillRect(0, 0, previewCanvas.width, previewCanvas.height);\n\n const shape = next.shape;\n const offsetX = (6 - shape[0].length) / 2;\n const offsetY = (6 - shape.length) / 2;\n for (let r = 0; r < shape.length; r++) {\n for (let c = 0; c < shape[r].length; c++) {\n if (shape[r][c]) {\n drawBlock(previewCtx, offsetX + c, offsetY + r, next.color, PREVIEW_BLOCK);\n }\n }\n }\n}\n\nfunction updateHUD(): void {\n scoreEl.textContent = score.toString();\n linesEl.textContent = lines.toString();\n levelEl.textContent = level.toString();\n}\n\n// ── Game Loop ──────────────────────────────────────────────────────────────\n\nfunction gameLoop(now: number): void {\n animationId = requestAnimationFrame(gameLoop);\n\n if (gameOver || paused) {\n drawBoard();\n drawPreview();\n return;\n }\n\n // Gravity\n const effectiveInterval = softDropping ? Math.max(30, dropInterval / 15) : dropInterval;\n if (now - lastDrop > effectiveInterval) {\n if (!collides(current.shape, { x: current.pos.x, y: current.pos.y + 1 })) {\n current.pos.y++;\n if (softDropping) score += 1;\n lastDrop = now;\n lockTimer = 0;\n } else {\n // Piece is resting on something — start lock delay\n if (lockTimer === 0) {\n lockTimer = now;\n } else if (now - lockTimer > lockDelay) {\n lock();\n lastDrop = now;\n }\n }\n }\n\n drawBoard();\n drawPreview();\n updateHUD();\n}\n\n// ── Input ──────────────────────────────────────────────────────────────────\n\ndocument.addEventListener(\"keydown\", (e: KeyboardEvent) => {\n if (gameOver) {\n if (e.key === \"r\" || e.key === \"R\") {\n init();\n }\n return;\n }\n\n if (e.key === \"p\" || e.key === \"P\" || e.key === \"Escape\") {\n paused = !paused;\n if (!paused) lastDrop = performance.now();\n return;\n }\n\n if (paused) return;\n\n switch (e.key) {\n case \"ArrowLeft\":\n case \"a\":\n if (!collides(current.shape, { x: current.pos.x - 1, y: current.pos.y })) {\n current.pos.x--;\n resetLockTimer();\n }\n e.preventDefault();\n break;\n case \"ArrowRight\":\n case \"d\":\n if (!collides(current.shape, { x: current.pos.x + 1, y: current.pos.y })) {\n current.pos.x++;\n resetLockTimer();\n }\n e.preventDefault();\n break;\n case \"ArrowDown\":\n case \"s\":\n softDropping = true;\n e.preventDefault();\n break;\n case \"ArrowUp\":\n case \"w\":\n tryRotate(1);\n e.preventDefault();\n break;\n case \"z\":\n case \"Z\":\n tryRotate(-1);\n e.preventDefault();\n break;\n case \" \":\n // Hard drop\n while (!collides(current.shape, { x: current.pos.x, y: current.pos.y + 1 })) {\n current.pos.y++;\n score += 2;\n }\n lock();\n lastDrop = performance.now();\n e.preventDefault();\n break;\n }\n});\n\ndocument.addEventListener(\"keyup\", (e: KeyboardEvent) => {\n if (e.key === \"ArrowDown\" || e.key === \"s\") {\n softDropping = false;\n }\n});\n\n// ── Initialization ─────────────────────────────────────────────────────────\n\nfunction init(): void {\n board = createBoard();\n score = 0;\n lines = 0;\n level = 0;\n gameOver = false;\n paused = false;\n dropInterval = 1000;\n lastDrop = performance.now();\n lockDelay = 500;\n lockTimer = 0;\n softDropping = false;\n\n next = randomPiece();\n spawn();\n updateHUD();\n\n if (animationId) cancelAnimationFrame(animationId);\n animationId = requestAnimationFrame(gameLoop);\n}\n\ninit();\n","numLines":544,"startLine":1,"totalLines":544}}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01HJTXrSMFBiRjg18trMD69M","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_016B6cNQgpqu9SMHyPDPr18P","name":"Read","input":{"file_path":"/tmp/loop-bench-amzysv_v/index.html"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":7000,"cache_read_input_tokens":13367,"cache_creation":{"ephemeral_5m_input_tokens":7000,"ephemeral_1h_input_tokens":0},"output_tokens":35,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"8bda06b1-3a03-4123-87e2-e009aee808aa"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_016B6cNQgpqu9SMHyPDPr18P","type":"tool_result","content":"1\t<!DOCTYPE html>\n2\t<html lang=\"en\">\n3\t<head>\n4\t <meta charset=\"UTF-8\" />\n5\t <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n6\t <title>Tetris</title>\n7\t <style>\n8\t * { margin: 0; padding: 0; box-sizing: border-box; }\n9\t\n10\t body {\n11\t background: #0a0a1a;\n12\t color: #e0e0e0;\n13\t font-family: 'Segoe UI', 'Consolas', monospace;\n14\t display: flex;\n15\t justify-content: center;\n16\t align-items: center;\n17\t min-height: 100vh;\n18\t overflow: hidden;\n19\t }\n20\t\n21\t .game-wrapper {\n22\t display: flex;\n23\t gap: 24px;\n24\t align-items: flex-start;\n25\t }\n26\t\n27\t #game-canvas {\n28\t border: 3px solid #444;\n29\t border-radius: 4px;\n30\t box-shadow: 0 0 30px rgba(80, 80, 255, 0.15);\n31\t }\n32\t\n33\t .sidebar {\n34\t display: flex;\n35\t flex-direction: column;\n36\t gap: 20px;\n37\t min-width: 160px;\n38\t }\n39\t\n40\t .panel {\n41\t background: #1a1a2e;\n42\t border: 2px solid #333;\n43\t border-radius: 8px;\n44\t padding: 16px;\n45\t }\n46\t\n47\t .panel h3 {\n48\t font-size: 11px;\n49\t text-transform: uppercase;\n50\t letter-spacing: 2px;\n51\t color: #888;\n52\t margin-bottom: 10px;\n53\t }\n54\t\n55\t .panel .value {\n56\t font-size: 26px;\n57\t font-weight: bold;\n58\t color: #fff;\n59\t font-variant-numeric: tabular-nums;\n60\t }\n61\t\n62\t #preview-canvas {\n63\t display: block;\n64\t margin: 0 auto;\n65\t border-radius: 4px;\n66\t }\n67\t\n68\t .controls {\n69\t font-size: 12px;\n70\t line-height: 1.9;\n71\t color: #777;\n72\t }\n73\t\n74\t .controls kbd {\n75\t background: #2a2a3e;\n76\t border: 1px solid #444;\n77\t border-radius: 3px;\n78\t padding: 1px 6px;\n79\t font-size: 11px;\n80\t color: #ccc;\n81\t }\n82\t </style>\n83\t</head>\n84\t<body>\n85\t <div class=\"game-wrapper\">\n86\t <canvas id=\"game-canvas\"></canvas>\n87\t <div class=\"sidebar\">\n88\t <div class=\"panel\">\n89\t <h3>Next</h3>\n90\t <canvas id=\"preview-canvas\"></canvas>\n91\t </div>\n92\t <div class=\"panel\">\n93\t <h3>Score</h3>\n94\t <div class=\"value\" id=\"score\">0</div>\n95\t </div>\n96\t <div class=\"panel\">\n97\t <h3>Lines</h3>\n98\t <div class=\"value\" id=\"lines\">0</div>\n99\t </div>\n100\t <div class=\"panel\">\n101\t <h3>Level</h3>\n102\t <div class=\"value\" id=\"level\">0</div>\n103\t </div>\n104\t <div class=\"panel controls\">\n105\t <h3>Controls</h3>\n106\t <kbd>&larr;</kbd> <kbd>&rarr;</kbd> Move<br/>\n107\t <kbd>&darr;</kbd> Soft drop<br/>\n108\t <kbd>&uarr;</kbd> Rotate CW<br/>\n109\t <kbd>Z</kbd> Rotate CCW<br/>\n110\t <kbd>Space</kbd> Hard drop<br/>\n111\t <kbd>P</kbd> / <kbd>Esc</kbd> Pause<br/>\n112\t <kbd>R</kbd> Restart\n113\t </div>\n114\t </div>\n115\t </div>\n116\t <script type=\"module\" src=\"dist/tetris.js\"></script>\n117\t</body>\n118\t</html>\n119\t"}]},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"b7935724-f829-4891-ae08-5d826277030d","timestamp":"2026-04-04T20:59:39.288Z","tool_use_result":{"type":"text","file":{"filePath":"/tmp/loop-bench-amzysv_v/index.html","content":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Tetris</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n\n body {\n background: #0a0a1a;\n color: #e0e0e0;\n font-family: 'Segoe UI', 'Consolas', monospace;\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 100vh;\n overflow: hidden;\n }\n\n .game-wrapper {\n display: flex;\n gap: 24px;\n align-items: flex-start;\n }\n\n #game-canvas {\n border: 3px solid #444;\n border-radius: 4px;\n box-shadow: 0 0 30px rgba(80, 80, 255, 0.15);\n }\n\n .sidebar {\n display: flex;\n flex-direction: column;\n gap: 20px;\n min-width: 160px;\n }\n\n .panel {\n background: #1a1a2e;\n border: 2px solid #333;\n border-radius: 8px;\n padding: 16px;\n }\n\n .panel h3 {\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 2px;\n color: #888;\n margin-bottom: 10px;\n }\n\n .panel .value {\n font-size: 26px;\n font-weight: bold;\n color: #fff;\n font-variant-numeric: tabular-nums;\n }\n\n #preview-canvas {\n display: block;\n margin: 0 auto;\n border-radius: 4px;\n }\n\n .controls {\n font-size: 12px;\n line-height: 1.9;\n color: #777;\n }\n\n .controls kbd {\n background: #2a2a3e;\n border: 1px solid #444;\n border-radius: 3px;\n padding: 1px 6px;\n font-size: 11px;\n color: #ccc;\n }\n </style>\n</head>\n<body>\n <div class=\"game-wrapper\">\n <canvas id=\"game-canvas\"></canvas>\n <div class=\"sidebar\">\n <div class=\"panel\">\n <h3>Next</h3>\n <canvas id=\"preview-canvas\"></canvas>\n </div>\n <div class=\"panel\">\n <h3>Score</h3>\n <div class=\"value\" id=\"score\">0</div>\n </div>\n <div class=\"panel\">\n <h3>Lines</h3>\n <div class=\"value\" id=\"lines\">0</div>\n </div>\n <div class=\"panel\">\n <h3>Level</h3>\n <div class=\"value\" id=\"level\">0</div>\n </div>\n <div class=\"panel controls\">\n <h3>Controls</h3>\n <kbd>&larr;</kbd> <kbd>&rarr;</kbd> Move<br/>\n <kbd>&darr;</kbd> Soft drop<br/>\n <kbd>&uarr;</kbd> Rotate CW<br/>\n <kbd>Z</kbd> Rotate CCW<br/>\n <kbd>Space</kbd> Hard drop<br/>\n <kbd>P</kbd> / <kbd>Esc</kbd> Pause<br/>\n <kbd>R</kbd> Restart\n </div>\n </div>\n </div>\n <script type=\"module\" src=\"dist/tetris.js\"></script>\n</body>\n</html>\n","numLines":119,"startLine":1,"totalLines":119}}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_018fmBF8xmWxBqR4yEcxLk9a","type":"message","role":"assistant","content":[{"type":"text","text":"Now let me add a build script to `package.json` and verify everything compiles cleanly:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":1321,"cache_read_input_tokens":20367,"cache_creation":{"ephemeral_5m_input_tokens":1321,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"8644a4f6-a9dd-4143-8b58-e0881690374a"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_018fmBF8xmWxBqR4yEcxLk9a","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01LeuchEhcdHCAZPR4zWjAxs","name":"Edit","input":{"replace_all":false,"file_path":"/tmp/loop-bench-amzysv_v/package.json","old_string":" \"scripts\": {\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },","new_string":" \"scripts\": {\n \"build\": \"tsc\",\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":1321,"cache_read_input_tokens":20367,"cache_creation":{"ephemeral_5m_input_tokens":1321,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"4e675597-b944-42a1-a01e-210a5e3c8390"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01LeuchEhcdHCAZPR4zWjAxs","type":"tool_result","content":"The file /tmp/loop-bench-amzysv_v/package.json has been updated successfully."}]},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"81012f04-b01b-4af2-8bf7-50675ee27ffc","timestamp":"2026-04-04T20:59:44.049Z","tool_use_result":{"filePath":"/tmp/loop-bench-amzysv_v/package.json","oldString":" \"scripts\": {\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },","newString":" \"scripts\": {\n \"build\": \"tsc\",\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },","originalFile":"{\n \"name\": \"loop-bench-amzysv_v\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },\n \"keywords\": [],\n \"author\": \"\",\n \"license\": \"ISC\",\n \"type\": \"commonjs\",\n \"devDependencies\": {\n \"@eslint/js\": \"^10.0.1\",\n \"@playwright/test\": \"^1.59.1\",\n \"@types/node\": \"^25.5.2\",\n \"eslint\": \"^10.2.0\",\n \"typescript\": \"^6.0.2\"\n }\n}\n","structuredPatch":[{"oldStart":4,"oldLines":6,"newStart":4,"newLines":7,"lines":[" \"description\": \"\","," \"main\": \"index.js\","," \"scripts\": {","+ \"build\": \"tsc\","," \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\""," },"," \"keywords\": [],"]}],"userModified":false,"replaceAll":false}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01Djtx7NYnZfhxhnSL1A3hyC","type":"message","role":"assistant","content":[{"type":"text","text":"Let me also verify the compiled JS works by checking it has no obvious issues:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":244,"cache_read_input_tokens":21688,"cache_creation":{"ephemeral_5m_input_tokens":244,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"8311a583-bb0f-4f60-9f39-71c8cf48afc2"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_01Djtx7NYnZfhxhnSL1A3hyC","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_0137zhqJQnMotZ9Vfyj2ubkA","name":"Read","input":{"file_path":"/tmp/loop-bench-amzysv_v/dist/tetris.js","limit":30},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":244,"cache_read_input_tokens":21688,"cache_creation":{"ephemeral_5m_input_tokens":244,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"d6e4c987-e49c-41ca-99c8-f60472361857"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_0137zhqJQnMotZ9Vfyj2ubkA","type":"tool_result","content":"1\t\"use strict\";\n2\t// ── Types ──────────────────────────────────────────────────────────────────\n3\t// ── Constants ──────────────────────────────────────────────────────────────\n4\tconst COLS = 10;\n5\tconst ROWS = 20;\n6\tconst BLOCK_SIZE = 32;\n7\tconst PREVIEW_BLOCK = 20;\n8\tconst COLORS = {\n9\t I: \"#00f0f0\",\n10\t O: \"#f0f000\",\n11\t T: \"#a000f0\",\n12\t S: \"#00f000\",\n13\t Z: \"#f00000\",\n14\t J: \"#0000f0\",\n15\t L: \"#f0a000\",\n16\t};\n17\tconst SHAPES = {\n18\t I: [\n19\t [0, 0, 0, 0],\n20\t [1, 1, 1, 1],\n21\t [0, 0, 0, 0],\n22\t [0, 0, 0, 0],\n23\t ],\n24\t O: [\n25\t [1, 1],\n26\t [1, 1],\n27\t ],\n28\t T: [\n29\t [0, 1, 0],\n30\t [1, 1, 1],"}]},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"b9165e8e-67c0-4c9f-a644-c1306116e91c","timestamp":"2026-04-04T20:59:48.427Z","tool_use_result":{"type":"text","file":{"filePath":"/tmp/loop-bench-amzysv_v/dist/tetris.js","content":"\"use strict\";\n// ── Types ──────────────────────────────────────────────────────────────────\n// ── Constants ──────────────────────────────────────────────────────────────\nconst COLS = 10;\nconst ROWS = 20;\nconst BLOCK_SIZE = 32;\nconst PREVIEW_BLOCK = 20;\nconst COLORS = {\n I: \"#00f0f0\",\n O: \"#f0f000\",\n T: \"#a000f0\",\n S: \"#00f000\",\n Z: \"#f00000\",\n J: \"#0000f0\",\n L: \"#f0a000\",\n};\nconst SHAPES = {\n I: [\n [0, 0, 0, 0],\n [1, 1, 1, 1],\n [0, 0, 0, 0],\n [0, 0, 0, 0],\n ],\n O: [\n [1, 1],\n [1, 1],\n ],\n T: [\n [0, 1, 0],\n [1, 1, 1],","numLines":30,"startLine":1,"totalLines":473}}} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_0193xxWCVHwRtxdt8HDXusPD","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"The compiled JS has `\"use strict\"` at the top but the HTML uses `type=\"module\"`. Since the TS compiler is set to output ES2020 modules, the compiled JS should use `export` statements. But since there are no exports in the file, and it's loaded as a module, it should still work fine - modules are strict by default and the `\"use strict\"` is harmless.\n\nWait, actually there might be an issue. The compiled JS uses `\"use strict\"` but ES modules are already strict. The real issue is whether the `document.getElementById` calls work. Since the script is at the bottom of the body with `type=\"module\"`, the DOM will be loaded before the script executes (modules are deferred by default), so that's fine.\n\n\nActually, since the file is loaded as a module, it's automatically treated as one regardless of whether it has explicit imports or exports, so the redundant `\"use strict\"` won't cause problems. The self-contained script should execute without issues.","signature":"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"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":462,"cache_read_input_tokens":21932,"cache_creation":{"ephemeral_5m_input_tokens":462,"ephemeral_1h_input_tokens":0},"output_tokens":9,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"e82b9fdd-e407-4eb5-ad1b-9cda78dbae67"} +{"type":"assistant","message":{"model":"claude-opus-4-6","id":"msg_0193xxWCVHwRtxdt8HDXusPD","type":"message","role":"assistant","content":[{"type":"text","text":"Everything compiles cleanly. Here's a summary of the complete Tetris game:\n\n## Files Created\n\n### `src/tetris.ts` — Game engine (544 lines)\nThe full game logic in TypeScript, including:\n\n- **7 standard Tetrominos** (I, O, T, S, Z, J, L) with correct SRS rotation shapes\n- **SRS Wall Kicks** — full Super Rotation System wall-kick tables for both I-pieces and standard pieces, so rotations work near walls and in tight spaces\n- **Collision detection** — validates moves against board boundaries and locked blocks\n- **Line clearing** — detects and removes full rows, shifts board down\n- **Scoring** — NES-style point system (100/300/500/800 × level multiplier for 1/2/3/4 lines), plus soft drop (+1/row) and hard drop (+2/row) bonuses\n- **Leveling** — level increases every 10 lines; gravity speeds up from 1000ms to a minimum of 50ms\n- **Lock delay** — 500ms grace period when a piece lands, allowing slides and rotations before locking\n- **Ghost piece** — translucent preview showing where the piece will land\n- **Next piece preview** — shown in a sidebar canvas\n- **Pause / Game Over** states with overlays\n\n### `index.html` — UI shell\nDark-themed layout with:\n- 320×640 game canvas (10×20 grid of 32px blocks)\n- Sidebar showing next piece, score, lines, level, and control reference\n\n### Keyboard Controls\n| Key | Action |\n|---|---|\n| `←` / `→` or `A` / `D` | Move left/right |\n| `↓` or `S` | Soft drop |\n| `↑` or `W` | Rotate clockwise |\n| `Z` | Rotate counter-clockwise |\n| `Space` | Hard drop |\n| `P` / `Esc` | Pause |\n| `R` | Restart (after game over) |\n\n### How to play\nOpen `index.html` in a browser. Since it uses `type=\"module\"`, you'll need to serve it over HTTP (e.g., `npx serve .` or `python3 -m http.server`) rather than opening as a `file://` URL."}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":462,"cache_read_input_tokens":21932,"cache_creation":{"ephemeral_5m_input_tokens":462,"ephemeral_1h_input_tokens":0},"output_tokens":9,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","uuid":"835ff0c4-a60c-4642-8af2-b6f465995406"} +{"type":"result","subtype":"success","is_error":false,"duration_ms":153497,"duration_api_ms":151572,"num_turns":15,"result":"Everything compiles cleanly. Here's a summary of the complete Tetris game:\n\n## Files Created\n\n### `src/tetris.ts` — Game engine (544 lines)\nThe full game logic in TypeScript, including:\n\n- **7 standard Tetrominos** (I, O, T, S, Z, J, L) with correct SRS rotation shapes\n- **SRS Wall Kicks** — full Super Rotation System wall-kick tables for both I-pieces and standard pieces, so rotations work near walls and in tight spaces\n- **Collision detection** — validates moves against board boundaries and locked blocks\n- **Line clearing** — detects and removes full rows, shifts board down\n- **Scoring** — NES-style point system (100/300/500/800 × level multiplier for 1/2/3/4 lines), plus soft drop (+1/row) and hard drop (+2/row) bonuses\n- **Leveling** — level increases every 10 lines; gravity speeds up from 1000ms to a minimum of 50ms\n- **Lock delay** — 500ms grace period when a piece lands, allowing slides and rotations before locking\n- **Ghost piece** — translucent preview showing where the piece will land\n- **Next piece preview** — shown in a sidebar canvas\n- **Pause / Game Over** states with overlays\n\n### `index.html` — UI shell\nDark-themed layout with:\n- 320×640 game canvas (10×20 grid of 32px blocks)\n- Sidebar showing next piece, score, lines, level, and control reference\n\n### Keyboard Controls\n| Key | Action |\n|---|---|\n| `←` / `→` or `A` / `D` | Move left/right |\n| `↓` or `S` | Soft drop |\n| `↑` or `W` | Rotate clockwise |\n| `Z` | Rotate counter-clockwise |\n| `Space` | Hard drop |\n| `P` / `Esc` | Pause |\n| `R` | Restart (after game over) |\n\n### How to play\nOpen `index.html` in a browser. Since it uses `type=\"module\"`, you'll need to serve it over HTTP (e.g., `npx serve .` or `python3 -m http.server`) rather than opening as a `file://` URL.","stop_reason":"end_turn","session_id":"3384cc86-5410-4e03-9eee-ffcc1ad82428","total_cost_usd":0.4540989999999999,"usage":{"input_tokens":17,"cache_creation_input_tokens":22938,"cache_read_input_tokens":152753,"output_tokens":9371,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":22938},"inference_geo":"","iterations":[],"speed":"standard"},"modelUsage":{"claude-opus-4-6":{"inputTokens":17,"outputTokens":9371,"cacheReadInputTokens":152753,"cacheCreationInputTokens":22938,"webSearchRequests":0,"costUSD":0.4540989999999999,"contextWindow":200000,"maxOutputTokens":64000}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","uuid":"6255903a-c924-47ed-ae02-d7e35cb68ec0"} diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/claude_output.json b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/claude_output.json @@ -0,0 +1 @@ +{"type":"result","subtype":"success","is_error":false,"duration_ms":418149,"duration_api_ms":414770,"num_turns":19,"result":"All IDs match perfectly. Here's a full summary of everything that was built:\n\n---\n\n## What was built\n\n**Files created:**\n\n| File | Purpose |\n|---|---|\n| `tetris.ts` | TypeScript source (~700 lines) |\n| `tetris.js` | Compiled output (served to the browser) |\n| `index.html` | Game layout — canvas + side panel |\n| `style.css` | Dark retro theme with animated title |\n| `tsconfig.json` | TypeScript compiler config |\n\n**Open `index.html` in any browser to play.**\n\n---\n\n## Features & mechanics\n\n### Pieces\n- All **7 standard tetrominoes** (I, O, T, S, Z, J, L) with authentic SRS (Super Rotation System) shapes and colour coding\n- **7-bag randomiser** — each set of 7 pieces contains one of every type, eliminating long droughts\n\n### Rotation\n- **SRS wall kicks** for both CW (`↑` / `X`) and CCW (`Z` / `Ctrl`) rotation — tries up to 5 offset tests so pieces kick off walls and floors cleanly\n\n### Movement\n- **Arrow keys** to move left/right/down\n- **DAS (Delayed Auto-Shift)** — hold a direction key and the piece starts repeating after 170 ms, then every 50 ms, for smooth modern feel\n\n### Dropping\n- **Soft drop** (`↓`) — accelerates fall, scores 1 pt/row\n- **Hard drop** (`Space`) — instantly slams to ghost position, scores 2 pts/row\n\n### Scoring\n| Lines cleared | Points × level |\n|---|---|\n| 1 (Single) | 100 |\n| 2 (Double) | 300 |\n| 3 (Triple) | 500 |\n| 4 (Tetris) | 800 |\n\n### Speed / Level\n- Level advances every **10 lines**\n- Drop interval formula: `max(50ms, 1000ms × 0.85^(level-1))` — roughly 12% faster per level, capping at 50 ms (20 drops/sec) around level 20\n\n### Visual\n- **Ghost piece** — outline showing where the current piece will land\n- **Beveled 3-D cells** with top-left highlight and bottom-right shadow\n- **Next piece preview** panel\n- Pause / Game Over overlays\n- Subtle grid, animated rainbow title\n\n### Controls summary\n| Key | Action |\n|---|---|\n| `←` / `→` | Move |\n| `↓` | Soft drop |\n| `Space` | Hard drop |\n| `↑` / `X` | Rotate CW |\n| `Z` / `Ctrl` | Rotate CCW |\n| `P` / `Esc` | Pause / Resume |\n| `R` | Restart |","stop_reason":"end_turn","session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","total_cost_usd":0.79805865,"usage":{"input_tokens":22,"cache_creation_input_tokens":42361,"cache_read_input_tokens":467563,"output_tokens":33258,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":42361},"inference_geo":"","iterations":[],"speed":"standard"},"modelUsage":{"claude-sonnet-4-6":{"inputTokens":22,"outputTokens":33258,"cacheReadInputTokens":467563,"cacheCreationInputTokens":42361,"webSearchRequests":0,"costUSD":0.79805865,"contextWindow":200000,"maxOutputTokens":32000}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","uuid":"b413f222-1fe0-4c08-b277-022934dc6f3a"} +\ No newline at end of file diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/claude_stderr.log b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/claude_stderr.log diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/eval_results.json b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/eval_results.json @@ -0,0 +1,261 @@ +{ + "structural": { + "pass": false, + "checks": [ + { + "name": "entry_point_exists", + "pass": true, + "detail": "index.html found" + }, + { + "name": "package_json_exists", + "pass": true, + "detail": "package.json found" + }, + { + "name": "build_succeeds", + "pass": true, + "detail": "no build script defined (static project)" + }, + { + "name": "typescript_compiles", + "pass": false, + "detail": "tsc --noEmit failed" + } + ], + "score": 0.75 + }, + "functional": { + "pass": false, + "error": "playwright eval not yet wired", + "score": 0 + }, + "quality": { + "lint": { + "pass": true, + "errors": 0, + "warnings": 0 + }, + "typecheck": { + "pass": false, + "errors": 2 + }, + "performance": { + "bundle_size_bytes": 105117, + "size_under_512kb": true + }, + "score": 0.33 + }, + "code_analysis": { + "files": { + "total": 8, + "code": 4, + "docs": 0, + "unnecessary": 0, + "unnecessary_list": [] + }, + "lines_of_code": 1542, + "dependencies": { + "production": 0, + "dev": 5, + "total": 5 + }, + "complexity": "moderate", + "console_logs": 0, + "magic_numbers": { + "count": 34, + "excessive": true + }, + "function_length": { + "count": 58, + "average": 8.2, + "max": 39, + "long_functions": 0 + }, + "max_nesting_depth": 12, + "global_declarations": 18, + "naming": { + "dominant_style": "camelCase", + "consistency_pct": 100.0, + "camel_case": 402, + "snake_case": 0 + }, + "error_handling": { + "try_catch_blocks": 0, + "has_error_handling": false + }, + "comments": { + "comment_lines": 131, + "source_lines": 1072, + "ratio_pct": 12.2 + }, + "separation_of_concerns": { + "verdict": "mixed", + "files_with_rendering": 2, + "files_with_logic": 2, + "files_with_both": 2 + }, + "html_validation": { + "valid": false, + "errors": 6 + }, + "duplication_percentage": 0.0, + "score": 0.85 + }, + "transcript_analysis": { + "total_events": 58, + "tool_calls": { + "total": 18, + "bash": 12, + "write": 0, + "edit": 2, + "read": 4 + }, + "wasted_turns": { + "total": 0, + "docs": 0, + "ascii_art": 0, + "server_starts": 0 + }, + "errors_encountered": 0, + "thinking_blocks": 7, + "text_blocks": 11, + "productivity_ratio": 1.0, + "self_tested": false, + "score": 1.0 + }, + "gameplay_bot": { + "pass": false, + "score": 0.94, + "total": 16, + "passed": 15, + "failed": 1, + "report": { + "implementation": { + "renderer": "canvas", + "grid_detected": true, + "grid_bounds": { + "x": 0, + "y": 0, + "width": 300, + "height": 600 + }, + "controls": { + "left": "ArrowLeft", + "right": "ArrowRight", + "down": "ArrowDown", + "rotate": "z", + "drop": "Space" + }, + "start_mechanism": "auto", + "score_element_found": true + }, + "tests": [ + { + "name": "game_loads", + "pass": true, + "detail": "no console errors" + }, + { + "name": "game_starts", + "pass": true, + "detail": "started via auto" + }, + { + "name": "auto_drop", + "pass": true, + "detail": "grid state changed after 5s with no input" + }, + { + "name": "move_left", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "move_right", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "move_down", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "rotate", + "pass": true, + "detail": "piece shape changed after rotate key" + }, + { + "name": "all_pieces_rotate", + "pass": true, + "detail": "rotated: [other] failed: [] (tested 1 piece types in 60 attempts)" + }, + { + "name": "hard_drop", + "pass": true, + "detail": "piece immediately dropped and new piece appeared" + }, + { + "name": "piece_locks", + "pass": true, + "detail": "filled cells persist at bottom" + }, + { + "name": "new_piece_spawns", + "pass": true, + "detail": "new piece detected at top of grid" + }, + { + "name": "multiple_pieces", + "pass": true, + "detail": "grid accumulated cells: 16 -> 39" + }, + { + "name": "line_clear", + "pass": true, + "detail": "1 line(s) cleared during AI play" + }, + { + "name": "score_changes", + "pass": false, + "detail": "score did not increase: [170] -> no change after polling" + }, + { + "name": "game_over", + "pass": true, + "detail": "game stopped after stacking to top" + }, + { + "name": "playable_30s", + "pass": true, + "detail": "played for 30s, placed 79 pieces, no crashes" + } + ], + "summary": { + "total": 16, + "passed": 15, + "failed": 1, + "score": 0.94 + }, + "gameplay": { + "pieces_placed": 120, + "lines_cleared": 2, + "max_score_observed": 172, + "play_duration_seconds": 30, + "errors_during_play": 0 + }, + "performance": { + "load_time_ms": 23 + }, + "accessibility": { + "issues": [ + "canvas without aria-label or role" + ], + "issue_count": 1, + "pass": false + } + } + }, + "score": 0.6475 +} +\ No newline at end of file diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/gameplay-bot-report.json b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/gameplay-bot-report.json @@ -0,0 +1,126 @@ +{ + "implementation": { + "renderer": "canvas", + "grid_detected": true, + "grid_bounds": { + "x": 0, + "y": 0, + "width": 300, + "height": 600 + }, + "controls": { + "left": "ArrowLeft", + "right": "ArrowRight", + "down": "ArrowDown", + "rotate": "z", + "drop": "Space" + }, + "start_mechanism": "auto", + "score_element_found": true + }, + "tests": [ + { + "name": "game_loads", + "pass": true, + "detail": "no console errors" + }, + { + "name": "game_starts", + "pass": true, + "detail": "started via auto" + }, + { + "name": "auto_drop", + "pass": true, + "detail": "grid state changed after 5s with no input" + }, + { + "name": "move_left", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "move_right", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "move_down", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "rotate", + "pass": true, + "detail": "piece shape changed after rotate key" + }, + { + "name": "all_pieces_rotate", + "pass": true, + "detail": "rotated: [other] failed: [] (tested 1 piece types in 60 attempts)" + }, + { + "name": "hard_drop", + "pass": true, + "detail": "piece immediately dropped and new piece appeared" + }, + { + "name": "piece_locks", + "pass": true, + "detail": "filled cells persist at bottom" + }, + { + "name": "new_piece_spawns", + "pass": true, + "detail": "new piece detected at top of grid" + }, + { + "name": "multiple_pieces", + "pass": true, + "detail": "grid accumulated cells: 16 -> 39" + }, + { + "name": "line_clear", + "pass": true, + "detail": "1 line(s) cleared during AI play" + }, + { + "name": "score_changes", + "pass": false, + "detail": "score did not increase: [170] -> no change after polling" + }, + { + "name": "game_over", + "pass": true, + "detail": "game stopped after stacking to top" + }, + { + "name": "playable_30s", + "pass": true, + "detail": "played for 30s, placed 79 pieces, no crashes" + } + ], + "summary": { + "total": 16, + "passed": 15, + "failed": 1, + "score": 0.94 + }, + "gameplay": { + "pieces_placed": 120, + "lines_cleared": 2, + "max_score_observed": 172, + "play_duration_seconds": 30, + "errors_during_play": 0 + }, + "performance": { + "load_time_ms": 23 + }, + "accessibility": { + "issues": [ + "canvas without aria-label or role" + ], + "issue_count": 1, + "pass": false + } +} +\ No newline at end of file diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/meta.json b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/meta.json @@ -0,0 +1,30 @@ +{ + "model": "sonnet", + "effort": "high", + "prompt_style": "simple", + "language": "typescript", + "human_language": "en", + "tool_read": "on", + "tool_write": "on", + "tool_edit": "on", + "tool_glob": "on", + "tool_grep": "on", + "linter": "on", + "playwright": "on", + "context_file": "none", + "sub_agents": "on", + "web_search": "on", + "max_budget": "low", + "task": "tetris", + "cell_id": "tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on", + "runs_per_cell": 3, + "timeout_seconds": 1200, + "max_budget_usd": 2.0, + "run_id": "tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1", + "run_number": 1, + "claude_version": "2.1.92 (Claude Code)", + "started_at": "2026-04-04T20:57:28.500554+00:00", + "wall_time_seconds": 419, + "exit_code": 0, + "completed_at": "2026-04-04T21:04:34.885122+00:00" +} +\ No newline at end of file diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/transcript.jsonl b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run1/transcript.jsonl @@ -0,0 +1,58 @@ +{"type": "harness", "subtype": "config", "model": "sonnet", "effort": "high", "tools": ["Bash", "Read", "Write", "Edit", "Glob", "Grep", "Agent", "WebSearch", "WebFetch"], "max_budget_usd": 2.0, "timeout_seconds": 1200, "task": "tetris", "language": "typescript", "prompt_style": "simple"} +{"type": "user", "subtype": "prompt", "message": {"role": "user", "content": "Build a playable Tetris game that runs in a browser. It should have all the standard mechanics: piece rotation, line clearing, scoring, and increasing speed. Use keyboard controls for movement and rotation.\n\n\nUse TypeScript."}} +{"type":"system","subtype":"init","cwd":"/tmp/loop-bench-6vr8i9xe","session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","tools":["Bash","Edit","Read"],"mcp_servers":[],"model":"claude-sonnet-4-6","permissionMode":"dontAsk","slash_commands":["update-config","debug","simplify","batch","loop","schedule","claude-api","compact","context","cost","heapdump","init","review","security-review","insights"],"apiKeySource":"apiKeyHelper","claude_code_version":"2.1.92","output_style":"default","agents":["general-purpose","statusline-setup","Explore","Plan"],"skills":["update-config","debug","simplify","batch","loop","schedule","claude-api"],"plugins":[{"name":"claude-hud","path":"/root/.claude/plugins/marketplaces/claude-hud/","source":"claude-hud@claude-hud"},{"name":"rust-analyzer-lsp","path":"/root/.claude/plugins/marketplaces/claude-plugins-official/plugins/rust-analyzer-lsp","source":"rust-analyzer-lsp@claude-plugins-official"}],"uuid":"b0ae94a2-622c-4b08-a855-f0b95459252d","fast_mode_state":"off"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_013vWVcXYwUGFB8g3BqBjfuj","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"Let me build a complete Tetris game in TypeScript that runs in a browser. I'll create:\n\n1. `index.html` - The main HTML file\n2. `tetris.ts` - The TypeScript source\n3. `style.css` - Styling for the game\n\nSince this needs to run in a browser without a build step (or with minimal setup), I'll write the TypeScript and also compile it, or better yet, I'll use a single HTML file with inline TypeScript that gets compiled, or I'll write it as a self-contained HTML + JS (compiled from TS).\n\nActually, let me think about this. The user asked for TypeScript. I'll create:\n\n\n- `index.html`\n- `tetris.ts` (the TypeScript source)\n- `style.css`\n\nTo make it runnable in a browser, I'll need to compile the TypeScript to JavaScript. Let me check if `tsc` is available and compile it, or I could embed the compiled JS directly in the HTML file.\n\nNow I'm planning out the core game structure: a 10×20 grid for the board, the seven standard Tetris pieces (I, O, T, S, Z, J, L), and the essential mechanics like piece rotation, collision detection, line clearing, and scoring.\n\nFor controls, I'll map arrow keys for movement and rotation, space for hard drop, and P for pause. I'm also going to include a ghost piece preview and next piece indicator.\n\nWriting the TypeScript implementation... Defining tetromino shapes... Defining the J and L piece rotations...\n\nNow I'm setting up the type definitions for the game — the tetromino types, board cells, and piece structure. I'm also implementing the wall kick system for rotation, starting with the standard rotation system data for J, L, S, T, and Z pieces.\n\nDefining the Tetris class... Setting up the game board and piece spawning...\n\nNow I'm implementing the collision detection logic to check if a piece can move to a new position, accounting for board boundaries and existing blocks.\n\nContinuing with piece spawning...\n\nNow I'm implementing the logic to lock a piece in place once it can't move anymore—I iterate through the piece's shape, place each block on the board with its color, then clear any completed lines and spawn the next piece.\n\nContinuing with the line-clearing logic, I'm scanning from the bottom of the board upward to find any rows that are completely filled, removing them and adding new empty rows at the top while incrementing a counter for each cleared line. Then I calculate the score based on how many lines were cleared, multiplying the points by the current level.\n\nNow I'm updating the total lines count, recalculating the level based on every 10 lines cleared, and adjusting the drop speed to get faster as the level increases. After that, I refresh the UI to display the updated score, lines, and level values. Finally, I'm implementing a ghost piece preview that shows where the current piece would land if dropped straight down.\n\nWriting the rendering logic... Writing the grid and rendering cells... Now I'm implementing the ghost piece visualization, which shows a semi-transparent outline of where the current piece would land if dropped straight down. I iterate through the ghost shape and draw hollow rectangles with a stroke style at reduced opacity to create that preview effect.\n\nWriting the next piece preview... Still writing rendering code... Writing the game loop and rendering... Writing game logic... Setting up the restart method to reset all game state variables back to initial values, then I'm wiring up keyboard controls to handle rotation, movement, pause toggling, and restart functionality. Setting up the HTML structure with a canvas for the game board and a side panel for the next piece preview, score, and controls, then organizing everything into separate files that I'll compile from TypeScript to JavaScript.","signature":"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"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":4729,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":4729,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"66189202-51c3-4216-a52b-4d88ac1e7bb9"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_013vWVcXYwUGFB8g3BqBjfuj","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01EoQyJ4UvFjGL7gjyQyXA1e","name":"Bash","input":{"command":"ls /tmp/loop-bench-6vr8i9xe && which tsc && tsc --version","description":"Check working directory and TypeScript compiler"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":4729,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":4729,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"26a69836-7bac-4e92-8cb8-0a5b29edb7e6"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01EoQyJ4UvFjGL7gjyQyXA1e","type":"tool_result","content":"node_modules\npackage.json\npackage-lock.json\n/usr/bin/tsc\nVersion 5.9.3","is_error":false}]},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"1a291799-ae28-4956-8759-8fc38f5b819f","timestamp":"2026-04-04T20:58:49.718Z","tool_use_result":{"stdout":"node_modules\npackage.json\npackage-lock.json\n/usr/bin/tsc\nVersion 5.9.3","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01UEVb7fX6zMVmYXZy7ArYxr","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01GewoQuGtmW2n8RuYRhzqfJ","name":"Read","input":{"file_path":"/tmp/loop-bench-6vr8i9xe/package.json"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":7025,"cache_read_input_tokens":4729,"cache_creation":{"ephemeral_5m_input_tokens":7025,"ephemeral_1h_input_tokens":0},"output_tokens":45,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"e4a732b5-6184-4a05-96d5-77e8edb61c89"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01GewoQuGtmW2n8RuYRhzqfJ","type":"tool_result","content":"1\t{\n2\t \"name\": \"loop-bench-6vr8i9xe\",\n3\t \"version\": \"1.0.0\",\n4\t \"description\": \"\",\n5\t \"main\": \"index.js\",\n6\t \"scripts\": {\n7\t \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n8\t },\n9\t \"keywords\": [],\n10\t \"author\": \"\",\n11\t \"license\": \"ISC\",\n12\t \"type\": \"commonjs\",\n13\t \"devDependencies\": {\n14\t \"@eslint/js\": \"^10.0.1\",\n15\t \"@playwright/test\": \"^1.59.1\",\n16\t \"@types/node\": \"^25.5.2\",\n17\t \"eslint\": \"^10.2.0\",\n18\t \"typescript\": \"^6.0.2\"\n19\t }\n20\t}\n21\t"}]},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"381d5e64-b174-4afb-a136-671b8bbfbab7","timestamp":"2026-04-04T20:58:51.927Z","tool_use_result":{"type":"text","file":{"filePath":"/tmp/loop-bench-6vr8i9xe/package.json","content":"{\n \"name\": \"loop-bench-6vr8i9xe\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },\n \"keywords\": [],\n \"author\": \"\",\n \"license\": \"ISC\",\n \"type\": \"commonjs\",\n \"devDependencies\": {\n \"@eslint/js\": \"^10.0.1\",\n \"@playwright/test\": \"^1.59.1\",\n \"@types/node\": \"^25.5.2\",\n \"eslint\": \"^10.2.0\",\n \"typescript\": \"^6.0.2\"\n }\n}\n","numLines":21,"startLine":1,"totalLines":21}}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_017ZoQmEdkTcjUabxiVGu2Ds","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"Good, TypeScript is available. Let me create the three files: `index.html`, `tetris.ts`, and `style.css`. Then compile the TypeScript.\n\nLet me think about the directory structure. I'll put all files in `/tmp/loop-bench-6vr8i9xe/`.\n\nLet me now write all the files. I'll be careful with the TypeScript code to make sure it compiles cleanly.\n\nFor the wall kick system, I need to be careful. Let me use the standard SRS (Super Rotation System) wall kicks.\n\nSRS wall kick data:\n\n\nDefining the wall kick offsets for J, L, S, T, Z pieces across all rotation transitions...\n\nActually, I need to think through the coordinate system more carefully—in Tetris, the SRS wall kick data uses specific x and y offsets, but I'm getting confused about whether y is positive upward or downward. Let me simplify this and just implement a basic set of wall kick attempts rather than memorizing the exact standard table. I'm going to implement the proper SRS wall kick system instead of a simplified version, since it'll give more accurate Tetris behavior. I need to convert between coordinate systems—the standard SRS uses y-positive-up, but screen coordinates have y-positive-down, so I'm negating the y values to translate between them. The conversion checks out with what I had before.\n\nActually, I'm second-guessing myself on some of these offset values. Let me just settle on a straightforward approach for the rotation offsets instead of overthinking the coordinate conversions. I'm working through the wall kick offsets for the I piece across its rotation states, mapping out the exact coordinate adjustments needed for each transition to ensure proper collision detection and piece placement.\n\nFor counter-clockwise rotations, I need to figure out whether to negate the existing clockwise kick values or compute them separately—the simplest approach is to negate both x and y coordinates from the corresponding clockwise transition kicks.\n\nNow I'm implementing the rotation function that computes the new rotation state, attempts wall kicks in sequence, and applies the appropriate kick table based on rotation direction.\n\nWriting rotation logic... I'm getting tangled up in the wall kick offsets—let me pull the actual SRS specifications from the Tetris wiki to make sure I have the correct data for JLSTZ pieces. row offset positive means UP:\n- 0→R: (0,0), (-1,0), (-1,+1), (0,-2), (-1,-2)\n- R→2: (0,0), (+1,0), (+1,-1), (0,+2), (+1,+2)\n- 2→L: (0,0), (+1,0), (+1,+1), (0,-2), (+1,-2)\n- L→0: (0,0), (-1,0), (-1,-1), (0,+2), (-1,+2)\n\nNow I'm converting these to screen coordinates by negating the y-axis, since the wiki uses y+ pointing up while screen coordinates have y+ pointing down. Let me verify the conversions work out correctly across all four rotation states.\n\nFor the I piece, I'm working through the same coordinate transformations across its rotation states, converting from the wiki's coordinate system to screen coordinates.\n\nI've got all the wall kick data mapped out now. I could simplify this with a basic offset system, but I'm going to implement the full SRS specification properly since I have the data. Now I need to handle the I piece's spawn position and then write out the complete game.\n\nThe I piece spawns at column 3 with its horizontal orientation occupying columns 3-6, while the other pieces start at column 3 as well but only span 3 columns. I'm working through the exact positioning logic for how these pieces sit on the board at spawn. Defining the I piece rotations...\n\nNow I'm working through the spawn positions for different piece types. For the I piece, I'll start it at row 1 of its 4x4 grid so it appears correctly when the game begins. For the smaller 3x3 pieces like T, S, Z, J, and L, the top row might be empty depending on the piece shape, but I'll position them starting at row 0 of their shape grid.\n\nLet me verify the T piece spawn: with the shape starting at x=3, y=0, the occupied cells map to board positions (4,0), (3,1), (4,1), and (5,1), which matches the expected layout.\n\nFor game over detection, I need to check if a piece can't fit at its spawn position—if `isValidPosition` returns false at y=0, the game ends. Though I should consider whether pieces with empty top rows like the I piece might need to spawn higher up on the board.\n\nActually, I'll keep it simple and spawn everything at y=0 (the top of the visible board), which is close enough to standard Tetris behavior where pieces appear in the hidden rows above the playfield.\n\nNow I'm ready to write out all the implementation files and make it polished.\n\nBuilding the HTML layout...","signature":"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"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":317,"cache_read_input_tokens":11754,"cache_creation":{"ephemeral_5m_input_tokens":317,"ephemeral_1h_input_tokens":0},"output_tokens":8,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"42dc9542-b23c-4c6e-a8b6-8155a31f7188"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_017ZoQmEdkTcjUabxiVGu2Ds","type":"message","role":"assistant","content":[{"type":"text","text":"Now I have a clear picture of what's available. Let me build the full Tetris game — TypeScript source, HTML, and CSS — then compile it."}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":317,"cache_read_input_tokens":11754,"cache_creation":{"ephemeral_5m_input_tokens":317,"ephemeral_1h_input_tokens":0},"output_tokens":8,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"f3fc03c3-c377-44e3-ace8-edff38ab2954"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_017ZoQmEdkTcjUabxiVGu2Ds","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01Ju5tZZtWAn8m6tYMVMnqdN","name":"Bash","input":{"command":"cat /tmp/loop-bench-6vr8i9xe/package.json","description":"Review package.json"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":317,"cache_read_input_tokens":11754,"cache_creation":{"ephemeral_5m_input_tokens":317,"ephemeral_1h_input_tokens":0},"output_tokens":8,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"11056e9a-984d-4f92-ba95-cdf8d5c2e83a"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01Ju5tZZtWAn8m6tYMVMnqdN","type":"tool_result","content":"{\n \"name\": \"loop-bench-6vr8i9xe\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },\n \"keywords\": [],\n \"author\": \"\",\n \"license\": \"ISC\",\n \"type\": \"commonjs\",\n \"devDependencies\": {\n \"@eslint/js\": \"^10.0.1\",\n \"@playwright/test\": \"^1.59.1\",\n \"@types/node\": \"^25.5.2\",\n \"eslint\": \"^10.2.0\",\n \"typescript\": \"^6.0.2\"\n }\n}","is_error":false}]},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"5671985b-36d5-45ed-88c1-c6ec96a869ee","timestamp":"2026-04-04T21:00:00.706Z","tool_use_result":{"stdout":"{\n \"name\": \"loop-bench-6vr8i9xe\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },\n \"keywords\": [],\n \"author\": \"\",\n \"license\": \"ISC\",\n \"type\": \"commonjs\",\n \"devDependencies\": {\n \"@eslint/js\": \"^10.0.1\",\n \"@playwright/test\": \"^1.59.1\",\n \"@types/node\": \"^25.5.2\",\n \"eslint\": \"^10.2.0\",\n \"typescript\": \"^6.0.2\"\n }\n}","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01SX1swHjDixcdnd6JVxBUgv","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01XXZpuu3VGmhgvM41zAR657","name":"Bash","input":{"command":"cat > /tmp/loop-bench-6vr8i9xe/tsconfig.json << 'EOF'\n{\n \"compilerOptions\": {\n \"target\": \"ES2017\",\n \"lib\": [\"ES2017\", \"DOM\"],\n \"module\": \"None\",\n \"outFile\": \"tetris.js\",\n \"strict\": true,\n \"noImplicitAny\": true,\n \"sourceMap\": true\n },\n \"include\": [\"tetris.ts\"]\n}\nEOF","description":"Create tsconfig.json"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":5518,"cache_read_input_tokens":12071,"cache_creation":{"ephemeral_5m_input_tokens":5518,"ephemeral_1h_input_tokens":0},"output_tokens":63,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"9d32aeb6-d84a-487c-9947-72cc813c05cd"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01XXZpuu3VGmhgvM41zAR657","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"851b059a-f312-477e-803b-4ac04d0d0e55","timestamp":"2026-04-04T21:00:03.675Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01Mn3FST8XDrR2AejHjKP7p1","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01M4C7djB75BWWZNw6Uygzjc","name":"Bash","input":{"command":"cat > /tmp/loop-bench-6vr8i9xe/style.css << 'CSSEOF'\n/* ── Reset & base ── */\n*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\nbody {\n background: #0d0d1a;\n color: #e0e0ff;\n font-family: 'Courier New', Courier, monospace;\n min-height: 100vh;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n user-select: none;\n}\n\n/* ── Title ── */\nh1 {\n font-size: 2.4rem;\n letter-spacing: 0.25em;\n text-transform: uppercase;\n background: linear-gradient(90deg, #00f0f0, #a000f0, #f0a000, #00f000, #f00000);\n background-size: 300% 100%;\n -webkit-background-clip: text;\n -webkit-text-fill-color: transparent;\n background-clip: text;\n animation: rainbow 4s linear infinite;\n margin-bottom: 20px;\n text-shadow: none;\n}\n\n@keyframes rainbow {\n 0% { background-position: 0% 50%; }\n 100% { background-position: 300% 50%; }\n}\n\n/* ── Main layout ── */\n.game-wrapper {\n display: flex;\n gap: 24px;\n align-items: flex-start;\n}\n\n/* ── Board ── */\n.board-container {\n position: relative;\n}\n\n#game-canvas {\n display: block;\n border: 2px solid rgba(255,255,255,0.15);\n border-radius: 4px;\n box-shadow:\n 0 0 0 1px rgba(255,255,255,0.05),\n 0 0 20px rgba(100,100,255,0.25),\n 0 0 60px rgba(80, 80,200,0.10);\n}\n\n/* ── Side panel ── */\n.side-panel {\n display: flex;\n flex-direction: column;\n gap: 18px;\n width: 150px;\n}\n\n.panel-box {\n background: rgba(255,255,255,0.04);\n border: 1px solid rgba(255,255,255,0.1);\n border-radius: 8px;\n padding: 12px 14px;\n}\n\n.panel-box h2 {\n font-size: 0.65rem;\n letter-spacing: 0.18em;\n text-transform: uppercase;\n color: rgba(200,200,255,0.5);\n margin-bottom: 8px;\n}\n\n/* ── Next piece canvas ── */\n#next-canvas {\n display: block;\n border-radius: 4px;\n margin: 0 auto;\n}\n\n/* ── Stat values ── */\n.stat-value {\n font-size: 1.5rem;\n font-weight: bold;\n color: #fff;\n letter-spacing: 0.05em;\n line-height: 1.1;\n}\n\n/* ── Controls list ── */\n.controls-list {\n list-style: none;\n display: flex;\n flex-direction: column;\n gap: 5px;\n}\n\n.controls-list li {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 0.72rem;\n color: rgba(200,200,255,0.7);\n}\n\nkbd {\n display: inline-block;\n background: rgba(255,255,255,0.1);\n border: 1px solid rgba(255,255,255,0.25);\n border-radius: 4px;\n padding: 1px 5px;\n font-family: inherit;\n font-size: 0.7rem;\n color: #fff;\n min-width: 22px;\n text-align: center;\n flex-shrink: 0;\n}\n\n/* ── Buttons ── */\n.btn-row {\n display: flex;\n gap: 8px;\n}\n\nbutton {\n flex: 1;\n background: rgba(255,255,255,0.07);\n border: 1px solid rgba(255,255,255,0.18);\n border-radius: 6px;\n color: #e0e0ff;\n font-family: inherit;\n font-size: 0.78rem;\n letter-spacing: 0.08em;\n padding: 7px 4px;\n cursor: pointer;\n transition: background 0.15s, border-color 0.15s;\n text-transform: uppercase;\n}\n\nbutton:hover { background: rgba(255,255,255,0.14); border-color: rgba(255,255,255,0.35); }\nbutton:active { background: rgba(255,255,255,0.22); }\n\n/* ── Level colour accent ── */\n.stat-level .stat-value { color: #f0a000; }\n.stat-lines .stat-value { color: #00f0f0; }\n.stat-score .stat-value { color: #a000f0; }\n\n/* ── Responsive shrink for small screens ── */\n@media (max-width: 600px) {\n h1 { font-size: 1.6rem; }\n .game-wrapper { gap: 14px; }\n .side-panel { width: 120px; }\n}\nCSSEOF","description":"Write style.css"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":213,"cache_read_input_tokens":17589,"cache_creation":{"ephemeral_5m_input_tokens":213,"ephemeral_1h_input_tokens":0},"output_tokens":54,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"e5d83350-7265-45d3-aa7a-df351271c724"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01M4C7djB75BWWZNw6Uygzjc","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"aaf4fc44-e015-4417-a7d7-ed70712a9425","timestamp":"2026-04-04T21:00:22.644Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_017i4GmFAiEGMxJNtAZvoM1X","type":"message","role":"assistant","content":[{"type":"text","text":"Now write the TypeScript source:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":1529,"cache_read_input_tokens":17802,"cache_creation":{"ephemeral_5m_input_tokens":1529,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"c18d5f27-261d-4370-a43d-78be96723141"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_018TMiAXhbqaxcp9958uPGEE","type":"message","role":"assistant","content":[{"type":"text","text":"Now let's write the full TypeScript source:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":0,"cache_read_input_tokens":19331,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"fed1468c-90b8-4ba2-8353-5957d871f43d"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_018TMiAXhbqaxcp9958uPGEE","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01F1P8wEJQPE95tKEMp6jJBC","name":"Bash","input":{"command":"cat > /tmp/loop-bench-6vr8i9xe/tetris.ts << 'TSEOF'\n// ─────────────────────────────────────────────\n// Tetris — TypeScript browser implementation\n// ─────────────────────────────────────────────\n\nconst COLS = 10;\nconst ROWS = 20;\nconst CELL = 30; // px per cell on main board\nconst NEXT_CELL = 25; // px per cell in next-piece preview\n\n// ── Colours ────────────────────────────────────\nconst PIECE_COLORS: Record<string, string> = {\n I: '#00f0f0',\n O: '#f0f000',\n T: '#a000f0',\n S: '#00e000',\n Z: '#f00000',\n J: '#0050f0',\n L: '#f0a000',\n};\n\n// ── SRS shape tables ───────────────────────────\n// Each entry is an array of rotation states.\n// 0 = spawn orientation, then CW 1, 180, CCW.\n// 1 inside the matrix means a filled cell.\nconst SHAPES: Record<string, number[][][]> = {\n I: [\n [[0,0,0,0],\n [1,1,1,1],\n [0,0,0,0],\n [0,0,0,0]],\n\n [[0,0,1,0],\n [0,0,1,0],\n [0,0,1,0],\n [0,0,1,0]],\n\n [[0,0,0,0],\n [0,0,0,0],\n [1,1,1,1],\n [0,0,0,0]],\n\n [[0,1,0,0],\n [0,1,0,0],\n [0,1,0,0],\n [0,1,0,0]],\n ],\n O: [\n [[0,1,1,0],\n [0,1,1,0],\n [0,0,0,0],\n [0,0,0,0]],\n ],\n T: [\n [[0,1,0],\n [1,1,1],\n [0,0,0]],\n\n [[0,1,0],\n [0,1,1],\n [0,1,0]],\n\n [[0,0,0],\n [1,1,1],\n [0,1,0]],\n\n [[0,1,0],\n [1,1,0],\n [0,1,0]],\n ],\n S: [\n [[0,1,1],\n [1,1,0],\n [0,0,0]],\n\n [[0,1,0],\n [0,1,1],\n [0,0,1]],\n\n [[0,0,0],\n [0,1,1],\n [1,1,0]],\n\n [[1,0,0],\n [1,1,0],\n [0,1,0]],\n ],\n Z: [\n [[1,1,0],\n [0,1,1],\n [0,0,0]],\n\n [[0,0,1],\n [0,1,1],\n [0,1,0]],\n\n [[0,0,0],\n [1,1,0],\n [0,1,1]],\n\n [[0,1,0],\n [1,1,0],\n [1,0,0]],\n ],\n J: [\n [[1,0,0],\n [1,1,1],\n [0,0,0]],\n\n [[0,1,1],\n [0,1,0],\n [0,1,0]],\n\n [[0,0,0],\n [1,1,1],\n [0,0,1]],\n\n [[0,1,0],\n [0,1,0],\n [1,1,0]],\n ],\n L: [\n [[0,0,1],\n [1,1,1],\n [0,0,0]],\n\n [[0,1,0],\n [0,1,0],\n [0,1,1]],\n\n [[0,0,0],\n [1,1,1],\n [1,0,0]],\n\n [[1,1,0],\n [0,1,0],\n [0,1,0]],\n ],\n};\n\n// ── SRS wall-kick tables ────────────────────────\n// Each row is the list of (dx, dy) offsets to try for a CW rotation\n// starting from that state index. Screen coords: +y = down.\n// Source: Tetris Wiki / SRS (y-axis flipped from wiki math coords).\ntype Kick = [number, number];\n\nconst KICKS_JLSTZ: Kick[][] = [\n // state 0 → 1 (CW)\n [[0,0],[-1,0],[-1,-1],[0,2],[-1,2]],\n // state 1 → 2 (CW)\n [[0,0],[1,0],[1,1],[0,-2],[1,-2]],\n // state 2 → 3 (CW)\n [[0,0],[1,0],[1,-1],[0,2],[1,2]],\n // state 3 → 0 (CW)\n [[0,0],[-1,0],[-1,1],[0,-2],[-1,-2]],\n];\n\nconst KICKS_I: Kick[][] = [\n // state 0 → 1 (CW)\n [[0,0],[-2,0],[1,0],[-2,1],[1,-2]],\n // state 1 → 2 (CW)\n [[0,0],[-1,0],[2,0],[-1,-2],[2,1]],\n // state 2 → 3 (CW)\n [[0,0],[2,0],[-1,0],[2,-1],[-1,2]],\n // state 3 → 0 (CW)\n [[0,0],[1,0],[-2,0],[1,2],[-2,-1]],\n];\n\n// ── Types ──────────────────────────────────────\ntype PieceType = keyof typeof SHAPES;\n\ninterface ActivePiece {\n type : PieceType;\n rot : number; // 0–3\n col : number; // board column of top-left of bounding box\n row : number; // board row of top-left of bounding box\n}\n\ntype Cell = string | null; // null = empty, string = colour\n\n// ── Scoring per lines cleared (× level) ────────\nconst LINE_POINTS = [0, 100, 300, 500, 800];\n\n// ─────────────────────────────────────────────\n// TetrisGame class\n// ─────────────────────────────────────────────\nclass TetrisGame {\n // Canvases\n private readonly boardCanvas : HTMLCanvasElement;\n private readonly boardCtx : CanvasRenderingContext2D;\n private readonly nextCanvas : HTMLCanvasElement;\n private readonly nextCtx : CanvasRenderingContext2D;\n\n // DOM stat elements\n private readonly elScore : HTMLElement;\n private readonly elLines : HTMLElement;\n private readonly elLevel : HTMLElement;\n\n // Game state\n private board : Cell[][];\n private current: ActivePiece | null = null;\n private next : ActivePiece | null = null;\n\n private score : number = 0;\n private lines : number = 0;\n private level : number = 1;\n\n private gameOver: boolean = false;\n private paused : boolean = false;\n\n // Timing\n private rafId : number = 0;\n private lastStamp : number = 0;\n private dropAccum : number = 0; // ms accumulated towards next auto-drop\n\n // DAS (Delayed Auto-Shift) state for smooth key repeat\n private dasKey : string | null = null;\n private dasTimer : number = 0;\n private readonly DAS_DELAY = 170; // ms before repeat starts\n private readonly DAS_PERIOD = 50; // ms between repeats\n\n // ── Bag randomiser (7-bag) ─────────────────\n private bag: PieceType[] = [];\n\n constructor() {\n this.boardCanvas = document.getElementById('game-canvas') as HTMLCanvasElement;\n this.boardCtx = this.boardCanvas.getContext('2d')!;\n this.nextCanvas = document.getElementById('next-canvas') as HTMLCanvasElement;\n this.nextCtx = this.nextCanvas.getContext('2d')!;\n\n this.elScore = document.getElementById('score')!;\n this.elLines = document.getElementById('lines')!;\n this.elLevel = document.getElementById('level')!;\n\n this.board = this.emptyBoard();\n\n // Wire up keyboard\n window.addEventListener('keydown', this.onKeyDown.bind(this));\n window.addEventListener('keyup', this.onKeyUp.bind(this));\n\n // Wire up buttons\n document.getElementById('btn-restart')!\n .addEventListener('click', () => this.restart());\n document.getElementById('btn-pause')!\n .addEventListener('click', () => this.togglePause());\n\n this.spawnNext(); // prepares this.next\n this.spawnPiece(); // moves next → current, prepares new next\n this.loop(0);\n }\n\n // ── Board helpers ──────────────────────────\n private emptyBoard(): Cell[][] {\n return Array.from({ length: ROWS }, () => Array<Cell>(COLS).fill(null));\n }\n\n // ── Bag randomiser ─────────────────────────\n private pullFromBag(): PieceType {\n if (this.bag.length === 0) {\n const types = Object.keys(SHAPES) as PieceType[];\n // Fisher-Yates shuffle\n for (let i = types.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [types[i], types[j]] = [types[j], types[i]];\n }\n this.bag = types;\n }\n return this.bag.pop()!;\n }\n\n private makePiece(type: PieceType): ActivePiece {\n // Centre-spawn: for 3-wide pieces col=3, for 4-wide pieces col=3 too\n return { type, rot: 0, col: 3, row: 0 };\n }\n\n private spawnNext(): void {\n this.next = this.makePiece(this.pullFromBag());\n this.drawNext();\n }\n\n private spawnPiece(): void {\n this.current = this.next ?? this.makePiece(this.pullFromBag());\n this.spawnNext();\n\n // Nudge piece so it isn't partially hidden above board\n // (the I-piece row-0 is blank in spawn orientation — push it up by 1)\n if (this.current.type === 'I') {\n this.current.row = -1;\n }\n\n if (!this.fits(this.current, 0, 0)) {\n this.gameOver = true;\n }\n }\n\n // ── Shape helpers ──────────────────────────\n private shape(p: ActivePiece): number[][] {\n const states = SHAPES[p.type];\n return states[p.rot % states.length];\n }\n\n /** Returns the list of {r, c} filled cells for a piece. */\n private cells(p: ActivePiece): Array<{r: number; c: number}> {\n const out: Array<{r: number; c: number}> = [];\n const s = this.shape(p);\n for (let r = 0; r < s.length; r++) {\n for (let c = 0; c < s[r].length; c++) {\n if (s[r][c]) out.push({ r: p.row + r, c: p.col + c });\n }\n }\n return out;\n }\n\n /** True when the piece (plus offset) fits on the board without collision. */\n private fits(p: ActivePiece, dc: number, dr: number, rot?: number): boolean {\n const test: ActivePiece = { ...p, col: p.col + dc, row: p.row + dr };\n if (rot !== undefined) test.rot = rot;\n for (const { r, c } of this.cells(test)) {\n if (c < 0 || c >= COLS || r >= ROWS) return false;\n if (r >= 0 && this.board[r][c] !== null) return false;\n }\n return true;\n }\n\n // ── Ghost piece ────────────────────────────\n private ghostRow(): number {\n if (!this.current) return 0;\n let dr = 0;\n while (this.fits(this.current, 0, dr + 1)) dr++;\n return this.current.row + dr;\n }\n\n // ── Movement ──────────────────────────────\n private moveLeft () : void { if (this.current && this.fits(this.current, -1, 0)) this.current.col--; }\n private moveRight() : void { if (this.current && this.fits(this.current, 1, 0)) this.current.col++; }\n\n private softDrop(): void {\n if (!this.current) return;\n if (this.fits(this.current, 0, 1)) {\n this.current.row++;\n this.score += 1;\n this.dropAccum = 0;\n this.updateStats();\n } else {\n this.lock();\n }\n }\n\n private hardDrop(): void {\n if (!this.current) return;\n let dropped = 0;\n while (this.fits(this.current, 0, 1)) {\n this.current.row++;\n dropped++;\n }\n this.score += dropped * 2;\n this.updateStats();\n this.lock();\n }\n\n private rotate(dir: 1 | -1): void {\n if (!this.current) return;\n const states = SHAPES[this.current.type].length;\n const oldRot = this.current.rot;\n const newRot = ((oldRot + dir) % states + states) % states;\n\n const kickTable = this.current.type === 'I' ? KICKS_I : KICKS_JLSTZ;\n\n // For CW use kicks[oldRot]; for CCW negate kicks[newRot]\n let kicks: Kick[];\n if (dir === 1) {\n kicks = kickTable[oldRot % kickTable.length];\n } else {\n kicks = kickTable[newRot % kickTable.length].map(([dx, dy]) => [-dx, -dy] as Kick);\n }\n\n for (const [dx, dy] of kicks) {\n if (this.fits(this.current, dx, dy, newRot)) {\n this.current.col += dx;\n this.current.row += dy;\n this.current.rot = newRot;\n return;\n }\n }\n // No kick succeeded — rotation blocked\n }\n\n // ── Locking ───────────────────────────────\n private lock(): void {\n if (!this.current) return;\n\n for (const { r, c } of this.cells(this.current)) {\n if (r >= 0) this.board[r][c] = PIECE_COLORS[this.current.type];\n }\n\n this.clearLines();\n this.spawnPiece();\n this.dropAccum = 0;\n }\n\n private clearLines(): void {\n let cleared = 0;\n for (let r = ROWS - 1; r >= 0; r--) {\n if (this.board[r].every(c => c !== null)) {\n this.board.splice(r, 1);\n this.board.unshift(Array<Cell>(COLS).fill(null));\n cleared++;\n r++; // re-check same index after splice\n }\n }\n if (cleared === 0) return;\n\n this.score += LINE_POINTS[cleared] * this.level;\n this.lines += cleared;\n this.level = Math.floor(this.lines / 10) + 1;\n this.updateStats();\n }\n\n // ── Drop interval (ms) by level ───────────\n private dropInterval(): number {\n // Levels 1–20, each level ~12% faster, floor at 50 ms\n return Math.max(50, Math.round(1000 * Math.pow(0.85, this.level - 1)));\n }\n\n // ── Stats DOM update ──────────────────────\n private updateStats(): void {\n this.elScore.textContent = this.score.toString();\n this.elLines.textContent = this.lines.toString();\n this.elLevel.textContent = this.level.toString();\n }\n\n // ── Public controls ───────────────────────\n togglePause(): void {\n if (this.gameOver) return;\n this.paused = !this.paused;\n const btn = document.getElementById('btn-pause');\n if (btn) btn.textContent = this.paused ? 'Resume' : 'Pause';\n }\n\n restart(): void {\n this.board = this.emptyBoard();\n this.current = null;\n this.next = null;\n this.bag = [];\n this.score = 0;\n this.lines = 0;\n this.level = 1;\n this.dropAccum = 0;\n this.gameOver = false;\n this.paused = false;\n this.dasKey = null;\n this.updateStats();\n const btn = document.getElementById('btn-pause');\n if (btn) btn.textContent = 'Pause';\n this.spawnNext();\n this.spawnPiece();\n }\n\n // ── Keyboard handling ─────────────────────\n private onKeyDown(e: KeyboardEvent): void {\n // Always allow restart / pause\n if (e.key === 'r' || e.key === 'R') { this.restart(); return; }\n if (e.key === 'p' || e.key === 'P') { this.togglePause(); return; }\n if (e.key === 'Escape') { this.togglePause(); return; }\n\n if (this.gameOver || this.paused) return;\n\n switch (e.key) {\n case 'ArrowLeft':\n e.preventDefault();\n this.moveLeft();\n this.startDAS('ArrowLeft');\n break;\n case 'ArrowRight':\n e.preventDefault();\n this.moveRight();\n this.startDAS('ArrowRight');\n break;\n case 'ArrowDown':\n e.preventDefault();\n this.softDrop();\n this.startDAS('ArrowDown');\n break;\n case 'ArrowUp':\n e.preventDefault();\n this.rotate(1);\n break;\n case 'x': case 'X':\n this.rotate(1); // CW\n break;\n case 'z': case 'Z':\n this.rotate(-1); // CCW\n break;\n case 'Control':\n this.rotate(-1); // CCW (alternative)\n break;\n case ' ':\n e.preventDefault();\n this.hardDrop();\n break;\n }\n }\n\n private onKeyUp(e: KeyboardEvent): void {\n if (this.dasKey === e.key) this.dasKey = null;\n }\n\n private startDAS(key: string): void {\n if (this.dasKey === key) return;\n this.dasKey = key;\n this.dasTimer = this.DAS_DELAY;\n }\n\n private tickDAS(dt: number): void {\n if (!this.dasKey || this.paused || this.gameOver) return;\n this.dasTimer -= dt;\n if (this.dasTimer <= 0) {\n this.dasTimer += this.DAS_PERIOD;\n switch (this.dasKey) {\n case 'ArrowLeft': this.moveLeft(); break;\n case 'ArrowRight': this.moveRight(); break;\n case 'ArrowDown': this.softDrop(); break;\n }\n }\n }\n\n // ── Main loop ─────────────────────────────\n private loop(stamp: number): void {\n const dt = Math.min(stamp - this.lastStamp, 100); // cap at 100ms\n this.lastStamp = stamp;\n\n if (!this.paused && !this.gameOver) {\n this.dropAccum += dt;\n if (this.dropAccum >= this.dropInterval()) {\n this.dropAccum -= this.dropInterval();\n if (this.current) {\n if (this.fits(this.current, 0, 1)) {\n this.current.row++;\n } else {\n this.lock();\n }\n }\n }\n this.tickDAS(dt);\n }\n\n this.render();\n this.rafId = requestAnimationFrame(this.loop.bind(this));\n }\n\n // ─────────────────────────────────────────\n // Rendering\n // ─────────────────────────────────────────\n private render(): void {\n const ctx = this.boardCtx;\n const W = this.boardCanvas.width;\n const H = this.boardCanvas.height;\n\n // Background\n ctx.fillStyle = '#0a0a1a';\n ctx.fillRect(0, 0, W, H);\n\n // Subtle grid\n ctx.strokeStyle = 'rgba(255,255,255,0.04)';\n ctx.lineWidth = 1;\n for (let c = 0; c <= COLS; c++) {\n ctx.beginPath(); ctx.moveTo(c * CELL, 0); ctx.lineTo(c * CELL, H); ctx.stroke();\n }\n for (let r = 0; r <= ROWS; r++) {\n ctx.beginPath(); ctx.moveTo(0, r * CELL); ctx.lineTo(W, r * CELL); ctx.stroke();\n }\n\n // Locked cells\n for (let r = 0; r < ROWS; r++) {\n for (let c = 0; c < COLS; c++) {\n const col = this.board[r][c];\n if (col) this.drawCell(ctx, c, r, col, CELL);\n }\n }\n\n // Ghost piece\n if (this.current && !this.gameOver) {\n const gr = this.ghostRow();\n const ghost: ActivePiece = { ...this.current, row: gr };\n for (const { r, c } of this.cells(ghost)) {\n if (r < 0) continue;\n ctx.strokeStyle = PIECE_COLORS[this.current.type];\n ctx.lineWidth = 1.5;\n ctx.globalAlpha = 0.3;\n ctx.strokeRect(c * CELL + 2, r * CELL + 2, CELL - 4, CELL - 4);\n ctx.globalAlpha = 1;\n }\n }\n\n // Active piece\n if (this.current && !this.gameOver) {\n const col = PIECE_COLORS[this.current.type];\n for (const { r, c } of this.cells(this.current)) {\n if (r < 0) continue;\n this.drawCell(ctx, c, r, col, CELL);\n }\n }\n\n // Overlays\n if (this.paused) {\n this.drawOverlay(ctx, W, H, 'PAUSED', '#ffffff', 'Press P to resume');\n } else if (this.gameOver) {\n this.drawOverlay(ctx, W, H, 'GAME OVER', '#ff4455', `Score: ${this.score} · Press R to restart`);\n }\n }\n\n private drawCell(\n ctx : CanvasRenderingContext2D,\n col : number, row: number,\n color: string,\n size : number,\n ): void {\n const x = col * size;\n const y = row * size;\n\n // Main fill\n ctx.fillStyle = color;\n ctx.fillRect(x + 1, y + 1, size - 2, size - 2);\n\n // Bright top-left bevel\n ctx.fillStyle = 'rgba(255,255,255,0.30)';\n ctx.fillRect(x + 1, y + 1, size - 2, 4);\n ctx.fillRect(x + 1, y + 1, 4, size - 2);\n\n // Dark bottom-right bevel\n ctx.fillStyle = 'rgba(0,0,0,0.35)';\n ctx.fillRect(x + 1, y + size - 5, size - 2, 4);\n ctx.fillRect(x + size - 5, y + 1, 4, size - 2);\n\n // Inner highlight\n ctx.fillStyle = 'rgba(255,255,255,0.12)';\n ctx.fillRect(x + 5, y + 5, size - 10, size - 10);\n }\n\n private drawOverlay(\n ctx : CanvasRenderingContext2D,\n W : number, H: number,\n title : string,\n titleColor: string,\n subtitle: string,\n ): void {\n ctx.fillStyle = 'rgba(0,0,0,0.72)';\n ctx.fillRect(0, 0, W, H);\n\n ctx.textAlign = 'center';\n ctx.textBaseline = 'middle';\n\n ctx.fillStyle = titleColor;\n ctx.font = `bold ${Math.round(CELL * 1.2)}px \"Courier New\", monospace`;\n ctx.fillText(title, W / 2, H / 2 - CELL);\n\n ctx.fillStyle = 'rgba(220,220,255,0.80)';\n ctx.font = `${Math.round(CELL * 0.5)}px \"Courier New\", monospace`;\n ctx.fillText(subtitle, W / 2, H / 2 + CELL * 0.4);\n }\n\n // ── Next-piece preview ────────────────────\n private drawNext(): void {\n const ctx = this.nextCtx;\n const W = this.nextCanvas.width;\n const H = this.nextCanvas.height;\n\n ctx.fillStyle = '#0a0a1a';\n ctx.fillRect(0, 0, W, H);\n\n if (!this.next) return;\n\n const s = this.shape(this.next);\n const rows = s.length;\n const cols = s[0].length;\n const color = PIECE_COLORS[this.next.type];\n\n const ox = Math.floor((W / NEXT_CELL - cols) / 2);\n const oy = Math.floor((H / NEXT_CELL - rows) / 2);\n\n for (let r = 0; r < rows; r++) {\n for (let c = 0; c < cols; c++) {\n if (s[r][c]) {\n this.drawCell(ctx, ox + c, oy + r, color, NEXT_CELL);\n }\n }\n }\n }\n}\n\n// ── Bootstrap ────────────────────────────────\nwindow.addEventListener('DOMContentLoaded', () => {\n new TetrisGame();\n});\nTSEOF","description":"Write tetris.ts (full game logic)"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":0,"cache_read_input_tokens":19331,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"3f3419c8-31dd-4503-b568-258604b9f001"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01F1P8wEJQPE95tKEMp6jJBC","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"fa85acb7-4c5a-4bde-b297-e1baccde3a31","timestamp":"2026-04-04T21:03:09.542Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_015gAz6nFDBzwkd5YooT9j6n","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01KZb13EsGPLvXzs1Syh5BrS","name":"Bash","input":{"command":"cat > /tmp/loop-bench-6vr8i9xe/index.html << 'HTMLEOF'\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Tetris</title>\n <link rel=\"stylesheet\" href=\"style.css\" />\n</head>\n<body>\n\n <h1>TETRIS</h1>\n\n <div class=\"game-wrapper\">\n\n <!-- ── Main board ── -->\n <div class=\"board-container\">\n <canvas id=\"game-canvas\"\n width=\"300\"\n height=\"600\"\n aria-label=\"Tetris game board\"></canvas>\n </div>\n\n <!-- ── Side panel ── -->\n <div class=\"side-panel\">\n\n <!-- Next piece -->\n <div class=\"panel-box\">\n <h2>Next</h2>\n <canvas id=\"next-canvas\" width=\"125\" height=\"100\"></canvas>\n </div>\n\n <!-- Score -->\n <div class=\"panel-box stat-score\">\n <h2>Score</h2>\n <div class=\"stat-value\" id=\"score\">0</div>\n </div>\n\n <!-- Lines -->\n <div class=\"panel-box stat-lines\">\n <h2>Lines</h2>\n <div class=\"stat-value\" id=\"lines\">0</div>\n </div>\n\n <!-- Level -->\n <div class=\"panel-box stat-level\">\n <h2>Level</h2>\n <div class=\"stat-value\" id=\"level\">1</div>\n </div>\n\n <!-- Buttons -->\n <div class=\"panel-box\">\n <div class=\"btn-row\">\n <button id=\"btn-pause\">Pause</button>\n <button id=\"btn-restart\">Restart</button>\n </div>\n </div>\n\n <!-- Controls -->\n <div class=\"panel-box\">\n <h2>Controls</h2>\n <ul class=\"controls-list\">\n <li><kbd>◀▶</kbd> Move</li>\n <li><kbd>▼</kbd> Soft drop</li>\n <li><kbd>Space</kbd> Hard drop</li>\n <li><kbd>▲</kbd> Rotate CW</li>\n <li><kbd>Z</kbd> Rotate CCW</li>\n <li><kbd>P</kbd> Pause</li>\n <li><kbd>R</kbd> Restart</li>\n </ul>\n </div>\n\n </div><!-- /side-panel -->\n </div><!-- /game-wrapper -->\n\n <script src=\"tetris.js\"></script>\n</body>\n</html>\nHTMLEOF","description":"Write index.html"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":7286,"cache_read_input_tokens":19331,"cache_creation":{"ephemeral_5m_input_tokens":7286,"ephemeral_1h_input_tokens":0},"output_tokens":38,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"81a5fa4b-c222-422c-96cf-af753a7f97dc"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01KZb13EsGPLvXzs1Syh5BrS","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"370c88d0-bcb8-405b-9190-690d48881deb","timestamp":"2026-04-04T21:03:19.121Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_019gscDMEnZHnskn3deBrvUp","type":"message","role":"assistant","content":[{"type":"text","text":"Now compile the TypeScript:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":794,"cache_read_input_tokens":26617,"cache_creation":{"ephemeral_5m_input_tokens":794,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"499285dd-7184-4cd6-8c22-cd3d15ec0d3f"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_019gscDMEnZHnskn3deBrvUp","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_015aaCDXB5EKEuZmpUTuT4AQ","name":"Bash","input":{"command":"tsc --project tsconfig.json 2>&1","description":"Compile tetris.ts → tetris.js"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":794,"cache_read_input_tokens":26617,"cache_creation":{"ephemeral_5m_input_tokens":794,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"fb62893d-ed5a-4189-b502-7e5664c74770"} +{"type":"user","message":{"role":"user","content":[{"type":"tool_result","content":"Exit code 2\nnode_modules/@types/node/http.d.ts(2176,36): error TS2792: Cannot find module 'undici-types'. Did you mean to set the 'moduleResolution' option to 'nodenext', or to add aliases to the 'paths' option?\nnode_modules/@types/node/http.d.ts(2180,37): error TS2792: Cannot find module 'undici-types'. Did you mean to set the 'moduleResolution' option to 'nodenext', or to add aliases to the 'paths' option?\nnode_modules/@types/node/http.d.ts(2184,39): error TS2792: Cannot find module 'undici-types'. Did you mean to set the 'moduleResolution' option to 'nodenext', or to add aliases to the 'paths' option?\nnode_modules/@types/node/web-globals/fetch.d.ts(3,25): error TS2792: Cannot find module 'undici-types'. Did you mean to set the 'moduleResolution' option to 'nodenext', or to add aliases to the 'paths' option?\nnode_modules/@types/node/worker_threads.d.ts(71,34): error TS2792: Cannot find module 'undici-types'. Did you mean to set the 'moduleResolution' option to 'nodenext', or to add aliases to the 'paths' option?","is_error":true,"tool_use_id":"toolu_015aaCDXB5EKEuZmpUTuT4AQ"}]},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"a82eea1e-fefd-4f44-b98d-6da8a9082e2f","timestamp":"2026-04-04T21:03:23.135Z","tool_use_result":"Error: Exit code 2\nnode_modules/@types/node/http.d.ts(2176,36): error TS2792: Cannot find module 'undici-types'. Did you mean to set the 'moduleResolution' option to 'nodenext', or to add aliases to the 'paths' option?\nnode_modules/@types/node/http.d.ts(2180,37): error TS2792: Cannot find module 'undici-types'. Did you mean to set the 'moduleResolution' option to 'nodenext', or to add aliases to the 'paths' option?\nnode_modules/@types/node/http.d.ts(2184,39): error TS2792: Cannot find module 'undici-types'. Did you mean to set the 'moduleResolution' option to 'nodenext', or to add aliases to the 'paths' option?\nnode_modules/@types/node/web-globals/fetch.d.ts(3,25): error TS2792: Cannot find module 'undici-types'. Did you mean to set the 'moduleResolution' option to 'nodenext', or to add aliases to the 'paths' option?\nnode_modules/@types/node/worker_threads.d.ts(71,34): error TS2792: Cannot find module 'undici-types'. Did you mean to set the 'moduleResolution' option to 'nodenext', or to add aliases to the 'paths' option?"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_019JtzUr3BHsVavomvTKS4zg","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"The errors are from `@types/node` conflicting. Let me exclude `node_modules` and only include the `DOM` lib (not `@types/node`). I'll update the `tsconfig.json` to explicitly skip node types.","signature":"Ev0CClsIDBgCKkB5LQG0zZyUVcYrphy/drHLPbKegYXn6y537Ipu7QL8o7Pt5Iv12GQ4YeQfwHa45sMX2R2ZDt8bm57rSOH4UhGNMhFjbGF1ZGUtc29ubmV0LTQtNjgAEgyIh+o+keJWemSTVm0aDH0iW3fkzsmT98NOeSIwvNzbq5QGsWBjGS8HCKn6A7ZmiekDvQRVcC7SOKF/Y6rVdtEWC56RMRc/XtrsRLbUKs8B25yfMzg4KhqfHoOwiUVPioGaQi4F0F9W/i7DC0etL4aGBVcgLRgnlvyzjGnF8hNHuD85xy5JKayXtlVTykWdtqxbVzkdwFOMOfhhIm2c4wBzpSrmbOPOPy7VS4dZR9N9UpjZsIVuN/zFDeKLe/xD4FdVhbXgc3v+qw+cnd1rYxIdo+AzKWqzqsxi40YSx6EIZgDKfxKYie42xfjkwqFio3sDq25YR8TSSuKzkJe3B2Wmg+az2QUm5CdQ66Koa8WREgwd5MAdbMIu6q6iqNXLGAE="}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":465,"cache_read_input_tokens":27411,"cache_creation":{"ephemeral_5m_input_tokens":465,"ephemeral_1h_input_tokens":0},"output_tokens":8,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"23466160-8f7b-4a72-8a8a-c97d48191a1c"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_019JtzUr3BHsVavomvTKS4zg","type":"message","role":"assistant","content":[{"type":"text","text":"The errors are from `@types/node` in the project. Let me fix the tsconfig to skip Node types:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":465,"cache_read_input_tokens":27411,"cache_creation":{"ephemeral_5m_input_tokens":465,"ephemeral_1h_input_tokens":0},"output_tokens":8,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"7ecd4054-85a1-4bba-bcab-84afdea20f88"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_019JtzUr3BHsVavomvTKS4zg","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01P6DoEJUWhFtT2yBeTTus2M","name":"Edit","input":{"replace_all":false,"file_path":"/tmp/loop-bench-6vr8i9xe/tsconfig.json","old_string":"{\n \"compilerOptions\": {\n \"target\": \"ES2017\",\n \"lib\": [\"ES2017\", \"DOM\"],\n \"module\": \"None\",\n \"outFile\": \"tetris.js\",\n \"strict\": true,\n \"noImplicitAny\": true,\n \"sourceMap\": true\n },\n \"include\": [\"tetris.ts\"]\n}","new_string":"{\n \"compilerOptions\": {\n \"target\": \"ES2017\",\n \"lib\": [\"ES2017\", \"DOM\"],\n \"module\": \"None\",\n \"outFile\": \"tetris.js\",\n \"strict\": true,\n \"noImplicitAny\": true,\n \"sourceMap\": true,\n \"types\": []\n },\n \"include\": [\"tetris.ts\"],\n \"exclude\": [\"node_modules\"]\n}"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":465,"cache_read_input_tokens":27411,"cache_creation":{"ephemeral_5m_input_tokens":465,"ephemeral_1h_input_tokens":0},"output_tokens":8,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"80d8e30c-4174-4cfe-a10b-fee69987a94d"} +{"type":"user","message":{"role":"user","content":[{"type":"tool_result","content":"<tool_use_error>File has not been read yet. Read it first before writing to it.</tool_use_error>","is_error":true,"tool_use_id":"toolu_01P6DoEJUWhFtT2yBeTTus2M"}]},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"a3e9e58f-636e-4d25-902e-80f618379a63","timestamp":"2026-04-04T21:03:30.677Z","tool_use_result":"Error: File has not been read yet. Read it first before writing to it."} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01XGJYLAmsWtQCP2Vy3YPArT","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01JRwvJ54SGu1j7PSNgubTg8","name":"Read","input":{"file_path":"/tmp/loop-bench-6vr8i9xe/tsconfig.json"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":470,"cache_read_input_tokens":27876,"cache_creation":{"ephemeral_5m_input_tokens":470,"ephemeral_1h_input_tokens":0},"output_tokens":71,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"c0241a6c-7123-426a-ba56-5489ec96452a"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01JRwvJ54SGu1j7PSNgubTg8","type":"tool_result","content":"1\t{\n2\t \"compilerOptions\": {\n3\t \"target\": \"ES2017\",\n4\t \"lib\": [\"ES2017\", \"DOM\"],\n5\t \"module\": \"None\",\n6\t \"outFile\": \"tetris.js\",\n7\t \"strict\": true,\n8\t \"noImplicitAny\": true,\n9\t \"sourceMap\": true\n10\t },\n11\t \"include\": [\"tetris.ts\"]\n12\t}\n13\t"}]},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"bc6338fd-a4e2-457b-893b-3fa7bd4bb3a5","timestamp":"2026-04-04T21:03:32.293Z","tool_use_result":{"type":"text","file":{"filePath":"/tmp/loop-bench-6vr8i9xe/tsconfig.json","content":"{\n \"compilerOptions\": {\n \"target\": \"ES2017\",\n \"lib\": [\"ES2017\", \"DOM\"],\n \"module\": \"None\",\n \"outFile\": \"tetris.js\",\n \"strict\": true,\n \"noImplicitAny\": true,\n \"sourceMap\": true\n },\n \"include\": [\"tetris.ts\"]\n}\n","numLines":13,"startLine":1,"totalLines":13}}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01SC2rrLbcKLi9oEj8wj3Jxe","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01WchsPSm1LYX9iWPtWgRQbr","name":"Edit","input":{"replace_all":false,"file_path":"/tmp/loop-bench-6vr8i9xe/tsconfig.json","old_string":"{\n \"compilerOptions\": {\n \"target\": \"ES2017\",\n \"lib\": [\"ES2017\", \"DOM\"],\n \"module\": \"None\",\n \"outFile\": \"tetris.js\",\n \"strict\": true,\n \"noImplicitAny\": true,\n \"sourceMap\": true\n },\n \"include\": [\"tetris.ts\"]\n}","new_string":"{\n \"compilerOptions\": {\n \"target\": \"ES2017\",\n \"lib\": [\"ES2017\", \"DOM\"],\n \"module\": \"None\",\n \"outFile\": \"tetris.js\",\n \"strict\": true,\n \"noImplicitAny\": true,\n \"sourceMap\": true,\n \"types\": []\n },\n \"include\": [\"tetris.ts\"],\n \"exclude\": [\"node_modules\"]\n}"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":674,"cache_read_input_tokens":27876,"cache_creation":{"ephemeral_5m_input_tokens":674,"ephemeral_1h_input_tokens":0},"output_tokens":66,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"0167214e-48b4-45a8-8386-17048c913f9d"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01WchsPSm1LYX9iWPtWgRQbr","type":"tool_result","content":"The file /tmp/loop-bench-6vr8i9xe/tsconfig.json has been updated successfully."}]},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"177e4a28-fb3f-4c30-9852-a99e44889e9e","timestamp":"2026-04-04T21:03:35.664Z","tool_use_result":{"filePath":"/tmp/loop-bench-6vr8i9xe/tsconfig.json","oldString":"{\n \"compilerOptions\": {\n \"target\": \"ES2017\",\n \"lib\": [\"ES2017\", \"DOM\"],\n \"module\": \"None\",\n \"outFile\": \"tetris.js\",\n \"strict\": true,\n \"noImplicitAny\": true,\n \"sourceMap\": true\n },\n \"include\": [\"tetris.ts\"]\n}","newString":"{\n \"compilerOptions\": {\n \"target\": \"ES2017\",\n \"lib\": [\"ES2017\", \"DOM\"],\n \"module\": \"None\",\n \"outFile\": \"tetris.js\",\n \"strict\": true,\n \"noImplicitAny\": true,\n \"sourceMap\": true,\n \"types\": []\n },\n \"include\": [\"tetris.ts\"],\n \"exclude\": [\"node_modules\"]\n}","originalFile":"{\n \"compilerOptions\": {\n \"target\": \"ES2017\",\n \"lib\": [\"ES2017\", \"DOM\"],\n \"module\": \"None\",\n \"outFile\": \"tetris.js\",\n \"strict\": true,\n \"noImplicitAny\": true,\n \"sourceMap\": true\n },\n \"include\": [\"tetris.ts\"]\n}\n","structuredPatch":[{"oldStart":6,"oldLines":7,"newStart":6,"newLines":9,"lines":[" \"outFile\": \"tetris.js\","," \"strict\": true,"," \"noImplicitAny\": true,","- \"sourceMap\": true","+ \"sourceMap\": true,","+ \"types\": []"," },","- \"include\": [\"tetris.ts\"]","+ \"include\": [\"tetris.ts\"],","+ \"exclude\": [\"node_modules\"]"," }"]}],"userModified":false,"replaceAll":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01Ju4LT7JLTdXUHLrtMsaEsa","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01NicRTQQpdDK4AVcuoGRVzC","name":"Bash","input":{"command":"tsc --project tsconfig.json 2>&1","description":"Compile TypeScript again with fixed tsconfig"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":366,"cache_read_input_tokens":28550,"cache_creation":{"ephemeral_5m_input_tokens":366,"ephemeral_1h_input_tokens":0},"output_tokens":65,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"06f83be5-3728-49c4-bb50-f2d1cdc4dd70"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01NicRTQQpdDK4AVcuoGRVzC","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"d94b6cd2-5024-4af9-a9ab-e17bdb9cbcde","timestamp":"2026-04-04T21:03:42.352Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01MC2Qff5s33WLD5rrbvhSsv","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"No errors! Let me verify the output file was created.","signature":"EvIBClsIDBgCKkBcc3fiiTe3DMn1dEYOS+wKFOS738Pc7HMIAewYxz//bwz4r2s4Hs7xjKpWC2zkEx/VxHqapk1tJIej0AEclBugMhFjbGF1ZGUtc29ubmV0LTQtNjgAEgxaWTp2Y20NYsVjCFcaDP+qGxWY4cOckTIvuCIwn8jOoLZXY63QRBfVJOGg0huZkNLRJtlqEKaJMnSOqPCDc3G+KIANWh86xGYs+MmnKkUeISuecGwARYGBofCipNmsBb4Dt4AhON5vJaJ4U+AEhozuOsuwGuABE5RXAYM0fqgbA6AqDspWjrxpmu5Dd2Jkr6JN8yAYAQ=="}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":472,"cache_read_input_tokens":28550,"cache_creation":{"ephemeral_5m_input_tokens":472,"ephemeral_1h_input_tokens":0},"output_tokens":8,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"3a3c1b96-1b17-4de5-b7c4-189a551da7bc"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01MC2Qff5s33WLD5rrbvhSsv","type":"message","role":"assistant","content":[{"type":"text","text":"Clean compile. Let me verify the output and the file list:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":472,"cache_read_input_tokens":28550,"cache_creation":{"ephemeral_5m_input_tokens":472,"ephemeral_1h_input_tokens":0},"output_tokens":8,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"03ab2a1d-135b-4cd7-ae1e-9e0678357778"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01MC2Qff5s33WLD5rrbvhSsv","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01UNNzaR5D93rhf6vZGz2ecD","name":"Bash","input":{"command":"ls -lh /tmp/loop-bench-6vr8i9xe/*.html /tmp/loop-bench-6vr8i9xe/*.css /tmp/loop-bench-6vr8i9xe/*.ts /tmp/loop-bench-6vr8i9xe/*.js 2>/dev/null","description":"List generated files"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":472,"cache_read_input_tokens":28550,"cache_creation":{"ephemeral_5m_input_tokens":472,"ephemeral_1h_input_tokens":0},"output_tokens":8,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"a1abb54d-12aa-4ede-a2ce-ce6e93be1e4c"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01UNNzaR5D93rhf6vZGz2ecD","type":"tool_result","content":"-rw-r--r-- 1 root root 1.9K Apr 4 23:03 /tmp/loop-bench-6vr8i9xe/index.html\n-rw-r--r-- 1 root root 3.4K Apr 4 23:00 /tmp/loop-bench-6vr8i9xe/style.css\n-rw-r--r-- 1 root root 21K Apr 4 23:03 /tmp/loop-bench-6vr8i9xe/tetris.js\n-rw-r--r-- 1 root root 20K Apr 4 23:03 /tmp/loop-bench-6vr8i9xe/tetris.ts","is_error":false}]},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"e7202181-5f6a-4945-abe5-c3738d12e16a","timestamp":"2026-04-04T21:03:46.572Z","tool_use_result":{"stdout":"-rw-r--r-- 1 root root 1.9K Apr 4 23:03 /tmp/loop-bench-6vr8i9xe/index.html\n-rw-r--r-- 1 root root 3.4K Apr 4 23:00 /tmp/loop-bench-6vr8i9xe/style.css\n-rw-r--r-- 1 root root 21K Apr 4 23:03 /tmp/loop-bench-6vr8i9xe/tetris.js\n-rw-r--r-- 1 root root 20K Apr 4 23:03 /tmp/loop-bench-6vr8i9xe/tetris.ts","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_011kk6ZrW5riNXLT5qBbMnUt","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01Jx4f5Y8gzv12h1yNUoXkqq","name":"Read","input":{"file_path":"/tmp/loop-bench-6vr8i9xe/tetris.js","limit":50},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":374,"cache_read_input_tokens":29022,"cache_creation":{"ephemeral_5m_input_tokens":374,"ephemeral_1h_input_tokens":0},"output_tokens":66,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"6077f00d-cf54-4309-9d07-1006b50dde8f"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01Jx4f5Y8gzv12h1yNUoXkqq","type":"tool_result","content":"1\t\"use strict\";\n2\t// ─────────────────────────────────────────────\n3\t// Tetris — TypeScript browser implementation\n4\t// ─────────────────────────────────────────────\n5\tconst COLS = 10;\n6\tconst ROWS = 20;\n7\tconst CELL = 30; // px per cell on main board\n8\tconst NEXT_CELL = 25; // px per cell in next-piece preview\n9\t// ── Colours ────────────────────────────────────\n10\tconst PIECE_COLORS = {\n11\t I: '#00f0f0',\n12\t O: '#f0f000',\n13\t T: '#a000f0',\n14\t S: '#00e000',\n15\t Z: '#f00000',\n16\t J: '#0050f0',\n17\t L: '#f0a000',\n18\t};\n19\t// ── SRS shape tables ───────────────────────────\n20\t// Each entry is an array of rotation states.\n21\t// 0 = spawn orientation, then CW 1, 180, CCW.\n22\t// 1 inside the matrix means a filled cell.\n23\tconst SHAPES = {\n24\t I: [\n25\t [[0, 0, 0, 0],\n26\t [1, 1, 1, 1],\n27\t [0, 0, 0, 0],\n28\t [0, 0, 0, 0]],\n29\t [[0, 0, 1, 0],\n30\t [0, 0, 1, 0],\n31\t [0, 0, 1, 0],\n32\t [0, 0, 1, 0]],\n33\t [[0, 0, 0, 0],\n34\t [0, 0, 0, 0],\n35\t [1, 1, 1, 1],\n36\t [0, 0, 0, 0]],\n37\t [[0, 1, 0, 0],\n38\t [0, 1, 0, 0],\n39\t [0, 1, 0, 0],\n40\t [0, 1, 0, 0]],\n41\t ],\n42\t O: [\n43\t [[0, 1, 1, 0],\n44\t [0, 1, 1, 0],\n45\t [0, 0, 0, 0],\n46\t [0, 0, 0, 0]],\n47\t ],\n48\t T: [\n49\t [[0, 1, 0],\n50\t [1, 1, 1],"}]},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"aaa9d286-60bb-471a-9e1d-438907e7c265","timestamp":"2026-04-04T21:03:48.484Z","tool_use_result":{"type":"text","file":{"filePath":"/tmp/loop-bench-6vr8i9xe/tetris.js","content":"\"use strict\";\n// ─────────────────────────────────────────────\n// Tetris — TypeScript browser implementation\n// ─────────────────────────────────────────────\nconst COLS = 10;\nconst ROWS = 20;\nconst CELL = 30; // px per cell on main board\nconst NEXT_CELL = 25; // px per cell in next-piece preview\n// ── Colours ────────────────────────────────────\nconst PIECE_COLORS = {\n I: '#00f0f0',\n O: '#f0f000',\n T: '#a000f0',\n S: '#00e000',\n Z: '#f00000',\n J: '#0050f0',\n L: '#f0a000',\n};\n// ── SRS shape tables ───────────────────────────\n// Each entry is an array of rotation states.\n// 0 = spawn orientation, then CW 1, 180, CCW.\n// 1 inside the matrix means a filled cell.\nconst SHAPES = {\n I: [\n [[0, 0, 0, 0],\n [1, 1, 1, 1],\n [0, 0, 0, 0],\n [0, 0, 0, 0]],\n [[0, 0, 1, 0],\n [0, 0, 1, 0],\n [0, 0, 1, 0],\n [0, 0, 1, 0]],\n [[0, 0, 0, 0],\n [0, 0, 0, 0],\n [1, 1, 1, 1],\n [0, 0, 0, 0]],\n [[0, 1, 0, 0],\n [0, 1, 0, 0],\n [0, 1, 0, 0],\n [0, 1, 0, 0]],\n ],\n O: [\n [[0, 1, 1, 0],\n [0, 1, 1, 0],\n [0, 0, 0, 0],\n [0, 0, 0, 0]],\n ],\n T: [\n [[0, 1, 0],\n [1, 1, 1],","numLines":50,"startLine":1,"totalLines":611}}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01PYVJknhY3Cks64GnsvyiBy","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"The compiled JS looks good. Let me do a quick sanity check by looking at the key parts of the compiled JavaScript to make sure everything looks right.","signature":"EtQCClsIDBgCKkDGMFh+97L0QP21XV70tEYyezjcxXU26IYEq9Uj1NJ8PCN5IpTxksAC7jNJLBnRbgvcNNmIH8G3NmImOWNu5NYZMhFjbGF1ZGUtc29ubmV0LTQtNjgAEgxSHuchR7zmFLwPnecaDE5K1EtqAKMKQqoZLSIwDeSNDfAoS1LJucXD1TXqsxWxXLngMK0x+ToL1ZewaRfK/B03bGCU/g8fY3u2TySJKqYBfcbfgPH7pLfbHMRO0SkjTp3/5ctq+7Gt08hQwbMmBzZy+MFOz3RYGX2fbsWjx/qjEm/7dm2YGCvSurPgPXOdMysQGkAwA9sk1iWRkHMIu0fpPECJUwgn27sonXxHq533K0bmOCTAiJeWb4vbwqRucyu9DZ2heJc3lva8lO/nJ5y+iFdUk22UoEMPgaisoQ9PzWviSL1q9KjZX1mdvHBcLyGMu+nRVxgB"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":1161,"cache_read_input_tokens":29022,"cache_creation":{"ephemeral_5m_input_tokens":1161,"ephemeral_1h_input_tokens":0},"output_tokens":9,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"02d581e6-3cf9-4bb8-be44-51a832b09737"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01PYVJknhY3Cks64GnsvyiBy","type":"message","role":"assistant","content":[{"type":"text","text":"Good — compiled correctly. Let me do a quick sanity-check on the critical methods:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":1161,"cache_read_input_tokens":29022,"cache_creation":{"ephemeral_5m_input_tokens":1161,"ephemeral_1h_input_tokens":0},"output_tokens":9,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"176af1e2-eeba-41b4-83aa-ff55db096b45"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01PYVJknhY3Cks64GnsvyiBy","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01Mupjg3rCwnKZ3TzKdUuFmP","name":"Bash","input":{"command":"grep -n \"TetrisGame\\|lock()\\|clearLines\\|spawnPiece\\|rotate\\|dropInterval\\|hardDrop\\|ghostRow\" /tmp/loop-bench-6vr8i9xe/tetris.js | head -50","description":"Verify key methods compiled into tetris.js"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":1161,"cache_read_input_tokens":29022,"cache_creation":{"ephemeral_5m_input_tokens":1161,"ephemeral_1h_input_tokens":0},"output_tokens":9,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"5e4d230a-eb72-433e-9bd5-762bd369796e"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01Mupjg3rCwnKZ3TzKdUuFmP","type":"tool_result","content":"142:// TetrisGame class\n144:class TetrisGame {\n181: this.spawnPiece(); // moves next → current, prepares new next\n209: spawnPiece() {\n253: ghostRow() {\n276: this.lock();\n279: hardDrop() {\n289: this.lock();\n291: rotate(dir) {\n317: lock() {\n324: this.clearLines();\n325: this.spawnPiece();\n328: clearLines() {\n346: dropInterval() {\n382: this.spawnPiece();\n419: this.rotate(1);\n423: this.rotate(1); // CW\n427: this.rotate(-1); // CCW\n430: this.rotate(-1); // CCW (alternative)\n434: this.hardDrop();\n473: if (this.dropAccum >= this.dropInterval()) {\n474: this.dropAccum -= this.dropInterval();\n480: this.lock();\n524: const gr = this.ghostRow();\n609: new TetrisGame();","is_error":false}]},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"a99def76-5ff8-4e6a-ab7d-3afb1abe8477","timestamp":"2026-04-04T21:03:56.221Z","tool_use_result":{"stdout":"142:// TetrisGame class\n144:class TetrisGame {\n181: this.spawnPiece(); // moves next → current, prepares new next\n209: spawnPiece() {\n253: ghostRow() {\n276: this.lock();\n279: hardDrop() {\n289: this.lock();\n291: rotate(dir) {\n317: lock() {\n324: this.clearLines();\n325: this.spawnPiece();\n328: clearLines() {\n346: dropInterval() {\n382: this.spawnPiece();\n419: this.rotate(1);\n423: this.rotate(1); // CW\n427: this.rotate(-1); // CCW\n430: this.rotate(-1); // CCW (alternative)\n434: this.hardDrop();\n473: if (this.dropAccum >= this.dropInterval()) {\n474: this.dropAccum -= this.dropInterval();\n480: this.lock();\n524: const gr = this.ghostRow();\n609: new TetrisGame();","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_013krw9eQqaPJVFaD4fNAoKc","type":"message","role":"assistant","content":[{"type":"text","text":"Everything is in order. Let me also do a final review of the complete source to catch any loose ends:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":492,"cache_read_input_tokens":30183,"cache_creation":{"ephemeral_5m_input_tokens":492,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"cb7df580-dc86-4c63-856c-25bd643644fd"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_013krw9eQqaPJVFaD4fNAoKc","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01RhNbyNvZFkkwwWQFQk1n7U","name":"Read","input":{"file_path":"/tmp/loop-bench-6vr8i9xe/tetris.ts"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":492,"cache_read_input_tokens":30183,"cache_creation":{"ephemeral_5m_input_tokens":492,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"3e6c5507-2501-4999-af3d-c8c8e122ad0d"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01RhNbyNvZFkkwwWQFQk1n7U","type":"tool_result","content":"1\t// ─────────────────────────────────────────────\n2\t// Tetris — TypeScript browser implementation\n3\t// ─────────────────────────────────────────────\n4\t\n5\tconst COLS = 10;\n6\tconst ROWS = 20;\n7\tconst CELL = 30; // px per cell on main board\n8\tconst NEXT_CELL = 25; // px per cell in next-piece preview\n9\t\n10\t// ── Colours ────────────────────────────────────\n11\tconst PIECE_COLORS: Record<string, string> = {\n12\t I: '#00f0f0',\n13\t O: '#f0f000',\n14\t T: '#a000f0',\n15\t S: '#00e000',\n16\t Z: '#f00000',\n17\t J: '#0050f0',\n18\t L: '#f0a000',\n19\t};\n20\t\n21\t// ── SRS shape tables ───────────────────────────\n22\t// Each entry is an array of rotation states.\n23\t// 0 = spawn orientation, then CW 1, 180, CCW.\n24\t// 1 inside the matrix means a filled cell.\n25\tconst SHAPES: Record<string, number[][][]> = {\n26\t I: [\n27\t [[0,0,0,0],\n28\t [1,1,1,1],\n29\t [0,0,0,0],\n30\t [0,0,0,0]],\n31\t\n32\t [[0,0,1,0],\n33\t [0,0,1,0],\n34\t [0,0,1,0],\n35\t [0,0,1,0]],\n36\t\n37\t [[0,0,0,0],\n38\t [0,0,0,0],\n39\t [1,1,1,1],\n40\t [0,0,0,0]],\n41\t\n42\t [[0,1,0,0],\n43\t [0,1,0,0],\n44\t [0,1,0,0],\n45\t [0,1,0,0]],\n46\t ],\n47\t O: [\n48\t [[0,1,1,0],\n49\t [0,1,1,0],\n50\t [0,0,0,0],\n51\t [0,0,0,0]],\n52\t ],\n53\t T: [\n54\t [[0,1,0],\n55\t [1,1,1],\n56\t [0,0,0]],\n57\t\n58\t [[0,1,0],\n59\t [0,1,1],\n60\t [0,1,0]],\n61\t\n62\t [[0,0,0],\n63\t [1,1,1],\n64\t [0,1,0]],\n65\t\n66\t [[0,1,0],\n67\t [1,1,0],\n68\t [0,1,0]],\n69\t ],\n70\t S: [\n71\t [[0,1,1],\n72\t [1,1,0],\n73\t [0,0,0]],\n74\t\n75\t [[0,1,0],\n76\t [0,1,1],\n77\t [0,0,1]],\n78\t\n79\t [[0,0,0],\n80\t [0,1,1],\n81\t [1,1,0]],\n82\t\n83\t [[1,0,0],\n84\t [1,1,0],\n85\t [0,1,0]],\n86\t ],\n87\t Z: [\n88\t [[1,1,0],\n89\t [0,1,1],\n90\t [0,0,0]],\n91\t\n92\t [[0,0,1],\n93\t [0,1,1],\n94\t [0,1,0]],\n95\t\n96\t [[0,0,0],\n97\t [1,1,0],\n98\t [0,1,1]],\n99\t\n100\t [[0,1,0],\n101\t [1,1,0],\n102\t [1,0,0]],\n103\t ],\n104\t J: [\n105\t [[1,0,0],\n106\t [1,1,1],\n107\t [0,0,0]],\n108\t\n109\t [[0,1,1],\n110\t [0,1,0],\n111\t [0,1,0]],\n112\t\n113\t [[0,0,0],\n114\t [1,1,1],\n115\t [0,0,1]],\n116\t\n117\t [[0,1,0],\n118\t [0,1,0],\n119\t [1,1,0]],\n120\t ],\n121\t L: [\n122\t [[0,0,1],\n123\t [1,1,1],\n124\t [0,0,0]],\n125\t\n126\t [[0,1,0],\n127\t [0,1,0],\n128\t [0,1,1]],\n129\t\n130\t [[0,0,0],\n131\t [1,1,1],\n132\t [1,0,0]],\n133\t\n134\t [[1,1,0],\n135\t [0,1,0],\n136\t [0,1,0]],\n137\t ],\n138\t};\n139\t\n140\t// ── SRS wall-kick tables ────────────────────────\n141\t// Each row is the list of (dx, dy) offsets to try for a CW rotation\n142\t// starting from that state index. Screen coords: +y = down.\n143\t// Source: Tetris Wiki / SRS (y-axis flipped from wiki math coords).\n144\ttype Kick = [number, number];\n145\t\n146\tconst KICKS_JLSTZ: Kick[][] = [\n147\t // state 0 → 1 (CW)\n148\t [[0,0],[-1,0],[-1,-1],[0,2],[-1,2]],\n149\t // state 1 → 2 (CW)\n150\t [[0,0],[1,0],[1,1],[0,-2],[1,-2]],\n151\t // state 2 → 3 (CW)\n152\t [[0,0],[1,0],[1,-1],[0,2],[1,2]],\n153\t // state 3 → 0 (CW)\n154\t [[0,0],[-1,0],[-1,1],[0,-2],[-1,-2]],\n155\t];\n156\t\n157\tconst KICKS_I: Kick[][] = [\n158\t // state 0 → 1 (CW)\n159\t [[0,0],[-2,0],[1,0],[-2,1],[1,-2]],\n160\t // state 1 → 2 (CW)\n161\t [[0,0],[-1,0],[2,0],[-1,-2],[2,1]],\n162\t // state 2 → 3 (CW)\n163\t [[0,0],[2,0],[-1,0],[2,-1],[-1,2]],\n164\t // state 3 → 0 (CW)\n165\t [[0,0],[1,0],[-2,0],[1,2],[-2,-1]],\n166\t];\n167\t\n168\t// ── Types ──────────────────────────────────────\n169\ttype PieceType = keyof typeof SHAPES;\n170\t\n171\tinterface ActivePiece {\n172\t type : PieceType;\n173\t rot : number; // 0–3\n174\t col : number; // board column of top-left of bounding box\n175\t row : number; // board row of top-left of bounding box\n176\t}\n177\t\n178\ttype Cell = string | null; // null = empty, string = colour\n179\t\n180\t// ── Scoring per lines cleared (× level) ────────\n181\tconst LINE_POINTS = [0, 100, 300, 500, 800];\n182\t\n183\t// ─────────────────────────────────────────────\n184\t// TetrisGame class\n185\t// ─────────────────────────────────────────────\n186\tclass TetrisGame {\n187\t // Canvases\n188\t private readonly boardCanvas : HTMLCanvasElement;\n189\t private readonly boardCtx : CanvasRenderingContext2D;\n190\t private readonly nextCanvas : HTMLCanvasElement;\n191\t private readonly nextCtx : CanvasRenderingContext2D;\n192\t\n193\t // DOM stat elements\n194\t private readonly elScore : HTMLElement;\n195\t private readonly elLines : HTMLElement;\n196\t private readonly elLevel : HTMLElement;\n197\t\n198\t // Game state\n199\t private board : Cell[][];\n200\t private current: ActivePiece | null = null;\n201\t private next : ActivePiece | null = null;\n202\t\n203\t private score : number = 0;\n204\t private lines : number = 0;\n205\t private level : number = 1;\n206\t\n207\t private gameOver: boolean = false;\n208\t private paused : boolean = false;\n209\t\n210\t // Timing\n211\t private rafId : number = 0;\n212\t private lastStamp : number = 0;\n213\t private dropAccum : number = 0; // ms accumulated towards next auto-drop\n214\t\n215\t // DAS (Delayed Auto-Shift) state for smooth key repeat\n216\t private dasKey : string | null = null;\n217\t private dasTimer : number = 0;\n218\t private readonly DAS_DELAY = 170; // ms before repeat starts\n219\t private readonly DAS_PERIOD = 50; // ms between repeats\n220\t\n221\t // ── Bag randomiser (7-bag) ─────────────────\n222\t private bag: PieceType[] = [];\n223\t\n224\t constructor() {\n225\t this.boardCanvas = document.getElementById('game-canvas') as HTMLCanvasElement;\n226\t this.boardCtx = this.boardCanvas.getContext('2d')!;\n227\t this.nextCanvas = document.getElementById('next-canvas') as HTMLCanvasElement;\n228\t this.nextCtx = this.nextCanvas.getContext('2d')!;\n229\t\n230\t this.elScore = document.getElementById('score')!;\n231\t this.elLines = document.getElementById('lines')!;\n232\t this.elLevel = document.getElementById('level')!;\n233\t\n234\t this.board = this.emptyBoard();\n235\t\n236\t // Wire up keyboard\n237\t window.addEventListener('keydown', this.onKeyDown.bind(this));\n238\t window.addEventListener('keyup', this.onKeyUp.bind(this));\n239\t\n240\t // Wire up buttons\n241\t document.getElementById('btn-restart')!\n242\t .addEventListener('click', () => this.restart());\n243\t document.getElementById('btn-pause')!\n244\t .addEventListener('click', () => this.togglePause());\n245\t\n246\t this.spawnNext(); // prepares this.next\n247\t this.spawnPiece(); // moves next → current, prepares new next\n248\t this.loop(0);\n249\t }\n250\t\n251\t // ── Board helpers ──────────────────────────\n252\t private emptyBoard(): Cell[][] {\n253\t return Array.from({ length: ROWS }, () => Array<Cell>(COLS).fill(null));\n254\t }\n255\t\n256\t // ── Bag randomiser ─────────────────────────\n257\t private pullFromBag(): PieceType {\n258\t if (this.bag.length === 0) {\n259\t const types = Object.keys(SHAPES) as PieceType[];\n260\t // Fisher-Yates shuffle\n261\t for (let i = types.length - 1; i > 0; i--) {\n262\t const j = Math.floor(Math.random() * (i + 1));\n263\t [types[i], types[j]] = [types[j], types[i]];\n264\t }\n265\t this.bag = types;\n266\t }\n267\t return this.bag.pop()!;\n268\t }\n269\t\n270\t private makePiece(type: PieceType): ActivePiece {\n271\t // Centre-spawn: for 3-wide pieces col=3, for 4-wide pieces col=3 too\n272\t return { type, rot: 0, col: 3, row: 0 };\n273\t }\n274\t\n275\t private spawnNext(): void {\n276\t this.next = this.makePiece(this.pullFromBag());\n277\t this.drawNext();\n278\t }\n279\t\n280\t private spawnPiece(): void {\n281\t this.current = this.next ?? this.makePiece(this.pullFromBag());\n282\t this.spawnNext();\n283\t\n284\t // Nudge piece so it isn't partially hidden above board\n285\t // (the I-piece row-0 is blank in spawn orientation — push it up by 1)\n286\t if (this.current.type === 'I') {\n287\t this.current.row = -1;\n288\t }\n289\t\n290\t if (!this.fits(this.current, 0, 0)) {\n291\t this.gameOver = true;\n292\t }\n293\t }\n294\t\n295\t // ── Shape helpers ──────────────────────────\n296\t private shape(p: ActivePiece): number[][] {\n297\t const states = SHAPES[p.type];\n298\t return states[p.rot % states.length];\n299\t }\n300\t\n301\t /** Returns the list of {r, c} filled cells for a piece. */\n302\t private cells(p: ActivePiece): Array<{r: number; c: number}> {\n303\t const out: Array<{r: number; c: number}> = [];\n304\t const s = this.shape(p);\n305\t for (let r = 0; r < s.length; r++) {\n306\t for (let c = 0; c < s[r].length; c++) {\n307\t if (s[r][c]) out.push({ r: p.row + r, c: p.col + c });\n308\t }\n309\t }\n310\t return out;\n311\t }\n312\t\n313\t /** True when the piece (plus offset) fits on the board without collision. */\n314\t private fits(p: ActivePiece, dc: number, dr: number, rot?: number): boolean {\n315\t const test: ActivePiece = { ...p, col: p.col + dc, row: p.row + dr };\n316\t if (rot !== undefined) test.rot = rot;\n317\t for (const { r, c } of this.cells(test)) {\n318\t if (c < 0 || c >= COLS || r >= ROWS) return false;\n319\t if (r >= 0 && this.board[r][c] !== null) return false;\n320\t }\n321\t return true;\n322\t }\n323\t\n324\t // ── Ghost piece ────────────────────────────\n325\t private ghostRow(): number {\n326\t if (!this.current) return 0;\n327\t let dr = 0;\n328\t while (this.fits(this.current, 0, dr + 1)) dr++;\n329\t return this.current.row + dr;\n330\t }\n331\t\n332\t // ── Movement ──────────────────────────────\n333\t private moveLeft () : void { if (this.current && this.fits(this.current, -1, 0)) this.current.col--; }\n334\t private moveRight() : void { if (this.current && this.fits(this.current, 1, 0)) this.current.col++; }\n335\t\n336\t private softDrop(): void {\n337\t if (!this.current) return;\n338\t if (this.fits(this.current, 0, 1)) {\n339\t this.current.row++;\n340\t this.score += 1;\n341\t this.dropAccum = 0;\n342\t this.updateStats();\n343\t } else {\n344\t this.lock();\n345\t }\n346\t }\n347\t\n348\t private hardDrop(): void {\n349\t if (!this.current) return;\n350\t let dropped = 0;\n351\t while (this.fits(this.current, 0, 1)) {\n352\t this.current.row++;\n353\t dropped++;\n354\t }\n355\t this.score += dropped * 2;\n356\t this.updateStats();\n357\t this.lock();\n358\t }\n359\t\n360\t private rotate(dir: 1 | -1): void {\n361\t if (!this.current) return;\n362\t const states = SHAPES[this.current.type].length;\n363\t const oldRot = this.current.rot;\n364\t const newRot = ((oldRot + dir) % states + states) % states;\n365\t\n366\t const kickTable = this.current.type === 'I' ? KICKS_I : KICKS_JLSTZ;\n367\t\n368\t // For CW use kicks[oldRot]; for CCW negate kicks[newRot]\n369\t let kicks: Kick[];\n370\t if (dir === 1) {\n371\t kicks = kickTable[oldRot % kickTable.length];\n372\t } else {\n373\t kicks = kickTable[newRot % kickTable.length].map(([dx, dy]) => [-dx, -dy] as Kick);\n374\t }\n375\t\n376\t for (const [dx, dy] of kicks) {\n377\t if (this.fits(this.current, dx, dy, newRot)) {\n378\t this.current.col += dx;\n379\t this.current.row += dy;\n380\t this.current.rot = newRot;\n381\t return;\n382\t }\n383\t }\n384\t // No kick succeeded — rotation blocked\n385\t }\n386\t\n387\t // ── Locking ───────────────────────────────\n388\t private lock(): void {\n389\t if (!this.current) return;\n390\t\n391\t for (const { r, c } of this.cells(this.current)) {\n392\t if (r >= 0) this.board[r][c] = PIECE_COLORS[this.current.type];\n393\t }\n394\t\n395\t this.clearLines();\n396\t this.spawnPiece();\n397\t this.dropAccum = 0;\n398\t }\n399\t\n400\t private clearLines(): void {\n401\t let cleared = 0;\n402\t for (let r = ROWS - 1; r >= 0; r--) {\n403\t if (this.board[r].every(c => c !== null)) {\n404\t this.board.splice(r, 1);\n405\t this.board.unshift(Array<Cell>(COLS).fill(null));\n406\t cleared++;\n407\t r++; // re-check same index after splice\n408\t }\n409\t }\n410\t if (cleared === 0) return;\n411\t\n412\t this.score += LINE_POINTS[cleared] * this.level;\n413\t this.lines += cleared;\n414\t this.level = Math.floor(this.lines / 10) + 1;\n415\t this.updateStats();\n416\t }\n417\t\n418\t // ── Drop interval (ms) by level ───────────\n419\t private dropInterval(): number {\n420\t // Levels 1–20, each level ~12% faster, floor at 50 ms\n421\t return Math.max(50, Math.round(1000 * Math.pow(0.85, this.level - 1)));\n422\t }\n423\t\n424\t // ── Stats DOM update ──────────────────────\n425\t private updateStats(): void {\n426\t this.elScore.textContent = this.score.toString();\n427\t this.elLines.textContent = this.lines.toString();\n428\t this.elLevel.textContent = this.level.toString();\n429\t }\n430\t\n431\t // ── Public controls ───────────────────────\n432\t togglePause(): void {\n433\t if (this.gameOver) return;\n434\t this.paused = !this.paused;\n435\t const btn = document.getElementById('btn-pause');\n436\t if (btn) btn.textContent = this.paused ? 'Resume' : 'Pause';\n437\t }\n438\t\n439\t restart(): void {\n440\t this.board = this.emptyBoard();\n441\t this.current = null;\n442\t this.next = null;\n443\t this.bag = [];\n444\t this.score = 0;\n445\t this.lines = 0;\n446\t this.level = 1;\n447\t this.dropAccum = 0;\n448\t this.gameOver = false;\n449\t this.paused = false;\n450\t this.dasKey = null;\n451\t this.updateStats();\n452\t const btn = document.getElementById('btn-pause');\n453\t if (btn) btn.textContent = 'Pause';\n454\t this.spawnNext();\n455\t this.spawnPiece();\n456\t }\n457\t\n458\t // ── Keyboard handling ─────────────────────\n459\t private onKeyDown(e: KeyboardEvent): void {\n460\t // Always allow restart / pause\n461\t if (e.key === 'r' || e.key === 'R') { this.restart(); return; }\n462\t if (e.key === 'p' || e.key === 'P') { this.togglePause(); return; }\n463\t if (e.key === 'Escape') { this.togglePause(); return; }\n464\t\n465\t if (this.gameOver || this.paused) return;\n466\t\n467\t switch (e.key) {\n468\t case 'ArrowLeft':\n469\t e.preventDefault();\n470\t this.moveLeft();\n471\t this.startDAS('ArrowLeft');\n472\t break;\n473\t case 'ArrowRight':\n474\t e.preventDefault();\n475\t this.moveRight();\n476\t this.startDAS('ArrowRight');\n477\t break;\n478\t case 'ArrowDown':\n479\t e.preventDefault();\n480\t this.softDrop();\n481\t this.startDAS('ArrowDown');\n482\t break;\n483\t case 'ArrowUp':\n484\t e.preventDefault();\n485\t this.rotate(1);\n486\t break;\n487\t case 'x': case 'X':\n488\t this.rotate(1); // CW\n489\t break;\n490\t case 'z': case 'Z':\n491\t this.rotate(-1); // CCW\n492\t break;\n493\t case 'Control':\n494\t this.rotate(-1); // CCW (alternative)\n495\t break;\n496\t case ' ':\n497\t e.preventDefault();\n498\t this.hardDrop();\n499\t break;\n500\t }\n501\t }\n502\t\n503\t private onKeyUp(e: KeyboardEvent): void {\n504\t if (this.dasKey === e.key) this.dasKey = null;\n505\t }\n506\t\n507\t private startDAS(key: string): void {\n508\t if (this.dasKey === key) return;\n509\t this.dasKey = key;\n510\t this.dasTimer = this.DAS_DELAY;\n511\t }\n512\t\n513\t private tickDAS(dt: number): void {\n514\t if (!this.dasKey || this.paused || this.gameOver) return;\n515\t this.dasTimer -= dt;\n516\t if (this.dasTimer <= 0) {\n517\t this.dasTimer += this.DAS_PERIOD;\n518\t switch (this.dasKey) {\n519\t case 'ArrowLeft': this.moveLeft(); break;\n520\t case 'ArrowRight': this.moveRight(); break;\n521\t case 'ArrowDown': this.softDrop(); break;\n522\t }\n523\t }\n524\t }\n525\t\n526\t // ── Main loop ─────────────────────────────\n527\t private loop(stamp: number): void {\n528\t const dt = Math.min(stamp - this.lastStamp, 100); // cap at 100ms\n529\t this.lastStamp = stamp;\n530\t\n531\t if (!this.paused && !this.gameOver) {\n532\t this.dropAccum += dt;\n533\t if (this.dropAccum >= this.dropInterval()) {\n534\t this.dropAccum -= this.dropInterval();\n535\t if (this.current) {\n536\t if (this.fits(this.current, 0, 1)) {\n537\t this.current.row++;\n538\t } else {\n539\t this.lock();\n540\t }\n541\t }\n542\t }\n543\t this.tickDAS(dt);\n544\t }\n545\t\n546\t this.render();\n547\t this.rafId = requestAnimationFrame(this.loop.bind(this));\n548\t }\n549\t\n550\t // ─────────────────────────────────────────\n551\t // Rendering\n552\t // ─────────────────────────────────────────\n553\t private render(): void {\n554\t const ctx = this.boardCtx;\n555\t const W = this.boardCanvas.width;\n556\t const H = this.boardCanvas.height;\n557\t\n558\t // Background\n559\t ctx.fillStyle = '#0a0a1a';\n560\t ctx.fillRect(0, 0, W, H);\n561\t\n562\t // Subtle grid\n563\t ctx.strokeStyle = 'rgba(255,255,255,0.04)';\n564\t ctx.lineWidth = 1;\n565\t for (let c = 0; c <= COLS; c++) {\n566\t ctx.beginPath(); ctx.moveTo(c * CELL, 0); ctx.lineTo(c * CELL, H); ctx.stroke();\n567\t }\n568\t for (let r = 0; r <= ROWS; r++) {\n569\t ctx.beginPath(); ctx.moveTo(0, r * CELL); ctx.lineTo(W, r * CELL); ctx.stroke();\n570\t }\n571\t\n572\t // Locked cells\n573\t for (let r = 0; r < ROWS; r++) {\n574\t for (let c = 0; c < COLS; c++) {\n575\t const col = this.board[r][c];\n576\t if (col) this.drawCell(ctx, c, r, col, CELL);\n577\t }\n578\t }\n579\t\n580\t // Ghost piece\n581\t if (this.current && !this.gameOver) {\n582\t const gr = this.ghostRow();\n583\t const ghost: ActivePiece = { ...this.current, row: gr };\n584\t for (const { r, c } of this.cells(ghost)) {\n585\t if (r < 0) continue;\n586\t ctx.strokeStyle = PIECE_COLORS[this.current.type];\n587\t ctx.lineWidth = 1.5;\n588\t ctx.globalAlpha = 0.3;\n589\t ctx.strokeRect(c * CELL + 2, r * CELL + 2, CELL - 4, CELL - 4);\n590\t ctx.globalAlpha = 1;\n591\t }\n592\t }\n593\t\n594\t // Active piece\n595\t if (this.current && !this.gameOver) {\n596\t const col = PIECE_COLORS[this.current.type];\n597\t for (const { r, c } of this.cells(this.current)) {\n598\t if (r < 0) continue;\n599\t this.drawCell(ctx, c, r, col, CELL);\n600\t }\n601\t }\n602\t\n603\t // Overlays\n604\t if (this.paused) {\n605\t this.drawOverlay(ctx, W, H, 'PAUSED', '#ffffff', 'Press P to resume');\n606\t } else if (this.gameOver) {\n607\t this.drawOverlay(ctx, W, H, 'GAME OVER', '#ff4455', `Score: ${this.score} · Press R to restart`);\n608\t }\n609\t }\n610\t\n611\t private drawCell(\n612\t ctx : CanvasRenderingContext2D,\n613\t col : number, row: number,\n614\t color: string,\n615\t size : number,\n616\t ): void {\n617\t const x = col * size;\n618\t const y = row * size;\n619\t\n620\t // Main fill\n621\t ctx.fillStyle = color;\n622\t ctx.fillRect(x + 1, y + 1, size - 2, size - 2);\n623\t\n624\t // Bright top-left bevel\n625\t ctx.fillStyle = 'rgba(255,255,255,0.30)';\n626\t ctx.fillRect(x + 1, y + 1, size - 2, 4);\n627\t ctx.fillRect(x + 1, y + 1, 4, size - 2);\n628\t\n629\t // Dark bottom-right bevel\n630\t ctx.fillStyle = 'rgba(0,0,0,0.35)';\n631\t ctx.fillRect(x + 1, y + size - 5, size - 2, 4);\n632\t ctx.fillRect(x + size - 5, y + 1, 4, size - 2);\n633\t\n634\t // Inner highlight\n635\t ctx.fillStyle = 'rgba(255,255,255,0.12)';\n636\t ctx.fillRect(x + 5, y + 5, size - 10, size - 10);\n637\t }\n638\t\n639\t private drawOverlay(\n640\t ctx : CanvasRenderingContext2D,\n641\t W : number, H: number,\n642\t title : string,\n643\t titleColor: string,\n644\t subtitle: string,\n645\t ): void {\n646\t ctx.fillStyle = 'rgba(0,0,0,0.72)';\n647\t ctx.fillRect(0, 0, W, H);\n648\t\n649\t ctx.textAlign = 'center';\n650\t ctx.textBaseline = 'middle';\n651\t\n652\t ctx.fillStyle = titleColor;\n653\t ctx.font = `bold ${Math.round(CELL * 1.2)}px \"Courier New\", monospace`;\n654\t ctx.fillText(title, W / 2, H / 2 - CELL);\n655\t\n656\t ctx.fillStyle = 'rgba(220,220,255,0.80)';\n657\t ctx.font = `${Math.round(CELL * 0.5)}px \"Courier New\", monospace`;\n658\t ctx.fillText(subtitle, W / 2, H / 2 + CELL * 0.4);\n659\t }\n660\t\n661\t // ── Next-piece preview ────────────────────\n662\t private drawNext(): void {\n663\t const ctx = this.nextCtx;\n664\t const W = this.nextCanvas.width;\n665\t const H = this.nextCanvas.height;\n666\t\n667\t ctx.fillStyle = '#0a0a1a';\n668\t ctx.fillRect(0, 0, W, H);\n669\t\n670\t if (!this.next) return;\n671\t\n672\t const s = this.shape(this.next);\n673\t const rows = s.length;\n674\t const cols = s[0].length;\n675\t const color = PIECE_COLORS[this.next.type];\n676\t\n677\t const ox = Math.floor((W / NEXT_CELL - cols) / 2);\n678\t const oy = Math.floor((H / NEXT_CELL - rows) / 2);\n679\t\n680\t for (let r = 0; r < rows; r++) {\n681\t for (let c = 0; c < cols; c++) {\n682\t if (s[r][c]) {\n683\t this.drawCell(ctx, ox + c, oy + r, color, NEXT_CELL);\n684\t }\n685\t }\n686\t }\n687\t }\n688\t}\n689\t\n690\t// ── Bootstrap ────────────────────────────────\n691\twindow.addEventListener('DOMContentLoaded', () => {\n692\t new TetrisGame();\n693\t});\n694\t"}]},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"6475fd58-4438-41e4-a4d0-1096fe7684db","timestamp":"2026-04-04T21:03:59.273Z","tool_use_result":{"type":"text","file":{"filePath":"/tmp/loop-bench-6vr8i9xe/tetris.ts","content":"// ─────────────────────────────────────────────\n// Tetris — TypeScript browser implementation\n// ─────────────────────────────────────────────\n\nconst COLS = 10;\nconst ROWS = 20;\nconst CELL = 30; // px per cell on main board\nconst NEXT_CELL = 25; // px per cell in next-piece preview\n\n// ── Colours ────────────────────────────────────\nconst PIECE_COLORS: Record<string, string> = {\n I: '#00f0f0',\n O: '#f0f000',\n T: '#a000f0',\n S: '#00e000',\n Z: '#f00000',\n J: '#0050f0',\n L: '#f0a000',\n};\n\n// ── SRS shape tables ───────────────────────────\n// Each entry is an array of rotation states.\n// 0 = spawn orientation, then CW 1, 180, CCW.\n// 1 inside the matrix means a filled cell.\nconst SHAPES: Record<string, number[][][]> = {\n I: [\n [[0,0,0,0],\n [1,1,1,1],\n [0,0,0,0],\n [0,0,0,0]],\n\n [[0,0,1,0],\n [0,0,1,0],\n [0,0,1,0],\n [0,0,1,0]],\n\n [[0,0,0,0],\n [0,0,0,0],\n [1,1,1,1],\n [0,0,0,0]],\n\n [[0,1,0,0],\n [0,1,0,0],\n [0,1,0,0],\n [0,1,0,0]],\n ],\n O: [\n [[0,1,1,0],\n [0,1,1,0],\n [0,0,0,0],\n [0,0,0,0]],\n ],\n T: [\n [[0,1,0],\n [1,1,1],\n [0,0,0]],\n\n [[0,1,0],\n [0,1,1],\n [0,1,0]],\n\n [[0,0,0],\n [1,1,1],\n [0,1,0]],\n\n [[0,1,0],\n [1,1,0],\n [0,1,0]],\n ],\n S: [\n [[0,1,1],\n [1,1,0],\n [0,0,0]],\n\n [[0,1,0],\n [0,1,1],\n [0,0,1]],\n\n [[0,0,0],\n [0,1,1],\n [1,1,0]],\n\n [[1,0,0],\n [1,1,0],\n [0,1,0]],\n ],\n Z: [\n [[1,1,0],\n [0,1,1],\n [0,0,0]],\n\n [[0,0,1],\n [0,1,1],\n [0,1,0]],\n\n [[0,0,0],\n [1,1,0],\n [0,1,1]],\n\n [[0,1,0],\n [1,1,0],\n [1,0,0]],\n ],\n J: [\n [[1,0,0],\n [1,1,1],\n [0,0,0]],\n\n [[0,1,1],\n [0,1,0],\n [0,1,0]],\n\n [[0,0,0],\n [1,1,1],\n [0,0,1]],\n\n [[0,1,0],\n [0,1,0],\n [1,1,0]],\n ],\n L: [\n [[0,0,1],\n [1,1,1],\n [0,0,0]],\n\n [[0,1,0],\n [0,1,0],\n [0,1,1]],\n\n [[0,0,0],\n [1,1,1],\n [1,0,0]],\n\n [[1,1,0],\n [0,1,0],\n [0,1,0]],\n ],\n};\n\n// ── SRS wall-kick tables ────────────────────────\n// Each row is the list of (dx, dy) offsets to try for a CW rotation\n// starting from that state index. Screen coords: +y = down.\n// Source: Tetris Wiki / SRS (y-axis flipped from wiki math coords).\ntype Kick = [number, number];\n\nconst KICKS_JLSTZ: Kick[][] = [\n // state 0 → 1 (CW)\n [[0,0],[-1,0],[-1,-1],[0,2],[-1,2]],\n // state 1 → 2 (CW)\n [[0,0],[1,0],[1,1],[0,-2],[1,-2]],\n // state 2 → 3 (CW)\n [[0,0],[1,0],[1,-1],[0,2],[1,2]],\n // state 3 → 0 (CW)\n [[0,0],[-1,0],[-1,1],[0,-2],[-1,-2]],\n];\n\nconst KICKS_I: Kick[][] = [\n // state 0 → 1 (CW)\n [[0,0],[-2,0],[1,0],[-2,1],[1,-2]],\n // state 1 → 2 (CW)\n [[0,0],[-1,0],[2,0],[-1,-2],[2,1]],\n // state 2 → 3 (CW)\n [[0,0],[2,0],[-1,0],[2,-1],[-1,2]],\n // state 3 → 0 (CW)\n [[0,0],[1,0],[-2,0],[1,2],[-2,-1]],\n];\n\n// ── Types ──────────────────────────────────────\ntype PieceType = keyof typeof SHAPES;\n\ninterface ActivePiece {\n type : PieceType;\n rot : number; // 0–3\n col : number; // board column of top-left of bounding box\n row : number; // board row of top-left of bounding box\n}\n\ntype Cell = string | null; // null = empty, string = colour\n\n// ── Scoring per lines cleared (× level) ────────\nconst LINE_POINTS = [0, 100, 300, 500, 800];\n\n// ─────────────────────────────────────────────\n// TetrisGame class\n// ─────────────────────────────────────────────\nclass TetrisGame {\n // Canvases\n private readonly boardCanvas : HTMLCanvasElement;\n private readonly boardCtx : CanvasRenderingContext2D;\n private readonly nextCanvas : HTMLCanvasElement;\n private readonly nextCtx : CanvasRenderingContext2D;\n\n // DOM stat elements\n private readonly elScore : HTMLElement;\n private readonly elLines : HTMLElement;\n private readonly elLevel : HTMLElement;\n\n // Game state\n private board : Cell[][];\n private current: ActivePiece | null = null;\n private next : ActivePiece | null = null;\n\n private score : number = 0;\n private lines : number = 0;\n private level : number = 1;\n\n private gameOver: boolean = false;\n private paused : boolean = false;\n\n // Timing\n private rafId : number = 0;\n private lastStamp : number = 0;\n private dropAccum : number = 0; // ms accumulated towards next auto-drop\n\n // DAS (Delayed Auto-Shift) state for smooth key repeat\n private dasKey : string | null = null;\n private dasTimer : number = 0;\n private readonly DAS_DELAY = 170; // ms before repeat starts\n private readonly DAS_PERIOD = 50; // ms between repeats\n\n // ── Bag randomiser (7-bag) ─────────────────\n private bag: PieceType[] = [];\n\n constructor() {\n this.boardCanvas = document.getElementById('game-canvas') as HTMLCanvasElement;\n this.boardCtx = this.boardCanvas.getContext('2d')!;\n this.nextCanvas = document.getElementById('next-canvas') as HTMLCanvasElement;\n this.nextCtx = this.nextCanvas.getContext('2d')!;\n\n this.elScore = document.getElementById('score')!;\n this.elLines = document.getElementById('lines')!;\n this.elLevel = document.getElementById('level')!;\n\n this.board = this.emptyBoard();\n\n // Wire up keyboard\n window.addEventListener('keydown', this.onKeyDown.bind(this));\n window.addEventListener('keyup', this.onKeyUp.bind(this));\n\n // Wire up buttons\n document.getElementById('btn-restart')!\n .addEventListener('click', () => this.restart());\n document.getElementById('btn-pause')!\n .addEventListener('click', () => this.togglePause());\n\n this.spawnNext(); // prepares this.next\n this.spawnPiece(); // moves next → current, prepares new next\n this.loop(0);\n }\n\n // ── Board helpers ──────────────────────────\n private emptyBoard(): Cell[][] {\n return Array.from({ length: ROWS }, () => Array<Cell>(COLS).fill(null));\n }\n\n // ── Bag randomiser ─────────────────────────\n private pullFromBag(): PieceType {\n if (this.bag.length === 0) {\n const types = Object.keys(SHAPES) as PieceType[];\n // Fisher-Yates shuffle\n for (let i = types.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [types[i], types[j]] = [types[j], types[i]];\n }\n this.bag = types;\n }\n return this.bag.pop()!;\n }\n\n private makePiece(type: PieceType): ActivePiece {\n // Centre-spawn: for 3-wide pieces col=3, for 4-wide pieces col=3 too\n return { type, rot: 0, col: 3, row: 0 };\n }\n\n private spawnNext(): void {\n this.next = this.makePiece(this.pullFromBag());\n this.drawNext();\n }\n\n private spawnPiece(): void {\n this.current = this.next ?? this.makePiece(this.pullFromBag());\n this.spawnNext();\n\n // Nudge piece so it isn't partially hidden above board\n // (the I-piece row-0 is blank in spawn orientation — push it up by 1)\n if (this.current.type === 'I') {\n this.current.row = -1;\n }\n\n if (!this.fits(this.current, 0, 0)) {\n this.gameOver = true;\n }\n }\n\n // ── Shape helpers ──────────────────────────\n private shape(p: ActivePiece): number[][] {\n const states = SHAPES[p.type];\n return states[p.rot % states.length];\n }\n\n /** Returns the list of {r, c} filled cells for a piece. */\n private cells(p: ActivePiece): Array<{r: number; c: number}> {\n const out: Array<{r: number; c: number}> = [];\n const s = this.shape(p);\n for (let r = 0; r < s.length; r++) {\n for (let c = 0; c < s[r].length; c++) {\n if (s[r][c]) out.push({ r: p.row + r, c: p.col + c });\n }\n }\n return out;\n }\n\n /** True when the piece (plus offset) fits on the board without collision. */\n private fits(p: ActivePiece, dc: number, dr: number, rot?: number): boolean {\n const test: ActivePiece = { ...p, col: p.col + dc, row: p.row + dr };\n if (rot !== undefined) test.rot = rot;\n for (const { r, c } of this.cells(test)) {\n if (c < 0 || c >= COLS || r >= ROWS) return false;\n if (r >= 0 && this.board[r][c] !== null) return false;\n }\n return true;\n }\n\n // ── Ghost piece ────────────────────────────\n private ghostRow(): number {\n if (!this.current) return 0;\n let dr = 0;\n while (this.fits(this.current, 0, dr + 1)) dr++;\n return this.current.row + dr;\n }\n\n // ── Movement ──────────────────────────────\n private moveLeft () : void { if (this.current && this.fits(this.current, -1, 0)) this.current.col--; }\n private moveRight() : void { if (this.current && this.fits(this.current, 1, 0)) this.current.col++; }\n\n private softDrop(): void {\n if (!this.current) return;\n if (this.fits(this.current, 0, 1)) {\n this.current.row++;\n this.score += 1;\n this.dropAccum = 0;\n this.updateStats();\n } else {\n this.lock();\n }\n }\n\n private hardDrop(): void {\n if (!this.current) return;\n let dropped = 0;\n while (this.fits(this.current, 0, 1)) {\n this.current.row++;\n dropped++;\n }\n this.score += dropped * 2;\n this.updateStats();\n this.lock();\n }\n\n private rotate(dir: 1 | -1): void {\n if (!this.current) return;\n const states = SHAPES[this.current.type].length;\n const oldRot = this.current.rot;\n const newRot = ((oldRot + dir) % states + states) % states;\n\n const kickTable = this.current.type === 'I' ? KICKS_I : KICKS_JLSTZ;\n\n // For CW use kicks[oldRot]; for CCW negate kicks[newRot]\n let kicks: Kick[];\n if (dir === 1) {\n kicks = kickTable[oldRot % kickTable.length];\n } else {\n kicks = kickTable[newRot % kickTable.length].map(([dx, dy]) => [-dx, -dy] as Kick);\n }\n\n for (const [dx, dy] of kicks) {\n if (this.fits(this.current, dx, dy, newRot)) {\n this.current.col += dx;\n this.current.row += dy;\n this.current.rot = newRot;\n return;\n }\n }\n // No kick succeeded — rotation blocked\n }\n\n // ── Locking ───────────────────────────────\n private lock(): void {\n if (!this.current) return;\n\n for (const { r, c } of this.cells(this.current)) {\n if (r >= 0) this.board[r][c] = PIECE_COLORS[this.current.type];\n }\n\n this.clearLines();\n this.spawnPiece();\n this.dropAccum = 0;\n }\n\n private clearLines(): void {\n let cleared = 0;\n for (let r = ROWS - 1; r >= 0; r--) {\n if (this.board[r].every(c => c !== null)) {\n this.board.splice(r, 1);\n this.board.unshift(Array<Cell>(COLS).fill(null));\n cleared++;\n r++; // re-check same index after splice\n }\n }\n if (cleared === 0) return;\n\n this.score += LINE_POINTS[cleared] * this.level;\n this.lines += cleared;\n this.level = Math.floor(this.lines / 10) + 1;\n this.updateStats();\n }\n\n // ── Drop interval (ms) by level ───────────\n private dropInterval(): number {\n // Levels 1–20, each level ~12% faster, floor at 50 ms\n return Math.max(50, Math.round(1000 * Math.pow(0.85, this.level - 1)));\n }\n\n // ── Stats DOM update ──────────────────────\n private updateStats(): void {\n this.elScore.textContent = this.score.toString();\n this.elLines.textContent = this.lines.toString();\n this.elLevel.textContent = this.level.toString();\n }\n\n // ── Public controls ───────────────────────\n togglePause(): void {\n if (this.gameOver) return;\n this.paused = !this.paused;\n const btn = document.getElementById('btn-pause');\n if (btn) btn.textContent = this.paused ? 'Resume' : 'Pause';\n }\n\n restart(): void {\n this.board = this.emptyBoard();\n this.current = null;\n this.next = null;\n this.bag = [];\n this.score = 0;\n this.lines = 0;\n this.level = 1;\n this.dropAccum = 0;\n this.gameOver = false;\n this.paused = false;\n this.dasKey = null;\n this.updateStats();\n const btn = document.getElementById('btn-pause');\n if (btn) btn.textContent = 'Pause';\n this.spawnNext();\n this.spawnPiece();\n }\n\n // ── Keyboard handling ─────────────────────\n private onKeyDown(e: KeyboardEvent): void {\n // Always allow restart / pause\n if (e.key === 'r' || e.key === 'R') { this.restart(); return; }\n if (e.key === 'p' || e.key === 'P') { this.togglePause(); return; }\n if (e.key === 'Escape') { this.togglePause(); return; }\n\n if (this.gameOver || this.paused) return;\n\n switch (e.key) {\n case 'ArrowLeft':\n e.preventDefault();\n this.moveLeft();\n this.startDAS('ArrowLeft');\n break;\n case 'ArrowRight':\n e.preventDefault();\n this.moveRight();\n this.startDAS('ArrowRight');\n break;\n case 'ArrowDown':\n e.preventDefault();\n this.softDrop();\n this.startDAS('ArrowDown');\n break;\n case 'ArrowUp':\n e.preventDefault();\n this.rotate(1);\n break;\n case 'x': case 'X':\n this.rotate(1); // CW\n break;\n case 'z': case 'Z':\n this.rotate(-1); // CCW\n break;\n case 'Control':\n this.rotate(-1); // CCW (alternative)\n break;\n case ' ':\n e.preventDefault();\n this.hardDrop();\n break;\n }\n }\n\n private onKeyUp(e: KeyboardEvent): void {\n if (this.dasKey === e.key) this.dasKey = null;\n }\n\n private startDAS(key: string): void {\n if (this.dasKey === key) return;\n this.dasKey = key;\n this.dasTimer = this.DAS_DELAY;\n }\n\n private tickDAS(dt: number): void {\n if (!this.dasKey || this.paused || this.gameOver) return;\n this.dasTimer -= dt;\n if (this.dasTimer <= 0) {\n this.dasTimer += this.DAS_PERIOD;\n switch (this.dasKey) {\n case 'ArrowLeft': this.moveLeft(); break;\n case 'ArrowRight': this.moveRight(); break;\n case 'ArrowDown': this.softDrop(); break;\n }\n }\n }\n\n // ── Main loop ─────────────────────────────\n private loop(stamp: number): void {\n const dt = Math.min(stamp - this.lastStamp, 100); // cap at 100ms\n this.lastStamp = stamp;\n\n if (!this.paused && !this.gameOver) {\n this.dropAccum += dt;\n if (this.dropAccum >= this.dropInterval()) {\n this.dropAccum -= this.dropInterval();\n if (this.current) {\n if (this.fits(this.current, 0, 1)) {\n this.current.row++;\n } else {\n this.lock();\n }\n }\n }\n this.tickDAS(dt);\n }\n\n this.render();\n this.rafId = requestAnimationFrame(this.loop.bind(this));\n }\n\n // ─────────────────────────────────────────\n // Rendering\n // ─────────────────────────────────────────\n private render(): void {\n const ctx = this.boardCtx;\n const W = this.boardCanvas.width;\n const H = this.boardCanvas.height;\n\n // Background\n ctx.fillStyle = '#0a0a1a';\n ctx.fillRect(0, 0, W, H);\n\n // Subtle grid\n ctx.strokeStyle = 'rgba(255,255,255,0.04)';\n ctx.lineWidth = 1;\n for (let c = 0; c <= COLS; c++) {\n ctx.beginPath(); ctx.moveTo(c * CELL, 0); ctx.lineTo(c * CELL, H); ctx.stroke();\n }\n for (let r = 0; r <= ROWS; r++) {\n ctx.beginPath(); ctx.moveTo(0, r * CELL); ctx.lineTo(W, r * CELL); ctx.stroke();\n }\n\n // Locked cells\n for (let r = 0; r < ROWS; r++) {\n for (let c = 0; c < COLS; c++) {\n const col = this.board[r][c];\n if (col) this.drawCell(ctx, c, r, col, CELL);\n }\n }\n\n // Ghost piece\n if (this.current && !this.gameOver) {\n const gr = this.ghostRow();\n const ghost: ActivePiece = { ...this.current, row: gr };\n for (const { r, c } of this.cells(ghost)) {\n if (r < 0) continue;\n ctx.strokeStyle = PIECE_COLORS[this.current.type];\n ctx.lineWidth = 1.5;\n ctx.globalAlpha = 0.3;\n ctx.strokeRect(c * CELL + 2, r * CELL + 2, CELL - 4, CELL - 4);\n ctx.globalAlpha = 1;\n }\n }\n\n // Active piece\n if (this.current && !this.gameOver) {\n const col = PIECE_COLORS[this.current.type];\n for (const { r, c } of this.cells(this.current)) {\n if (r < 0) continue;\n this.drawCell(ctx, c, r, col, CELL);\n }\n }\n\n // Overlays\n if (this.paused) {\n this.drawOverlay(ctx, W, H, 'PAUSED', '#ffffff', 'Press P to resume');\n } else if (this.gameOver) {\n this.drawOverlay(ctx, W, H, 'GAME OVER', '#ff4455', `Score: ${this.score} · Press R to restart`);\n }\n }\n\n private drawCell(\n ctx : CanvasRenderingContext2D,\n col : number, row: number,\n color: string,\n size : number,\n ): void {\n const x = col * size;\n const y = row * size;\n\n // Main fill\n ctx.fillStyle = color;\n ctx.fillRect(x + 1, y + 1, size - 2, size - 2);\n\n // Bright top-left bevel\n ctx.fillStyle = 'rgba(255,255,255,0.30)';\n ctx.fillRect(x + 1, y + 1, size - 2, 4);\n ctx.fillRect(x + 1, y + 1, 4, size - 2);\n\n // Dark bottom-right bevel\n ctx.fillStyle = 'rgba(0,0,0,0.35)';\n ctx.fillRect(x + 1, y + size - 5, size - 2, 4);\n ctx.fillRect(x + size - 5, y + 1, 4, size - 2);\n\n // Inner highlight\n ctx.fillStyle = 'rgba(255,255,255,0.12)';\n ctx.fillRect(x + 5, y + 5, size - 10, size - 10);\n }\n\n private drawOverlay(\n ctx : CanvasRenderingContext2D,\n W : number, H: number,\n title : string,\n titleColor: string,\n subtitle: string,\n ): void {\n ctx.fillStyle = 'rgba(0,0,0,0.72)';\n ctx.fillRect(0, 0, W, H);\n\n ctx.textAlign = 'center';\n ctx.textBaseline = 'middle';\n\n ctx.fillStyle = titleColor;\n ctx.font = `bold ${Math.round(CELL * 1.2)}px \"Courier New\", monospace`;\n ctx.fillText(title, W / 2, H / 2 - CELL);\n\n ctx.fillStyle = 'rgba(220,220,255,0.80)';\n ctx.font = `${Math.round(CELL * 0.5)}px \"Courier New\", monospace`;\n ctx.fillText(subtitle, W / 2, H / 2 + CELL * 0.4);\n }\n\n // ── Next-piece preview ────────────────────\n private drawNext(): void {\n const ctx = this.nextCtx;\n const W = this.nextCanvas.width;\n const H = this.nextCanvas.height;\n\n ctx.fillStyle = '#0a0a1a';\n ctx.fillRect(0, 0, W, H);\n\n if (!this.next) return;\n\n const s = this.shape(this.next);\n const rows = s.length;\n const cols = s[0].length;\n const color = PIECE_COLORS[this.next.type];\n\n const ox = Math.floor((W / NEXT_CELL - cols) / 2);\n const oy = Math.floor((H / NEXT_CELL - rows) / 2);\n\n for (let r = 0; r < rows; r++) {\n for (let c = 0; c < cols; c++) {\n if (s[r][c]) {\n this.drawCell(ctx, ox + c, oy + r, color, NEXT_CELL);\n }\n }\n }\n }\n}\n\n// ── Bootstrap ────────────────────────────────\nwindow.addEventListener('DOMContentLoaded', () => {\n new TetrisGame();\n});\n","numLines":694,"startLine":1,"totalLines":694}}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_017s6QFKBAVxDZCF8aF4A9iy","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"The source looks good! Let me verify the complete file list and make sure everything is in order.\n\nLet me summarize what was built:\n\n1. **`tetris.ts`** - Complete TypeScript source (~700 lines)\n2. **`tetris.js`** - Compiled JavaScript output\n3. **`index.html`** - HTML structure with canvas elements and side panel\n4. **`style.css`** - Polished styling with dark theme\n\nKey features implemented:\n- All 7 tetrominoes (I, O, T, S, Z, J, L) with authentic SRS rotation\n- SRS wall kicks for both CW and CCW rotation\n\n\n- 7-bag randomizer to prevent piece repeats, ghost piece preview, line clearing with multipliers (100/300/500/800 points), soft and hard drop mechanics, level progression every 10 lines with increasing speed down to 50ms, DAS for smooth movement control, pause and restart functionality, and game over detection with next piece preview and beveled cell rendering.\n- I'm adding a rainbow-colored animated title and finishing the visual polish with the beveled cell styling to give the game depth.","signature":"Et8KClsIDBgCKkDNEFeTK/X+MlhKFW5G55PH+/KUJwLwL7kj8vEstkLkCKpqSNDyGkqCE4WtYY06F8ufFDmCMWw2I6n3+haxjrdnMhFjbGF1ZGUtc29ubmV0LTQtNjgAEgzBoc8Q5Gq9YXt3dMUaDPiWEVq71HKWNr/3wiIwUyx3S8JVTo8XJk8u0uWPdyNSZ1S7m5NYxMyrkXkpMI8zR2mC2Rs8+anKa4C+17sSKrEJjySnItWYYMgsixMZP7+SdSqLYnwg1lroy715CpTJcAVC+Jo6N7bh38EzvCPJcVv+BtnbFjKRrSJes5lzX5Rg0AcVWdYRVanTwKRxsT4sh19Az3hNZR+GFtC9Z/+pZQPFiGfHMKwEDnWHdN3i54VA69LEBrvfUtyuA59F8ThA/AKdyV6QUEQt4jx18I3HszB2SnYoJ1hJRr5nGefzBmScQtPTTNH1YGmFj7qIatsAROiy+uakQf6XDqZTrNfzf/pZpJ/+JBEJQZ3W/ZL0uyQCBTkr4/FEif5gDnws9TJycr577c33uVyovl2IbLevg9hruk3mV/LhAYw5s7c1siCipSUVd72rkemOtqnW/xK+7UDLX/L7tMDg3zgAD2t8Qn2BvbKD+0c0Ik83RH3+40ZeEoy4xiEsIcLjIf6rOPZhd9xonsMP0A3x9CxSblvcJtVqiSl7louRt08QiTDFtr2Xb13Dq+Dm5THmckyxz3aCbCPXppigJZuNTd1tpexYHoWDikymYhfgjsFVTgyzkH+qF9MY+vVn+nSNmsF4+uP5+hFUuX6Mb+CGANCf3o6B8mgehJTfi5gV2qsqDevNLGEPDXDwxKzFKtdjPIhlHIT52tfPBIUgpdho0Q5QCVTx5WiU6gbWboL4m+QDpOBYIwP0XytBpquoTPL5yOxZlbgzvjo6gPYJNmUbT7BDO5TJVDLqyPB5oJDVPEA8J4P9OkY97akz+eZ9vz4snwdnsFrDFjE4+IryiGTaPzCQtz1c+w2xm8BeHcYiY3KfPKdEeFIkRdVIidGie3Tdc8s68nKSvFba/+dQThNa/P2wuiv1xe0zjkB+BF14K6jwh6UYnCNKzbqN69qpv2XdMrhbi2Nmiu4Y6uok4/Hf0i23NAA2k7ik7ktka8i2ToxRPcbk0gXr1cItZY7zbq9l3dhNSzAoGwIhoCInpqwGBFk2jRzKHyvrqLtBKdU1Gh2GEKBzb+FXVATeMcX1wwEjDNYavGjgh7ZPYA/VeCAOaOkWF0vyKZyPUcpvgwCJT/cz3Bd9R55evTe2VlwKnhvs24Wfh3JgcE2N80XLEtjgb/c7vR6xTW5QfdTedwF1RQReTwWygylGxW3znRM8DJeur2HRQI3bxi+r4YtBPUHKVt3H2LRoQLndHe3D3w2LT65kOsWqUzRxshYsKp7CRYTOQY/EZogmvh7oF2UlxfX0hsU0vc1lbAh06apy/0Azn7le2jV8ycbOERJ7AjSH7Bzbjf3Y93ZoZratOs2sVGd5rvk8jzM2zWV4yUjRVXCX1x42StCl5R+ap7/9QnHahbTbZma5C6I4Jiz47lsUWUDcFwgbJ018qpktKDNsNfgmRzRpg/DT1ss/cwtb1X4VE/SYNLgGB6tE7g/ylPP03FLLm2i/xOyd36Qq7QkRcVRycYHVxaKmDZQBy9rwXuBydT8c6GhG4iQBtoMsFN2aTtH6ht5P/d8+Q9rsofzSqvzCvjqhJB76C45xSp5SBtylGGy9DzyQUZxkDKSB1/UMIrhEUeJsDcQ+6T0GP3+bFx2k3SV918pK4U/6frOCdKqbFtXB3XFp4u0U3+CnAJv/eCe0BtSK+eVPiFbzDBgB"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":9236,"cache_read_input_tokens":30183,"cache_creation":{"ephemeral_5m_input_tokens":9236,"ephemeral_1h_input_tokens":0},"output_tokens":8,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"115f802e-2094-472f-8f2a-1d7ac10c9fb5"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_017s6QFKBAVxDZCF8aF4A9iy","type":"message","role":"assistant","content":[{"type":"text","text":"The source looks excellent. Let me do one final check to confirm all four files are present and the JS has no obvious issues:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":9236,"cache_read_input_tokens":30183,"cache_creation":{"ephemeral_5m_input_tokens":9236,"ephemeral_1h_input_tokens":0},"output_tokens":8,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"71163278-937b-47f9-91d5-037a32df671f"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_017s6QFKBAVxDZCF8aF4A9iy","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01ARish627ZMQpyNniDpTMVY","name":"Bash","input":{"command":"ls -lh /tmp/loop-bench-6vr8i9xe/{index.html,style.css,tetris.ts,tetris.js,tsconfig.json} && echo \"---\" && node --input-type=commonjs < /tmp/loop-bench-6vr8i9xe/tetris.js 2>&1 | head -5 || true","description":"Verify all files exist and check JS syntax via Node"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":9236,"cache_read_input_tokens":30183,"cache_creation":{"ephemeral_5m_input_tokens":9236,"ephemeral_1h_input_tokens":0},"output_tokens":8,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"fcdc2cb4-2fd3-4713-ba23-c238da1af835"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01ARish627ZMQpyNniDpTMVY","type":"tool_result","content":"-rw-r--r-- 1 root root 1.9K Apr 4 23:03 /tmp/loop-bench-6vr8i9xe/index.html\n-rw-r--r-- 1 root root 3.4K Apr 4 23:00 /tmp/loop-bench-6vr8i9xe/style.css\n-rw-r--r-- 1 root root 21K Apr 4 23:03 /tmp/loop-bench-6vr8i9xe/tetris.js\n-rw-r--r-- 1 root root 20K Apr 4 23:03 /tmp/loop-bench-6vr8i9xe/tetris.ts\n-rw-r--r-- 1 root root 280 Apr 4 23:03 /tmp/loop-bench-6vr8i9xe/tsconfig.json\n---\n[stdin]:608\nwindow.addEventListener('DOMContentLoaded', () => {\n^\n\nReferenceError: window is not defined","is_error":false}]},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"63925300-f18b-4067-9317-867d712dbfd4","timestamp":"2026-04-04T21:04:11.388Z","tool_use_result":{"stdout":"-rw-r--r-- 1 root root 1.9K Apr 4 23:03 /tmp/loop-bench-6vr8i9xe/index.html\n-rw-r--r-- 1 root root 3.4K Apr 4 23:00 /tmp/loop-bench-6vr8i9xe/style.css\n-rw-r--r-- 1 root root 21K Apr 4 23:03 /tmp/loop-bench-6vr8i9xe/tetris.js\n-rw-r--r-- 1 root root 20K Apr 4 23:03 /tmp/loop-bench-6vr8i9xe/tetris.ts\n-rw-r--r-- 1 root root 280 Apr 4 23:03 /tmp/loop-bench-6vr8i9xe/tsconfig.json\n---\n[stdin]:608\nwindow.addEventListener('DOMContentLoaded', () => {\n^\n\nReferenceError: window is not defined","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01Dupd4JS2ALYBxVw5uYtK7X","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"The `ReferenceError: window is not defined` is expected since `window` is a browser global that doesn't exist in Node.js. This just means the code is syntactically correct and runs fine until it hits the browser-specific `window` global — which confirms the JS file is syntactically valid. The game will work perfectly in a browser.\n\nLet me also verify the HTML is correct by checking that the canvas IDs and script source match.","signature":"Eu0EClsIDBgCKkBC/b53hMKoFdrzAjEs3v+V15rBSAJaCABP1d8JAHJ5LIt6DUEECkpGHg1H+7dz7JkpIGDwINgRqVO4bdCpg10VMhFjbGF1ZGUtc29ubmV0LTQtNjgAEgzA/EbJaAS/I0hi4jIaDMzHVkSEFKr0zOXBYyIwzR7AbkCWaqVYVg3gBvjP1HzrGEJXDkq1YSh2zZOjz72/ZbOwZeL0zS0dn+i4QkRDKr8DHDlTQgB7n5a2XNH7dGzKDvWE3/YA7U4dDru+yrHjNmYMStr69IQhYwe6x3hAcEUG+WClKUOGvwbOPPwuo3pgk8beh18Kuo2rci3HxvQZ4qmNtCBRuJteoLcAPcOH2fzx4W7JpXoy39XV4rhkL3YJk5D8lUU4JOmmBik7ZQo+uUtHHmJPvGGkgkriv38RctSXfq1148bj9It4GfjjWES4cqk3mdPYmljsHnKC+uGPFzoOEa/VaDw5+7LmnpiAl7iUMB/W18Fg3Drw8rg7qGggjiY4jwgQ2qFu8f+9TwaG+dihd0L7OamrqElRN3R6SFy1/v8sxducR675TK6ooCL4jGwtDitF3Y3IvFfQ472JP5d6BN5JgsAHdMyCFci50rb0qH/w30krUGm5jLlQDwtdgWB/PewbagQeg73BG/hc76a62rIfYhrgR1UGx4442kHgZ0KVapGjBconYv7GbzMb38xvwalZZIVjLMbZ6QalKo5ZYXqiZfzXPkFGyBYATEVKDo7z3SaEJ+8tgD1v5sLuT1Ag0U24q8ObaQDeUWthi09dT5K0r5m7J9/0RCKeMEPY0GmKmUZUghTfv4OgrViDGAE="}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":828,"cache_read_input_tokens":39419,"cache_creation":{"ephemeral_5m_input_tokens":828,"ephemeral_1h_input_tokens":0},"output_tokens":8,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"48035810-aca9-4b07-b7d5-9e6288cf9d8a"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01Dupd4JS2ALYBxVw5uYtK7X","type":"message","role":"assistant","content":[{"type":"text","text":"The `window is not defined` error is expected — `window` is a browser-only global. Node parsed and ran the entire file until that last line, confirming no syntax errors anywhere in the compiled JS."}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":828,"cache_read_input_tokens":39419,"cache_creation":{"ephemeral_5m_input_tokens":828,"ephemeral_1h_input_tokens":0},"output_tokens":8,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"af8b44ea-130e-4c1a-bf8a-75d71a78f940"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01Dupd4JS2ALYBxVw5uYtK7X","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01Sofq6t43uh7EU4MJcMmoqp","name":"Bash","input":{"command":"# Verify HTML references match the JS expectations\ngrep -E 'id=|src=' /tmp/loop-bench-6vr8i9xe/index.html","description":"Verify HTML element IDs match those queried in the JS"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":828,"cache_read_input_tokens":39419,"cache_creation":{"ephemeral_5m_input_tokens":828,"ephemeral_1h_input_tokens":0},"output_tokens":8,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"d2ebf253-00ab-4995-bea9-8085218fd110"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01Sofq6t43uh7EU4MJcMmoqp","type":"tool_result","content":" <canvas id=\"game-canvas\"\n <canvas id=\"next-canvas\" width=\"125\" height=\"100\"></canvas>\n <div class=\"stat-value\" id=\"score\">0</div>\n <div class=\"stat-value\" id=\"lines\">0</div>\n <div class=\"stat-value\" id=\"level\">1</div>\n <button id=\"btn-pause\">Pause</button>\n <button id=\"btn-restart\">Restart</button>\n <script src=\"tetris.js\"></script>","is_error":false}]},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"cd768065-b065-422e-9a16-542b7ac71bf2","timestamp":"2026-04-04T21:04:17.827Z","tool_use_result":{"stdout":" <canvas id=\"game-canvas\"\n <canvas id=\"next-canvas\" width=\"125\" height=\"100\"></canvas>\n <div class=\"stat-value\" id=\"score\">0</div>\n <div class=\"stat-value\" id=\"lines\">0</div>\n <div class=\"stat-value\" id=\"level\">1</div>\n <button id=\"btn-pause\">Pause</button>\n <button id=\"btn-restart\">Restart</button>\n <script src=\"tetris.js\"></script>","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_012pKq75JyaawSPKz81CMGtY","type":"message","role":"assistant","content":[{"type":"text","text":"All IDs match perfectly. Here's a full summary of everything that was built:\n\n---\n\n## What was built\n\n**Files created:**\n\n| File | Purpose |\n|---|---|\n| `tetris.ts` | TypeScript source (~700 lines) |\n| `tetris.js` | Compiled output (served to the browser) |\n| `index.html` | Game layout — canvas + side panel |\n| `style.css` | Dark retro theme with animated title |\n| `tsconfig.json` | TypeScript compiler config |\n\n**Open `index.html` in any browser to play.**\n\n---\n\n## Features & mechanics\n\n### Pieces\n- All **7 standard tetrominoes** (I, O, T, S, Z, J, L) with authentic SRS (Super Rotation System) shapes and colour coding\n- **7-bag randomiser** — each set of 7 pieces contains one of every type, eliminating long droughts\n\n### Rotation\n- **SRS wall kicks** for both CW (`↑` / `X`) and CCW (`Z` / `Ctrl`) rotation — tries up to 5 offset tests so pieces kick off walls and floors cleanly\n\n### Movement\n- **Arrow keys** to move left/right/down\n- **DAS (Delayed Auto-Shift)** — hold a direction key and the piece starts repeating after 170 ms, then every 50 ms, for smooth modern feel\n\n### Dropping\n- **Soft drop** (`↓`) — accelerates fall, scores 1 pt/row\n- **Hard drop** (`Space`) — instantly slams to ghost position, scores 2 pts/row\n\n### Scoring\n| Lines cleared | Points × level |\n|---|---|\n| 1 (Single) | 100 |\n| 2 (Double) | 300 |\n| 3 (Triple) | 500 |\n| 4 (Tetris) | 800 |\n\n### Speed / Level\n- Level advances every **10 lines**\n- Drop interval formula: `max(50ms, 1000ms × 0.85^(level-1))` — roughly 12% faster per level, capping at 50 ms (20 drops/sec) around level 20\n\n### Visual\n- **Ghost piece** — outline showing where the current piece will land\n- **Beveled 3-D cells** with top-left highlight and bottom-right shadow\n- **Next piece preview** panel\n- Pause / Game Over overlays\n- Subtle grid, animated rainbow title\n\n### Controls summary\n| Key | Action |\n|---|---|\n| `←` / `→` | Move |\n| `↓` | Soft drop |\n| `Space` | Hard drop |\n| `↑` / `X` | Rotate CW |\n| `Z` / `Ctrl` | Rotate CCW |\n| `P` / `Esc` | Pause / Resume |\n| `R` | Restart |"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":412,"cache_read_input_tokens":40247,"cache_creation":{"ephemeral_5m_input_tokens":412,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","uuid":"f351f034-2231-45c7-b39f-7c817222e164"} +{"type":"result","subtype":"success","is_error":false,"duration_ms":418149,"duration_api_ms":414770,"num_turns":19,"result":"All IDs match perfectly. Here's a full summary of everything that was built:\n\n---\n\n## What was built\n\n**Files created:**\n\n| File | Purpose |\n|---|---|\n| `tetris.ts` | TypeScript source (~700 lines) |\n| `tetris.js` | Compiled output (served to the browser) |\n| `index.html` | Game layout — canvas + side panel |\n| `style.css` | Dark retro theme with animated title |\n| `tsconfig.json` | TypeScript compiler config |\n\n**Open `index.html` in any browser to play.**\n\n---\n\n## Features & mechanics\n\n### Pieces\n- All **7 standard tetrominoes** (I, O, T, S, Z, J, L) with authentic SRS (Super Rotation System) shapes and colour coding\n- **7-bag randomiser** — each set of 7 pieces contains one of every type, eliminating long droughts\n\n### Rotation\n- **SRS wall kicks** for both CW (`↑` / `X`) and CCW (`Z` / `Ctrl`) rotation — tries up to 5 offset tests so pieces kick off walls and floors cleanly\n\n### Movement\n- **Arrow keys** to move left/right/down\n- **DAS (Delayed Auto-Shift)** — hold a direction key and the piece starts repeating after 170 ms, then every 50 ms, for smooth modern feel\n\n### Dropping\n- **Soft drop** (`↓`) — accelerates fall, scores 1 pt/row\n- **Hard drop** (`Space`) — instantly slams to ghost position, scores 2 pts/row\n\n### Scoring\n| Lines cleared | Points × level |\n|---|---|\n| 1 (Single) | 100 |\n| 2 (Double) | 300 |\n| 3 (Triple) | 500 |\n| 4 (Tetris) | 800 |\n\n### Speed / Level\n- Level advances every **10 lines**\n- Drop interval formula: `max(50ms, 1000ms × 0.85^(level-1))` — roughly 12% faster per level, capping at 50 ms (20 drops/sec) around level 20\n\n### Visual\n- **Ghost piece** — outline showing where the current piece will land\n- **Beveled 3-D cells** with top-left highlight and bottom-right shadow\n- **Next piece preview** panel\n- Pause / Game Over overlays\n- Subtle grid, animated rainbow title\n\n### Controls summary\n| Key | Action |\n|---|---|\n| `←` / `→` | Move |\n| `↓` | Soft drop |\n| `Space` | Hard drop |\n| `↑` / `X` | Rotate CW |\n| `Z` / `Ctrl` | Rotate CCW |\n| `P` / `Esc` | Pause / Resume |\n| `R` | Restart |","stop_reason":"end_turn","session_id":"d49edfaa-873e-41f2-b233-25ccd2819ad5","total_cost_usd":0.79805865,"usage":{"input_tokens":22,"cache_creation_input_tokens":42361,"cache_read_input_tokens":467563,"output_tokens":33258,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":42361},"inference_geo":"","iterations":[],"speed":"standard"},"modelUsage":{"claude-sonnet-4-6":{"inputTokens":22,"outputTokens":33258,"cacheReadInputTokens":467563,"cacheCreationInputTokens":42361,"webSearchRequests":0,"costUSD":0.79805865,"contextWindow":200000,"maxOutputTokens":32000}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","uuid":"b413f222-1fe0-4c08-b277-022934dc6f3a"} diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/claude_output.json b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/claude_output.json @@ -0,0 +1 @@ +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01DNYZ8EjojHwDGBq1gjrSY3","type":"message","role":"assistant","content":[{"type":"text","text":"All mechanics verified. Let me also do a quick test of line-clearing scoring:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":1966,"cache_read_input_tokens":23671,"cache_creation":{"ephemeral_5m_input_tokens":1966,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"56a1d09c-d7a2-40dc-b45e-aa30329a2754"} +\ No newline at end of file diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/claude_stderr.log b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/claude_stderr.log diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/eval_results.json b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/eval_results.json @@ -0,0 +1,261 @@ +{ + "structural": { + "pass": true, + "checks": [ + { + "name": "entry_point_exists", + "pass": true, + "detail": "index.html found" + }, + { + "name": "package_json_exists", + "pass": true, + "detail": "package.json found" + }, + { + "name": "build_succeeds", + "pass": true, + "detail": "no build script defined (static project)" + }, + { + "name": "typescript_compiles", + "pass": true, + "detail": "tsc --noEmit passed" + } + ], + "score": 1.0 + }, + "functional": { + "pass": false, + "error": "playwright eval not yet wired", + "score": 0 + }, + "quality": { + "lint": { + "pass": true, + "errors": 0, + "warnings": 0 + }, + "typecheck": { + "pass": true + }, + "performance": { + "bundle_size_bytes": 16686, + "size_under_512kb": true + }, + "score": 0.67 + }, + "code_analysis": { + "files": { + "total": 7, + "code": 3, + "docs": 0, + "unnecessary": 0, + "unnecessary_list": [] + }, + "lines_of_code": 1225, + "dependencies": { + "production": 0, + "dev": 5, + "total": 5 + }, + "complexity": "moderate", + "console_logs": 0, + "magic_numbers": { + "count": 82, + "excessive": true + }, + "function_length": { + "count": 57, + "average": 7.6, + "max": 31, + "long_functions": 0 + }, + "max_nesting_depth": 10, + "global_declarations": 18, + "naming": { + "dominant_style": "camelCase", + "consistency_pct": 100.0, + "camel_case": 371, + "snake_case": 0 + }, + "error_handling": { + "try_catch_blocks": 0, + "has_error_handling": false + }, + "comments": { + "comment_lines": 61, + "source_lines": 887, + "ratio_pct": 6.9 + }, + "separation_of_concerns": { + "verdict": "mixed", + "files_with_rendering": 2, + "files_with_logic": 2, + "files_with_both": 2 + }, + "html_validation": { + "valid": false, + "errors": 2 + }, + "duplication_percentage": 0.0, + "score": 0.85 + }, + "transcript_analysis": { + "total_events": 74, + "tool_calls": { + "total": 27, + "bash": 17, + "write": 0, + "edit": 4, + "read": 6 + }, + "wasted_turns": { + "total": 0, + "docs": 0, + "ascii_art": 0, + "server_starts": 0 + }, + "errors_encountered": 0, + "thinking_blocks": 8, + "text_blocks": 9, + "productivity_ratio": 1.0, + "self_tested": true, + "score": 1.0 + }, + "gameplay_bot": { + "pass": false, + "score": 0.88, + "total": 16, + "passed": 14, + "failed": 2, + "report": { + "implementation": { + "renderer": "canvas", + "grid_detected": true, + "grid_bounds": { + "x": 0, + "y": 0, + "width": 300, + "height": 600 + }, + "controls": { + "left": "ArrowLeft", + "right": "ArrowRight", + "down": "ArrowDown", + "rotate": "x", + "drop": "Space" + }, + "start_mechanism": "auto", + "score_element_found": true + }, + "tests": [ + { + "name": "game_loads", + "pass": true, + "detail": "no console errors" + }, + { + "name": "game_starts", + "pass": true, + "detail": "started via auto" + }, + { + "name": "auto_drop", + "pass": true, + "detail": "grid state changed after 5s with no input" + }, + { + "name": "move_left", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "move_right", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "move_down", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "rotate", + "pass": true, + "detail": "piece shape changed after rotate key" + }, + { + "name": "all_pieces_rotate", + "pass": true, + "detail": "rotated: [other] failed: [] (tested 1 piece types in 60 attempts)" + }, + { + "name": "hard_drop", + "pass": true, + "detail": "piece immediately dropped and new piece appeared" + }, + { + "name": "piece_locks", + "pass": true, + "detail": "filled cells persist at bottom" + }, + { + "name": "new_piece_spawns", + "pass": true, + "detail": "new piece detected at top of grid" + }, + { + "name": "multiple_pieces", + "pass": true, + "detail": "game still responding after 10 piece drops" + }, + { + "name": "line_clear", + "pass": true, + "detail": "10 line(s) cleared during AI play" + }, + { + "name": "score_changes", + "pass": false, + "detail": "score did not increase: [228] -> no change after polling" + }, + { + "name": "game_over", + "pass": false, + "detail": "could not trigger or detect game over" + }, + { + "name": "playable_30s", + "pass": true, + "detail": "played for 30s, placed 85 pieces, no crashes" + } + ], + "summary": { + "total": 16, + "passed": 14, + "failed": 2, + "score": 0.88 + }, + "gameplay": { + "pieces_placed": 126, + "lines_cleared": 37, + "max_score_observed": 198, + "play_duration_seconds": 30, + "errors_during_play": 0 + }, + "performance": { + "load_time_ms": 21 + }, + "accessibility": { + "issues": [ + "canvas without aria-label or role", + "canvas without aria-label or role" + ], + "issue_count": 2, + "pass": false + } + } + }, + "score": 0.7345 +} +\ No newline at end of file diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/gameplay-bot-report.json b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/gameplay-bot-report.json @@ -0,0 +1,127 @@ +{ + "implementation": { + "renderer": "canvas", + "grid_detected": true, + "grid_bounds": { + "x": 0, + "y": 0, + "width": 300, + "height": 600 + }, + "controls": { + "left": "ArrowLeft", + "right": "ArrowRight", + "down": "ArrowDown", + "rotate": "x", + "drop": "Space" + }, + "start_mechanism": "auto", + "score_element_found": true + }, + "tests": [ + { + "name": "game_loads", + "pass": true, + "detail": "no console errors" + }, + { + "name": "game_starts", + "pass": true, + "detail": "started via auto" + }, + { + "name": "auto_drop", + "pass": true, + "detail": "grid state changed after 5s with no input" + }, + { + "name": "move_left", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "move_right", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "move_down", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "rotate", + "pass": true, + "detail": "piece shape changed after rotate key" + }, + { + "name": "all_pieces_rotate", + "pass": true, + "detail": "rotated: [other] failed: [] (tested 1 piece types in 60 attempts)" + }, + { + "name": "hard_drop", + "pass": true, + "detail": "piece immediately dropped and new piece appeared" + }, + { + "name": "piece_locks", + "pass": true, + "detail": "filled cells persist at bottom" + }, + { + "name": "new_piece_spawns", + "pass": true, + "detail": "new piece detected at top of grid" + }, + { + "name": "multiple_pieces", + "pass": true, + "detail": "game still responding after 10 piece drops" + }, + { + "name": "line_clear", + "pass": true, + "detail": "10 line(s) cleared during AI play" + }, + { + "name": "score_changes", + "pass": false, + "detail": "score did not increase: [228] -> no change after polling" + }, + { + "name": "game_over", + "pass": false, + "detail": "could not trigger or detect game over" + }, + { + "name": "playable_30s", + "pass": true, + "detail": "played for 30s, placed 85 pieces, no crashes" + } + ], + "summary": { + "total": 16, + "passed": 14, + "failed": 2, + "score": 0.88 + }, + "gameplay": { + "pieces_placed": 126, + "lines_cleared": 37, + "max_score_observed": 198, + "play_duration_seconds": 30, + "errors_during_play": 0 + }, + "performance": { + "load_time_ms": 21 + }, + "accessibility": { + "issues": [ + "canvas without aria-label or role", + "canvas without aria-label or role" + ], + "issue_count": 2, + "pass": false + } +} +\ No newline at end of file diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/meta.json b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/meta.json @@ -0,0 +1,30 @@ +{ + "model": "sonnet", + "effort": "high", + "prompt_style": "simple", + "language": "typescript", + "human_language": "en", + "tool_read": "on", + "tool_write": "on", + "tool_edit": "on", + "tool_glob": "on", + "tool_grep": "on", + "linter": "on", + "playwright": "on", + "context_file": "none", + "sub_agents": "on", + "web_search": "on", + "max_budget": "low", + "task": "tetris", + "cell_id": "tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on", + "runs_per_cell": 3, + "timeout_seconds": 1200, + "max_budget_usd": 2.0, + "run_id": "tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2", + "run_number": 2, + "claude_version": "2.1.92 (Claude Code)", + "started_at": "2026-04-04T20:57:28.503761+00:00", + "wall_time_seconds": 1200, + "exit_code": 124, + "completed_at": "2026-04-04T21:17:35.875117+00:00" +} +\ No newline at end of file diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/transcript.jsonl b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run2/transcript.jsonl @@ -0,0 +1,74 @@ +{"type": "harness", "subtype": "config", "model": "sonnet", "effort": "high", "tools": ["Bash", "Read", "Write", "Edit", "Glob", "Grep", "Agent", "WebSearch", "WebFetch"], "max_budget_usd": 2.0, "timeout_seconds": 1200, "task": "tetris", "language": "typescript", "prompt_style": "simple"} +{"type": "user", "subtype": "prompt", "message": {"role": "user", "content": "Build a playable Tetris game that runs in a browser. It should have all the standard mechanics: piece rotation, line clearing, scoring, and increasing speed. Use keyboard controls for movement and rotation.\n\n\nUse TypeScript."}} +{"type":"system","subtype":"init","cwd":"/tmp/loop-bench-q82ugmwv","session_id":"785df422-d6d2-4506-9806-c920da9beacf","tools":["Bash","Edit","Read"],"mcp_servers":[],"model":"claude-sonnet-4-6","permissionMode":"dontAsk","slash_commands":["update-config","debug","simplify","batch","loop","schedule","claude-api","compact","context","cost","heapdump","init","review","security-review","insights"],"apiKeySource":"apiKeyHelper","claude_code_version":"2.1.92","output_style":"default","agents":["general-purpose","statusline-setup","Explore","Plan"],"skills":["update-config","debug","simplify","batch","loop","schedule","claude-api"],"plugins":[{"name":"claude-hud","path":"/root/.claude/plugins/marketplaces/claude-hud/","source":"claude-hud@claude-hud"},{"name":"rust-analyzer-lsp","path":"/root/.claude/plugins/marketplaces/claude-plugins-official/plugins/rust-analyzer-lsp","source":"rust-analyzer-lsp@claude-plugins-official"}],"uuid":"a39b9e24-6934-4433-80a1-093b93180fa3","fast_mode_state":"off"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01Mo9JguaYscXG2phC22n9mf","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"The user wants a playable Tetris game in the browser using TypeScript. Let me build a complete Tetris game with:\n\n1. All 7 standard Tetris pieces (I, O, T, S, Z, J, L)\n2. Piece rotation\n3. Line clearing\n4. Scoring system\n5. Increasing speed\n6. Keyboard controls (arrow keys for movement, up arrow or Z for rotation)\n7. Game over detection\n8. Next piece preview\n9. Level display\n\nI'll create:\n- `index.html` - The main HTML file\n\n\n- `src/tetris.ts` - The TypeScript game logic\n- `tsconfig.json` - TypeScript configuration\n- `package.json` - Package configuration\n\nNow I'm planning the core mechanics: a standard 10x20 grid with all seven tetromino shapes, a scoring system that rewards line clears progressively (100 for single, 300 for double, 500 for triple, 800 for Tetris), and difficulty that ramps up every 10 lines cleared. The controls will use arrow keys for movement and rotation.\n\nI need to set this up as a browser-runnable project, so I'll create an HTML entry point alongside the TypeScript source, then compile everything down to JavaScript. Let me structure this with the tetromino definitions as rotation states and build out the game state to track the board, current piece, and scoring.\n\nWriting the game logic... Defining the tetromino shapes and their rotations... Defining the rotation states for the remaining Tetris pieces... Defining the rotation states for the remaining Tetris pieces... Setting up the Tetris class constructor to initialize the canvas contexts, game board, and tracking variables like score, level, and drop timing... Writing game logic... Writing game mechanics... Writing the game logic... Writing the render loop... Writing the game over screen... Writing the block rendering... Still writing the drawing logic... Writing the game controls... Writing the game loop and HTML structure...\n\nNow I'm planning the layout with a canvas for the 10x20 grid at 30 pixels per block, plus a side panel showing the next piece preview, score, level, and line count. I'll set up the TypeScript compilation pipeline with a config file and package.json, then handle the scoring system using standard Tetris rules where clearing lines awards points multiplied by the current level. I also want to implement proper wall kick behavior for piece rotation, especially for the I-piece. I'm setting up the project structure with TypeScript configuration and planning to output compiled files to a `dist/` directory. Now I'm working through the standard Tetris piece rotations using the Super Rotation System, starting with how the I-piece should be positioned and rotated across its different states.\n\nDefining all piece shapes... Defining the piece rotations... Defining the piece rotations...","signature":"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"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":4728,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":4728,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"6203bd30-b2be-4b06-9b6b-d0c3b85438f6"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01E6Bhhtpi484NnNVE4yKdrv","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"I'll build a complete Tetris game in TypeScript that runs in the browser. Let me plan the structure:\n\n1. **index.html** - The main HTML file\n2. **style.css** - Styling for the game\n3. **tetris.ts** - The TypeScript game logic\n4. **package.json** - For TypeScript compilation\n5. **tsconfig.json** - TypeScript configuration\n\nLet me think about the game structure:\n\n### Game Components:\n- **Board**: 10x20 grid\n- **Pieces (Tetrominoes)**: I, O, T, S, Z, J, L pieces\n\n\n- **Colors**: Each piece has a unique color\n- **Game Loop**: Uses requestAnimationFrame\n- **Controls**: Arrow keys for movement, Up/Z/X for rotation\n- **Scoring**: Standard Tetris scoring system\n- **Speed**: Increases as level goes up\n- **Preview**: Next piece preview\n- **Game Over**: Detection and display\n\n### Controls:\n- Arrow Left/Right: Move piece\n- Arrow Down: Soft drop\n- Space: Hard drop\n- Arrow Up / Z: Rotate clockwise\n- X: Rotate counter-clockwise\n- P: Pause\n\n### Scoring:\n- 1 line: 100 × level\n- 2 lines: 300 × level\n- 3 lines: 500 × level\n- 4 lines: 800 × level\n\nThe level increases every 10 lines cleared. For delivery, I'm thinking about the best approach—I could create separate files for HTML, TypeScript, and config, but for simplicity and immediate browser compatibility, I'll compile everything and embed it directly in a single HTML file. Let me start building out the TypeScript structure with types and core game logic.\n\nWriting TypeScript code... Defining the complete Tetris piece set with their shapes and colors, then planning out the full game implementation with rotation mechanics, ghost pieces, hold functionality, next piece preview, scoring, and progressive difficulty. I'm setting up the rotation system using a matrix approach that rotates pieces clockwise, then implementing wall kick logic to handle collisions by trying shifts in both directions before rejecting the rotation.\n\nNow I'm planning out the full Tetris implementation with a standard 10x20 board, all seven tetrominoes, SRS rotation with wall kicks, ghost piece preview, hold functionality, next piece queue, score tracking with level progression every 10 lines, lock delay mechanics, and game over detection.\n\nWriting the complete implementation... Defining the piece shapes and wall kick data... Finishing up the wall kick data for the I-piece, then moving on to the scoring system and game speed mechanics. I'm reconsidering the speed formula—the standard Tetris approach uses an exponential calculation, but I think a simpler linear formula based on milliseconds per drop would be cleaner and easier to tune.\n\nNow I'm ready to write out the full implementation across multiple files: the TypeScript game logic, configuration, HTML template, and build setup.\n\nWriting the complete Tetris game... Defining the piece shapes and wall kick data... Defining wall kick offsets for I-pieces... I need to convert the wall kick offsets to canvas coordinates where y increases downward, so I'll negate all the y values from the standard Tetris specification. I'm looking at the rotation state offsets for the other directions and realizing I should just keep the implementation simple—basic wall kick attempts in a few directions (left, right, up) will make the game feel responsive without needing perfect SRS compliance.\n\nActually, let me commit to proper SRS wall kick data instead. I'm laying out the exact offset sequences for both JLSTZ and I pieces across all rotation transitions, using canvas coordinates where y increases downward.\n\nDefining wall kick offsets... I'm getting bogged down in the SRS wall kick specifications—the exact offsets for each rotation state are intricate. Let me simplify and just implement a working Tetris game with rotation and basic wall kicks rather than getting stuck on perfect SRS compliance.\n\nWriting the complete implementation... Defining the Tetris game class with canvas contexts for the main board, next piece preview, and hold piece display, along with properties for tracking the current piece, upcoming pieces, and game state... Setting up the canvas contexts for the game board, next piece preview, and hold piece display, then initializing the game state by creating the board, populating the piece bag, spawning the first piece, and attaching keyboard event listeners to start the game loop.\n\nNow I'm setting up the core game mechanics—creating an empty board grid, implementing a randomized piece bag using Fisher-Yates shuffle so pieces are drawn in a balanced sequence, and pulling from the bag with automatic refilling when it runs low.\n\nI'm handling piece spawning by maintaining a queue of next pieces, pulling the current piece from that queue, positioning it at the top center of the board, and checking for an immediate game over condition if it collides right away.\n\nWriting rotation and utility methods... Still writing collision and rotation logic... Implementing proper SRS kick tables...\n\nI need to work through the standard rotation kick offsets for JLSTZ pieces in both clockwise and counterclockwise directions, then convert them to match the coordinate system I'm using where y increases downward instead of upward. Now I'm working through the counterclockwise rotation kicks in the standard coordinate system, then converting those same offsets to canvas coordinates where y points downward. Now I'm working through the I piece rotations in canvas coordinates with y-axis flipped, then mapping out the counterclockwise kick offsets for the same piece in the standard SRS system.\n\nWriting TypeScript implementation... Defining the piece shapes... I'm realizing that using 4x4 matrices for all pieces is the standard approach in Tetris implementations since it makes rotation consistent and straightforward—I just need to handle the O piece as a special case that doesn't rotate. Let me work through the clockwise rotation formula for a 4x4 matrix and verify it's working correctly with an example. Tracing through the rotation mapping for each element of the J piece to verify the transformation is working correctly. I've settled on using 4x4 matrices for all pieces, and now I need to figure out the spawn logic. With a 10-column board, the center position should be around column 3, so pieces spawn centered horizontally at the top. Let me just write the full implementation now with the rotation logic, wall kicks, randomizer, ghost piece, hold mechanics, and standard scoring.\n\nI'm setting up the project structure with TypeScript and creating the HTML layout with panels for the hold piece on the left, the main game board in the center, and the next pieces preview on the right.\n\nWriting the HTML structure... Writing the TypeScript implementation... Defining the piece shapes... Defining the wall kick offset tables for counterclockwise rotations and continuing the SRS system for J, L, S, T, and Z pieces... Defining the wall kick tables for the I-piece counter-clockwise rotations, then setting up the scoring system and drop speed mechanics.\n\nNow I'm setting up the main Tetris game class with canvas rendering for the board, hold piece display, and next pieces preview, along with initializing the core game state like the current piece and queue. I'm setting up the canvas elements for the game board, hold piece display, and next piece preview, along with grabbing references to all the UI elements I'll need to update during gameplay.\n\nNow I'm initializing the game state by creating the board, filling the piece bag twice, populating the next pieces queue, and spawning the first piece. I'm also wiring up keyboard controls and the restart button, then kicking off the main game loop with requestAnimationFrame.\n\nWriting the HTML and CSS... Building the game interface... Still writing styles... Still writing styles...\n\nNow I'm moving into the TypeScript implementation for the complete Tetris game, starting with the core constants like board dimensions and game mechanics parameters, then defining the types I'll need for colors and the game board structure.\n\nDefining the piece types and their color mappings... Defining the tetromino shapes and setting up the wall kick offset tables for rotation collision detection... Defining kick offset data... Finishing up the I-piece kick data, then moving into scoring logic with line clear values and a speed calculation based on the level, plus some utility functions for rotating pieces clockwise.\n\nImplementing counterclockwise rotation and shape cloning...\n\nNow I'm setting up the main Tetris game class with canvas contexts for the board, hold box, and next piece preview, along with DOM elements for score, lines, and level display. I'm initializing the game board, active piece, and the queue of upcoming pieces.\n\nSetting up game state variables... Setting up event listeners for keyboard input and button clicks, then initializing the game state by resetting the board, score, and game variables before starting a new game. Implementing the bag randomizer with Fisher-Yates shuffle...\n\nNow I'm setting up the piece spawning logic—initializing the current piece with its type, shape, position, and rotation state, then checking if it immediately collides with the board to detect a game over condition. After that, I'm moving into the collision detection system to check if a piece at a given offset intersects with the board or other pieces.\n\nContinuing with the collision logic...\n\nNow I'm placing the locked piece onto the board by iterating through its shape and marking each occupied cell with the piece's color, then checking for completed lines to clear and spawning the next piece.\n\nContinuing with the game logic...\n\nI'm implementing the rotation system with wall kick data—checking different kick offsets based on the piece type and rotation direction to see if the piece can rotate into a valid position. Writing movement and drop logic... Writing game loop logic... Handling the lock delay logic when the piece lifts off the ground, then moving into the render cycle and setting up the board canvas with its dimensions.\n\nDrawing the board background and grid... Writing the cell rendering... Writing the rendering methods... Writing the preview rendering... Writing the piece rendering...\n\nNow I'm setting up the input handling for keyboard controls, mapping arrow keys and letter keys to game actions like movement, rotation, and dropping pieces. Finishing the stats update method, then bootstrapping the game on page load. But I'm realizing there's a critical issue with my lock detection logic—I need to be more careful about when the piece actually lands. The current approach doesn't properly handle the case where a piece spawns already touching the ground, and the lock timer reset logic needs refinement to only allow resets up to a maximum number of moves before the piece locks permanently.\n\nLet me rethink the game loop to properly accumulate drop time and trigger gravity at the right intervals...\n\nWriting game loop logic... Refining the ground detection logic...\n\nI'm realizing the ground state needs to be recalculated each frame rather than tracked as a persistent flag, since horizontal movement could change whether the piece is actually supported. I'll simplify this by checking collision directly each update and resetting the lock accumulator whenever the piece lifts off.\n\nFor the lock delay mechanic itself, I need to handle the standard Tetris behavior: the timer starts when grounded, resets if the player successfully moves or rotates while still on ground, and eventually locks after either the delay expires or too many resets occur. The key is that if a move causes the piece to become airborne, the timer stops completely.\n\nImplementing the game loop... Setting up DAS tracking...\n\nNow I'm assembling everything together and adding the auto-repeat mechanism for held keys—immediate movement on press, then repeating after a delay at a fixed rate. I'm also considering visual polish like line clear animations and level indicators, but I'll keep it minimal for now and focus on a solid, working implementation. For rotation, I need to implement proper wall kick offsets following the standard Tetris guideline, where the specific kicks vary by piece type and rotation direction.\n\nDefining wall kick offsets... I'm working through the coordinate transformations for the I-piece rotation states, converting between y-up and y-down coordinate systems, and now I need to figure out the counterclockwise rotation kicks by reversing the clockwise sequence. I need to implement the CCW rotation kicks properly by understanding that they're derived from the CW kicks. For each rotation state transition, I'll negate the corresponding CW kick offsets to get the CCW behavior, which should give me the correct wall kick sequences for counter-clockwise rotations. I'm realizing I made an error—the SRS wall kick offsets for counter-clockwise rotations aren't simply negations of the clockwise ones. They're actually defined separately in the Tetris specification, and I need to look up the correct values from the official documentation rather than deriving them. I'm working through the counterclockwise rotation kicks for each orientation state, converting them from Tetris y-up coordinates to y-down coordinates to match the standard game grid system. I'm seeing the pattern now—the clockwise and counterclockwise kicks are related by symmetry, where CW rotations map to CCW rotations in specific positions. Let me verify this relationship holds across all four transitions to make sure I have the symmetry right. The pattern doesn't match what I expected—the counterclockwise and clockwise rotations aren't simple transformations of each other. I'll just hard-code both rotation tables for the JLSTZ pieces, defining all the kick offsets for each rotation state. Verifying the rotation offsets by converting from y-up to y-down coordinates and checking them against the wiki reference data. Checking the third rotation state: the CCW kicks from state 3 to 2 match my expected values when converted to y-down coordinates. Now I'm compiling all the CCW rotation kick data into the final array structure.\n\nMoving on to the I piece, which has different kick offsets than JLSTZ—I'm pulling the rotation kick values from the Tetris Wiki and converting them to match my coordinate system. I'm noticing a pattern here—the counterclockwise kicks appear to be the clockwise kicks in reverse order with their offsets inverted. Let me verify this relationship to make sure I understand the symmetry correctly. I've verified the pattern holds across all rotation transitions—the CCW kicks at any state match the CW kicks at a rotated index, specifically two positions ahead. Now I'm converting the I piece CW kicks into y-down coordinates... I'm now working through the counter-clockwise rotation kicks for the I-piece in y-down coordinates, mapping them to their equivalent clockwise rotations at different states.\n\nDefining the kick tables... Defining the I piece kick tables...\n\nNow I have all the rotation kick data set up. Let me write out the complete implementation, combining everything together into a clean, working solution.\n\nFor the input handling, I need to implement DAS (Delayed Auto Shift) so that holding left or right arrow keys will auto-repeat the movement after an initial delay. I'm tracking which key is held, timing how long it's been pressed, and then triggering repeated moves at a consistent rate once the delay threshold is crossed.\n\nNow I'm ready to write out the complete implementation cleanly, and I should also account for the canvas dimensions—with 32-pixel cells, 10 columns, and 20 rows, the board will be 320×640 pixels, plus space for the hold and next piece displays.\n\nActually, let me adjust the cell size to 30 pixels to keep things more compact and ensure everything fits nicely on screen. That gives me a 300×600 board with 120-pixel preview panels on each side, totaling around 580 pixels wide with some padding—should work well without forcing scrolling on most devices.\n\nWriting the complete HTML and JavaScript... Building the HTML structure... Writing the HTML structure... Still writing styles... Still writing styles...\n\nNow I'm setting up the core Tetris game constants—board dimensions, cell size, timing parameters for piece locking and input handling.\n\nDefining the TypeScript types and color mappings for each tetromino piece... Defining the piece rotation states and wall kick offsets... Defining the kick offset tables for I-piece rotations... Writing helper functions...\n\nNow I'm setting up the core game class with canvas contexts for the board, hold piece, and next queue displays, along with DOM elements for tracking the score, level, and line count.\n\nI'm also initializing all the piece state variables—the current active piece, the queue of upcoming pieces, the hold slot, and the randomization bag—plus the game statistics and timing accumulators that manage piece drops and lock delays, along with boolean flags for game status like whether it's paused or over.\n\nNow I'm setting up the DAS (delayed auto shift) system to handle left and right arrow key repeats, then in the constructor I'm grabbing the canvas elements and setting up their 2D rendering contexts while also initializing the score and other UI elements.\n\nSetting up event listeners...\n\nNow I'm initializing the game state by resetting the board, score, and all the game variables like the piece queue, hold piece, and accumulator timers. I'm refilling the bag twice to set up the randomizer, then populating the initial queue with three pieces before spawning the first one and updating the UI.\n\nHiding the overlay and pause elements, then starting the game loop with a timestamp if it's not already running.\n\nSetting up the bag randomization system... Writing game logic... Continuing with the line-clearing logic, I'm removing completed rows from the bottom up and shifting new empty rows to the top, then calculating the score multiplier based on how many lines were cleared and the current level. Now I'm moving into the rotation system, starting with determining the new shape after a clockwise or counterclockwise rotation.\n\nI'm setting up the wall kick data based on the piece type—I-pieces and O-pieces have their own kick tables, while J, L, S, T, and Z pieces share a common set. Then I'm iterating through each possible kick offset to test if the rotated piece can fit at a new position.\n\nTesting rotation with wall kicks... Writing game mechanics... Writing game logic... Writing game loop logic... Finishing the grid drawing, then rendering the locked cells from the board state, drawing a ghost piece preview at its landing position with reduced opacity, and finally rendering the active falling piece. Writing the cell rendering... Still writing the rendering logic... Writing the block rendering... Writing input handling... Writing game logic...\n\nWait, I'm noticing a potential issue in the game loop—when a piece locks and spawns a new one, the animation frame continues, which might cause timing problems with the next piece's movement. I'm realizing the loop keeps running even when paused or over, which wastes CPU cycles since we're not updating or drawing anything—though that's acceptable for this implementation. I also need to double-check the DAS timer logic: it should only tick when the game is actively running, not during pause, so I need to move that call inside the pause check.\n\nFor game over, when a piece spawns and immediately collides, we set `this.over = true` and the loop stops updating state. When the user clicks \"Play Again\" and `start()` is called, there's a potential issue: `this.alive` is already true from the previous game, so we won't request a new animation frame—but the old loop is still running and checking `this.alive`, so it continues correctly.\n\nHowever, I need to reset `this.lastTs` in `start()` to prevent a huge delta time on the next frame after restarting. I should also handle the pause/unpause case similarly, resetting `lastTs` when unpausing so the DAS state doesn't get confused by a large time delta.\n\nNow I'm checking the ghost piece logic—the `ghostY()` method finds where the current piece would land by incrementing down until it hits something, and `hitsAt()` validates collision detection...\n\nChecking collision detection logic...\n\nFor rendering the ghost, I'm thinking about visibility—an alpha of 0.2 might be too subtle, so I could bump it to 0.25 or draw just the outline instead. Actually, keeping it at 0.2 should work fine. I also need to skip drawing the ghost when it's already at the piece's position since that means the piece is already on the ground.\n\nNow I'm reconsidering the collision detection methods—I have both `hits` for the active piece with offsets and `hitsAt` for absolute positioning. They're doing different things, so keeping both makes sense rather than consolidating them.\n\nFor the line clear animation, I'll keep it simple and just remove completed lines immediately without any flash or dissolve effect, since adding animations would complicate things significantly.\n\nI'm also thinking through the game over flow: when `lock()` calls `spawn()` and that triggers `gameOver()`, setting `this.over = true` will prevent state updates in subsequent frames, which is the correct behavior.\n\nNow I'm putting together the final code and need to verify the canvas dimensions...\n\nActually, I'm reconsidering the visual polish—things like dynamic level colors and \"LEVEL UP!\" indicators would be nice, but I should focus on getting a fully functional game working first.\n\nFor the board dimensions, I'll use CELL = 30 to keep things compact, giving us a 300 × 600 pixel board. The hold and next preview canvases will be 120 × 80 and 120 × 240 respectively.\n\nSetting up the canvas drawing...\n\nNow I'm working through the layout dimensions to make sure everything fits within the 600px height—the left panel with hold, score, level, and lines sections should take up around 260px with padding, while the right panel's next queue needs 240px, both leaving plenty of room. For the overall width, I'm aiming for 584px total with the 300px game board centered between two 130px side panels and 12px gaps on each side, which should work well on standard screens. I'll use `align-items: flex-start` on the side panels so they align with the top of the game area rather than stretching to fill the full height.\n\nBefore I write out the final code, I want to make sure the piece spawning logic is correct—specifically for the O piece, which uses a 4×4 matrix like all the others but has a different centering pattern. Spawning at x=3 means the piece occupies columns 4 and 5 on the 10-wide board, which is perfectly centered.\n\nThe I, T, and other pieces also spawn correctly at x=3 with their respective column ranges. Now I'm checking whether rotation could cause any collision issues—when a piece rotates, its x position stays fixed, so I need to verify the rotated shape still fits within bounds.\n\nLet me work through the T piece rotation manually using the standard clockwise formula to see what the matrix looks like after transformation. I'm working through the rotation mapping to verify the final result matrix, and after applying the clockwise rotation transformation to the T piece, I get a matrix with 1s in the third column at rows 0-2, which represents the rotated T shape correctly.\n\nNow I'm checking the positioning when this rotated piece is placed at x=3 on the board—the cells end up at columns 5 and 6, which does shift the piece slightly to the right compared to its spawn position. I'm considering whether this is the expected behavior, but the SRS wall kick system should handle any positioning adjustments automatically, so the (0,0) kick attempt first ensures the rotation is valid at the current position before trying other kicks. I'm realizing the issue—my 4×4 rotation matrices aren't matching the standard SRS spec, which defines rotation centers more precisely. The T piece should maintain its center point during rotation, but my implementation is shifting it rightward. I need to check the official SRS rotation data to see how the spawn state and rotated states should actually align on the playfield. I'm working through the rotation formula for a 4×4 grid, testing it against the T piece to make sure the transformation maps each cell correctly when rotating clockwise.\n\nThe result shows the T piece ends up too far to the right on the board, which suggests the rotation center at (1.5, 1.5) might not be the right approach for how the piece should actually position itself during gameplay.\n\nI'm thinking through whether to use different matrix sizes for different pieces, adjust spawn positions per piece type, or just accept some visual shift and handle it with wall kicks. Looking at how actual Tetris implementations handle this, the standard approach uses 4×4 matrices for everything but varies the spawn x position depending on the piece—I pieces spawn at x=3, O pieces at x=4, and the rest at x=3. But that means I need to figure out the right spawn positions to make the pieces land where they should visually.\n\nActually, the real issue is that the T piece visibly jumps right after rotation, which is definitely noticeable during gameplay. Instead of trying to make a generic 4×4 rotation system work, I should use pre-computed rotation states for each piece, where I define exactly what each piece looks like in all four rotations. This way I can use the minimum bounding box for each piece type—4×4 for I, 2×2 for O, 3×3 for the rest—and avoid the position shifting problem entirely.\n\nDefining rotation states... I see the issue with mixing different matrix sizes for different pieces—the O piece doesn't rotate so it stays the same across all states, while T/J/L/S/Z pieces use 3×3 grids. I'm going to standardize everything to 4×4 matrices to keep the rotation logic consistent and simpler.\n\nNow I'm working through the T piece rotations manually to verify they're correct. Let me trace through what happens when I rotate the initial state clockwise using my rotation function. Applying the rotation again to state 1, I get state 2, but the piece ends up in rows 2-3 and columns 1-3, which would place it off the bottom of the board immediately at spawn. This reveals the core issue: my 4×4 rotation matrix is mathematically correct but doesn't preserve the standard Tetris piece positioning—the visual center drifts with each rotation. I need to use a different approach instead.\n\nI'm going to define all four rotation states explicitly for each piece using their proper Tetris positions within a 3×3 bounding box, then pad them to 4×4 for consistency. Defining rotation states for the T piece...\n\nDefining piece rotation states... Defining the rotation states for the remaining Tetris pieces... Defining the rotation states for the J and L pieces...\n\nNow I'm carefully working through each Tetris piece's rotation states. For the T piece, I need to map out how it rotates from its spawn position through each 90-degree turn. Let me verify each state is correct by visualizing the piece in a 4×4 grid and making sure the rotations follow the standard Tetris rotation system. Now I'm mapping out the J piece rotations, checking each of the four states to make sure the block positions are accurate in both the visual and 4×4 grid formats. Defining the rotation states for the S piece... I need to clarify the S piece rotation states in standard SRS. States 0 and 2 are both horizontal orientations but positioned differently, while states 1 and 3 are both vertical but with different offsets for wall kick calculations—they're visually identical but treated as distinct states in the rotation system.\n\nRather than getting bogged down in exact SRS compliance, I'll focus on what matters for a playable game: the pieces need to look correct at each rotation and have sensible wall kick behavior. I'm defining the S piece states based on visual appearance, with state 0 as the horizontal orientation, state 1 as vertical, and state 2 as the horizontal rotated back. I'm realizing that states 0 and 2 for both S and Z pieces are visually identical to their counterparts, but the rotation kicks will differentiate them. Rather than overthinking the visual representation, I'll just hardcode all four rotation states and let the SRS kick system handle the actual positioning during rotations.\n\nDefining all rotation states... Defining the rotation states for each tetromino piece... Defining the rotation states for the J and L pieces... Verifying the T-piece rotations across all four states to ensure the coordinates map correctly to the 4×4 grid format.\n\nNow I'm checking the J-piece rotations through all four states, mapping each orientation to its corresponding 4×4 grid representation. Verifying the L piece rotations across all four states, and they all check out. Now moving through the S piece configurations to confirm those rotations are correct as well. I see that the piece anchor is fixed while rotation states define where blocks appear within the 4×4 grid, so state 2 naturally shifts the piece down and right compared to state 0—this is correct SRS behavior. Now I need to verify whether S state 3 should be identical to state 1 visually or if it needs a different configuration based on the anchor position. I'm confirming that S and Z pieces have matching states at rotations 1 and 3, which simplifies the implementation since the wall kick system will handle any positioning differences. Now I need to move forward with the next step.\n\nI'm going to stop overthinking the SRS details and just build a functional, enjoyable Tetris game instead. I'll use hardcoded rotation states, standard wall kicks, and straightforward cell-based rendering. The game doesn't need perfect SRS compliance to be fun and playable.\n\nWriting the TypeScript implementation... Defining rotation states for each tetromino piece... Defining the tetromino rotation states... Finishing the Tetris piece definitions...\n\nI'm noticing an issue with the S piece rotation states—states 1 and 3 appear identical, which means the piece would look the same at both positions even though SRS will apply different wall kicks based on the rotation state. Let me reconsider whether the visual representation actually matches standard Tetris Guideline behavior for the S piece across its rotation states. The S piece actually has rotational symmetry—rotating it clockwise and counterclockwise both produce the same vertical orientation, so states 1 and 3 are visually identical even though they might be positioned differently within the grid.\n\nSince the rotation logic cycles through states using modulo arithmetic, having states 1 and 3 share the same matrix content works fine—the system will still progress correctly through the rotation sequence regardless of whether intermediate states look identical. I piece has a quirk where states 0 and 2 place the horizontal line in different rows of the 4×4 grid, which is intentional in SRS and handled by the wall kick system. Now I'm ready to write out the complete implementation carefully. I'm verifying the I-piece rotation states match the standard SRS definition, and they do. Now I'm ready to write the actual code—I'll set up the project structure with HTML, CSS, TypeScript, and config files, then compile everything with TypeScript.\n\nActually, I'm simplifying this: I'll just put the TypeScript directly in `tetris.ts` and compile it to `tetris.js` rather than using a separate src directory. Now I'm reconsidering the canvas dimensions—the board should be 300px wide and 600px tall, with 120px-wide side panels for the hold and next piece previews. For the hold canvas, I need about 100px × 80px to accommodate the largest pieces with some padding.\n\nI need to stop overthinking the layout and just start writing the code. Let me commit to the file structure: `index.html`, `style.css`, `tetris.ts`, `tsconfig.json`, and `package.json`. I'll use pre-computed rotation states derived from the spawn positions rather than hardcoding all variations, which keeps things cleaner while still being accurate.\n\nActually, I'm second-guessing the rotation math again. The core issue is that applying a standard rotation matrix to a 4×4 grid rotates around the grid's center, not the piece's visual center, which causes unwanted shifts. Let me verify this with the T piece as a concrete example to make sure I understand the problem correctly before I start coding. I'm wondering if I should define the spawn states differently to make the matrix rotation approach work. The problem is that pieces start in the top-left corner of the 4×4 grid, and rotating them shifts them rightward. What if I centered the pieces instead? I'm trying different configurations—placing the T piece centered both horizontally and vertically in the grid to see if that produces the correct rotation results.\n\nLooking at the actual SRS spec, the I piece occupies an entire row while the T piece has a specific pattern across two rows. The rotation center sits at (1.5, 1.5) in the 4×4 grid, which is why my rotations are ending up in unexpected columns. The SRS system handles this by defining the piece anchor point differently rather than relying on a simple matrix rotation.\n\nFor the T piece specifically, if I treat it as living in a 3×3 grid with the anchor at (1,1), then rotating clockwise about that center point gives me the correct state 1 shape. When I place that 3×3 content into the 4×4 grid starting at (0,0), I get exactly the expected result. So the key insight is that rotation needs to happen within the 3×3 space, not the full 4×4 grid.\n\nNow I need to work out the rotation formula for a 3×3 matrix...\n\nActually, this is getting unwieldy. I think the most straightforward approach is to just hardcode all the rotation states directly. It's what most Tetris implementations do anyway, and it's guaranteed to match the SRS spec perfectly. I'll define the states for each piece type—I pieces use 4×4 grids, T/J/L/S/Z pieces use 3×3 subgrids within 4×4, and O pieces stay the same across all rotations.\n\nDefining the piece rotation states... Defining the rotation states for the remaining Tetris pieces... Defining the rotation states for the remaining Tetris pieces... I'm working through the 180° rotation more systematically—reversing each row and flipping the row order to see what the Z piece should look like at that orientation. I see that the Z piece's second state needs to be positioned differently within the 4×4 grid—it sits in rows 1-2 rather than rows 0-1 like the initial state. This reveals the key insight: SRS pieces intentionally vary their internal grid positioning across different rotation states, so the visual placement depends on both the piece's y coordinate and where the actual content sits within that 4×4 bounding box. Defining rotation states for the S piece...\n\nI'm realizing there's a potential issue with how the piece appears to shift vertically when rotating between certain states. The S piece in state 0 occupies rows 0-1, but state 2 shifts it to rows 1-2, which would make it look like the piece drops a row. However, the SRS kick table should handle this adjustment automatically by modifying the y-position during rotation to keep the piece visually stable.\n\nActually, I'm overthinking this. In real gameplay, the piece won't be at the spawn position for long, and the SRS kicks will compensate for any minor shifts anyway. The slight visual adjustment during rotation at the top of the board is negligible compared to the overall gameplay experience. I'll stick with the rotation states I defined earlier.\n\nNow I'm verifying my piece state definitions to make sure they're correct before writing the implementation. Let me trace through the T piece's third rotation state to confirm the layout. I'm verifying the J piece rotations by checking each state in a 4×4 grid. State 1 matches my expected rotation with the corner at the top-right, and now I'm confirming state 2 at 180 degrees where the corner moves to the bottom-right.\n\nNow I'm working through the L piece orientations, starting with state 0 where it has the corner at the top-right, then rotating clockwise to show the L facing right with the corner at the bottom-right. I'm working through the rotation states for the L-piece more carefully now. I need to visualize each of the four orientations—starting with the hook at the top-right, then rotating clockwise through bottom-right, bottom-left, and finally top-left positions.\n\nNow I'm verifying that the spawn position works correctly for all pieces, particularly checking that the I-piece with its 4-wide span starting at column 3 lands in the right place.","signature":"EqO5CQpbCAwYAipAhVqJxaf4QoSk6aN0R6XWeQ3y2pZp1UluzcvKCLlNeGVMThGNUY49shVwxhD8kB6bc9o/0f2sEJVXSE6eSbu5bTIRY2xhdWRlLXNvbm5ldC00LTY4ABIMGLhgsRGPlEj0saq2Ggw6u5fectBoYGBhVZUiMN5FNgF2/WQaG30Ocf7a0Qs+UkxuYUzVklcJqaRD1MRBJnbU6lhKbn2q7PvGlQE8Qir0twno00vBfo+RQhpNC+3ggEld5eIEOMFSILDRdvvUYqYGovm0DJe+MfDForMV3TIgluTtiyA5SkONPxnWzFqdK+gKjsjQYOFgx/w+Tq5V83gUlgFPCK3u/I3mbY4M9O9nDmH29pwETV0Tm4mmKZJqXa+Dpoh9XalJ3g+VKh00H/JjFnmwjaOrXMf0ZCw0DgbyW4TSnhlmlbW1osiuiScMEjKzvEi/Lm+8XnunPMG6cNz/xStwpf5cW6xLJ/jnF5nc98qZe6Hu5TX+b9AjQy1BJTGIC/SJlRD5K+Q4VgLSQxUuJTBFZiBlMQIULEBLrTHKJ6UBOnFydQ6wkKJLcsNQaYIspr/7LRNEGHERQwUvP9y962TUjww0x9Ug1pGfw77C/vd8jUdl3tO0smWAOYVDJYCKV0pO1hBV9oP2PBtXAtx1CDa1bkZudgqTqdQNNQfWQyLL2MSMChRp/Vh09Chk0A6w8lZy1ldIUdXt7qGf/uMsFg3pnHwwuntzvHWNR+UatMewqYlMiSfArLqWf3L6I7jC8rQjH1MlyVOBFXkLKFFRt/+lu2vt2K5dtJN4NSzu94oLGQNeaGiAeHL4cP/3Am8t9tK6nBxUthTPdCkqD98Dz5bInJUsYgOaeCVfGE+2R/xHvuY1Q69BbttzG9bM5Vp1skXQDvQYT0karcOPyn3tgXyRGcFeZ39HbdjDTIIcYdRZGVca+eg4z6xSQIjh3L7N+HlrUo16EfzHTyQRzvOtgFv1sbtVqfmyl0eVu1r06deIhT4apsQbXW4IVvEPMvQ85BsWGE91SDltmy57wh3mj5kV1MNawHiD3D/wUsDlpRMbpjOJs6H3JXMe95AH2/pCOyxwYinbwEvRZPKWnCcQQUw9sIhgwsHsq2Hi6tpt34tHtpT9jnAlsBW85H7VkJBCk9lASA6qiQlzJK4NvyBz5UNEDJXmb+A6OPBajSCVAaztLiU5o+nY9d/QuaZRYBbnTvk0RKoxzTeQlVALmtdvflJoRLraQ4FhkWdM9MdWNvoeMigF9IYGN4ZgiBu/6HDuIz0KmUP7RaqFLmKPJjFGNb5ybtNFRuCoZUK7+doady9SBLCA1Iga5c8VZm4/Kiw5uP16Fz81YMGiZ8S+7xzbrzfFVe8sdFKK3icXycCSzafzc8cyx3u5DwBo2RP55pOkCnCtLmhB7yxg1rzXzDa2AM763kMF77zvFBKJVN2zO5JTPpMHP2yW4DEvS6AbIaU2X9AeWk47nnHsQ+1FoNlR56DVJVEFwgDoSo915GwOKHlendgTPny4sAARCav5AE2R9uCqgFB9riGhuCeAk/82J1yhMh423SLjo/38f1tfLaYTxWJuCKwQNlgKfc92kWXhk2suv8KK0SMuIv6Cw81s1C1Y4xqVngFGvS0XrBtoxRVflAeCzOB+7tYYHs05+i1CiD3yteCY7Lz/hUgAdc/tCscXCafttIFh0SI58FVziw1/EishBVUncpV3eJiUa4qwKexPWgOLN5nXRXhzh5WQ9q0726pQ648Zm690xcIjNF0vHqOfYvUWwa839p24fDDLcEV+bPllBLbCJA61WckmADawAdxaM1Hu6JVNrVSt9ljaZD+W2Z9Besx1IpVJjgTS/3mHO+uLP5jlZ8YHwPv4d+jLIoVsCB1OP5RFhcV1vysfNE3mcZv3+FO0Y/RcqgegSXfdYLRDVAKRCWKOJIxyQASjq+lgPkAAAJM0VCY4n1x+TUVihbpf8BUN7mud/wCeqGAloOA2cyaY2KyekTUDGtQ/LtWpyt5gUhKAVwioQyD6CbkQPsAJ74HATa4N/c/YOHR4IFAbSCVooRsbdt1se1aDsM4PwhHC7mklm+i1qFaYY5Ih4nigIdNHE2q2F9YxMeW8M1uSZp+NZOG+H6T6APKKsKCmDQ98wY/FDkSf+WPM13zLdYh14QQvGwFAkCLSIjwN7BuI066Ri3HqVgGVwgxv20hxsofE0KsE3agEIIl30VLTmc8Rl+nm239EmPmpXKMq1kDNNgBjmovyU1Lm7hNCSh4blnnGUxyqOOjcTHHUT14nSfeK9jSWyyS/Mtz28Z/huq9ycPZ1K3kV9uGsTcetd/ZO0OyIjp8hrElzPlOHBBqZl+eNwrcRtjOoByeQAKC1cJkyd7nf2iBVw4nYc8A2MBvF/V+wwDt1row1FpPUT72NeadzAH/tzUVNZ3F3uDvY18aqt2ESH9+YAkKC1YGBuPZjVImWJmJ1KkaOtcHqkXQLVaUfKtnvJWr/0O+HhInDoVaXk+T3mKvlx/2oQGRJo9niMMSH9sQTIinJO2fAg4ulSWgheWz+PJeuHrNEmyDUIYuic602Lr8gEfM93fBae6ER6bhzAE9UYg3mNDq42jqL18cjZFBjWx+z4QNbZCL8M4cJSjlPNODgR2sNH2MXZfETAXcG07dBVTlD+3HMgglOGW6sm9FDf15Xw7cEBDv1kTQNKCvCp6kx+gM7J4+d3pUHIWhspvKyxzU1X2FI61KfqweCziUZAePe85Y/2CbnzhggP+AW+oKiQ9741wyow9F0s1oEI9EChK/2ZTo1UIp2F513Wo4nQSIN9p+c1avOTRW6+KQaGK5YNZjAiFQXcBwNBzh0U7usMIUcIztaaYfTikewfvnwwws+i8snJ5TtqhlNkfjILefXiEykrt3+Yc7T/Qtv3Yl18sgWe4nI2VXwMBd8ppvDqe6QDgdR6B6kCwxbTQ8SNt71u/lgFCUTrV8+KXvRLB5f/Bv5XABu8PdXKdmc+z7uKn2X7dCS/85dzSAIxG9oK/n+g784QcadnCUfH6G2dc4y36WCS99CKDISD1cdNdKJ3h2QRCrY5ZabyAGoH89+O+gec5nWoJflW++kJDTNlSSV77CnkrlMrCyVqiCij28FC0g7Ms+5OHYkeuFPCJkngyBcNFcAfGIKtyssSuDqdaqWf08QVc0R7gwQJYRZcbrY9mRnylUrRQrLV4FZnCIxr3DtItJ6B+Tz7lvZUHdVxmXGrelGDfLkyJkB8GQP1WqMFmLFt2mQmu1rh5QWmtm/kStnRxGVddDOdknhZKxcU8ABvh+tRR5G3RP9xygOqSNV2FviLb8D+wKzouD8uyXkM8mxRm3SJi8XmlE3dWHCsceQXETgGrL8yJbOQcnwchOXH11EaHZLLrfAgD6uha+pElpDtJ2m5VR90phfndEzhdh4EpXVpQ1YJIsJwcsMbTevgQaQUDz2uwCEQKrMT2AVAkuQGMS35VCsSHHmRbU0Hu7snuwgoDdB9vecVzd/wRyW4KjMdWdECyJ9ey5OtqJTaAiJ/noR5MiBAdtMP/4p0ke+gXL2tYYYGcJI4s9tRlTH8wzvyeKR6cfzv5yMk1BmDHco6WPMN7h3Dl2SuNDVhelDepHH1nqik1vU8u4aiaQ9cYhSxzJnqMDZWH5N0Kshfc7842iVySFyfFwRrrCghQww/c4Y7SzVZ2yor6m1j1UI1ly8rbhJ4PRuaiwmT3n7lQPR2OxZSdZq5R73a9gnj/DRZqz353f6AcekeFoccDDxwnch4rOGyKWfJe4YJlN7obKAUxYWIbJfhcTSQSMUJGGBBihZk+Td4rXxjfO511pPD8ZOuHY3ia7mg3CMnaULhIKN6P4+KdmOtCzfvT/mMG84GRq528AqGcRgWgS37zAclXznusBvjL+FhD7O7Tmp7qLyumVOLmZqsZj0EpJLw86xHfTycsrKhEaud0k+APZAPE/TsY3taIxnMuxMnWV6anCycMTVmnOo08vUFXrOMjq/rUgMsN1dTE+geyldErw6YZARCOr0vWiEtsJRg84BPIEQHvXU587ey25X5UqD4V/1/HVYBMqzx/CJhqJhFLHAtjF4PlOamyTYkI8EY8bRfGqiCBsw/qsQ5iIKEXAjQzqocUfngRX+6S5VUOckqz0P/rUwiWXN+oLaCHzrWLfLJZBgorZgh32nw3Wj0VmCDDKfUkxkhowxUP5HQrqPdSVQyIpuEcKWr+sXPKv4Y1+Vpa71ViyCPPFOeOcwDJNw7Ic152L5zC4YatgJxU+pH8A/u6gCZnMAMlkNuQJJDOkDJm7T86Sq87L9vpTIK4HYUYx9tD8CKHJuDTJUuR/u7DvLFBM2JlH74nxMm91EUF4SnS2rR9cV4I5TbKSnIjTGDUnhx2OwQbgTRRbbdxi0RqEqWXYeGzUwMV5LvKDkfcmA/x+W+eUp8/0uWiQhWDqx+tpEfS+e9qhYbPS0hFEH2Vv9bfhQav6XEpDB4yGvEoSAfd5RT21UasydWUBXmn1n9fowUlXTPskc0MKIw+bbd2GQ+CL1fvgHzVd9POW79zpojEued1eCtWTJ9H+z+l2d4volGr4Tre8/Ds60YjmR8FyaqE2GwuVHuMa6WTSdigfcAdgLo6MJFc7WivYKNl5bLq5ny/bFYIUiRiiHTmeVzYLwaL9AIlZRDxt+uZjQsoM/ev3HKuFGl7HaxU/qKBBS+F2/Z/93zniPoSjoymTJXjLm1nrBFvsho9uCWxDeJ2toTo8zjQ0MNa6K9JvI7Vdq+w6PhtIqVJ2ebxNN8aI09lJ0m6p3LSsfcPWI0xBddk7cc7CJDK/FS2YtQINt8f3KHfg+ZLXCc6M+nPSkuTwMJt7sT276A9vtbeexjhIf5KJ9mwxFFBHg0rYAkmgvo1BLaRNGU6G/mm+iDkfKuUsBnZeSnj36q2BWzIYoCjgXeXNK4F/N5/txaR/MPRMDXVN8KWBvk0hPsG/zTCmFv3niTMe4XwjhU8apoVKfvI3hQY8WMnvyj9DwztZLYRZi+6YzRAvNIN97s8AWGpna0L3v3RA0A832Jrl2Vvn5D++BvGtiMRODvMe3dkM4HAo1vqu8tZDySmpMIj8OGQUmC47l+EZE3SMxUvTkqFlQ//6iMU05Pz71Z7Uw32U999L7jl4pi8858mMhd+fl42UBbUD05/cAcou2WHGZ0wZiaXeSqaddJ6+cH90PDxDZYfaweVqKVQSjS79jek9zVfslMvH78Un0CtyYm33n/Tk23KE2MwbMfopUxCagMUvvqhHsq0V8kDDJuAG8wl8ed26V1Vz3LdzkUAo6Z8t1lRVoQ9EY3b3Ai9zDl6NLOG+GHP21UXDEsriJug5uWE36cTNc1ks2J9H5daWaoa6N3UT9fX5G6eZ4AkOAAATMtGeYC9QdgTyaRMGSsojnipKQq0FW8Io6SKHLytEXnBoCcpBHvTgpYfs1dInr5H3q5bVsLSBW3tPvgPXkGvqESiF3eQUzHfr5E9uTEnTR9/lWxnfcUgBYe4MYdQRcTT+lW5As/J5j0BTRe8I8Oy1gq5WBZu8xnx5tVct89+eRKz4nwMdid9aSQL7b0YMUfqxCStMePFSPbvdcqSvrcuJHJaT94petIYr2XJyoDKfmz2Rqu8VDSVCadrC7NGBUqCC4b+MaPuUgSAPJm5IR/fD8+EWJahyBHufH7ftD5n4X7A6Qe+j30FXLvC3/eeilVxjp9XuQw4m1rsnxWmDM0lj1x3MROyaIn2cXHcp+x8AKoOh7DtHsWDs09LkBcpDJ11O1gkacFvfgPk39Hw2G7rBafgpzFXKOQ0n6UZnZKl0ki5h2RYfVJJlcD+yFgd7PpMSepJcZBoif788UCUAxoxsRvO4L2x8tsmFfjX36xvrugf0hEuYe68g9u2vUOm9pFYcTRm+YjmxplmiPY6VzXHfiq+Fjn52H0VTcwleRmGq9MbZMvka1vBHyrGVmObifqX4BwDrGO2fmPlkEFHYtG5orSMBjy5M/c9T4tLTY/f/iwubC8R7VXSqn5vnpeCBIbSDI5AYXY+y++sq9y7ZpyNj6OWFNaOMhbLrHLn66yJ0Sa/KIt3bYR6gNbKoEop2bZQaKmv8bPmY16m/r4TCwpwYAuvGJdyH1SPptM8hO0UPUvGj2yVmqosFW42SWtApbWWNrGvHdMZ/Kq7jE4FFTBZqO2mH+VK+B33PLFCT4Fkr910jyUW1duVvU9h87f8gz4VB+N1cI87jB7cjEQMUWYMeDBBD9r/pb5iXND6s43Y3Lgu4W2OIZCGJiF7eY8/xLOTeiZyV4PSaSY1iXB95FBhZzH/3Dg3kdMhf4u1e5s4ZDysRWQwc8bRt5+nCeTcRVAtxOsYTlrVOvbg9bl/baYvG2S2ge+DDUPJAV4ymyAW+kjhrmenPujf3OUse3nLPR0OS5pYpLLcMpk6f4KH7WekuHCR3YovbFMdF1EqyQa80CXzCfxr375O/LvfzxrwKV08/Jl7NBW8sKMSa6nuIzQ9Eaj7+Vy92smQWfIprzSDrBjMuNWsuvtDOyfcRIV4vA5uMnxT9S+opG28RR/Eb33rs9jwnQYKqRhnCDthzrDxoEgc49ciP3vTH7N3m6jdWwls3GyRDicVYHtoYRhZam/lxfqFVaKyAMWlG9Bn5tT3OZ/PYPkMoaHh+2wt67an0B4JETuWfprxhWPv9a4fBBe5b8TLVaZETh8pHtN3VBapX4av7C63I8NuFyz4T1OOrpoLLsV09OKEvTTtgRMeO39L6Qvx/ov5OEvpzDfVFnG4FAtT54EHAT0GEniAW6ojiQ7V0zRGB4DN1s+Mo7Hc63jWeytbToR49clWHHo+pFzFDM8fIX1HzHP3gW6a4UipKNoHvaobv95Uf2x4xo3bbB3qRgtbPz9pPOVjz/sw5kTGvenQzChICFogwUodUD/UX+Ji1eMKb6UIGk2GwwsSuICrgSF3kyGFYtQzK4CP41dXxm6sb4GSIZPl0napDY4ESwAtEpTmBnJEJrWkvOSIVTkkTts+mY1//K+MK3oeHpTCrV4LflGw2rxmDsk719dCXYGpfi4tSPPJytee3a57N3H7LY8JKhNSy1jvw5FuhDWOZinLCHUtNu6FpNsxIs+nDZzboMU9u7SdMvBP8B0+DXtf3DfRf3hR2k2Sy8N61ULtmc5yPMdwPhCP6iUQpT9WOzuedDjiAzvBlrKB6NHxVOhQTXteX+7CkIgt5OspZQlf69kqkYrcbNdWCLkxeqXaWmvnrcGy8SQv9Gxh6jN8beAzk9tsID6CGHqo2GZ5pbtYpYdwxeI0/x6WgER+gqXJEgwW/Ki+lrUCJB5yZbDuhD3/Qjt0vzL4q1AhKiVZ6r+sv+PUXPZvf1CUwltSIen0s3hkd4Nvx8vnG7H7o2PZYoUD1+z7vQf1ATZPn0fPYhkSc1crnv3KyTQJpEO/FNbL233yfB6utFbWlnTrq4lzx0+7nXwW6cV5DlLUFsNxLf4VVrmzI+P0llrXpl2QxEf3lTEKRokOyoiZJgcOTabwWbhZvAipQB556O8PIOTejxVfNKBH1wxzgNL3yI5O+Q2IAUI0G9lrUy7DZXI4LM+pXZnqFIMF1tnv3hGnBOBsBE65pEzENbuyAjbcuHY+7wlB1slHXpPH3b2GZYCSmyvcf10bM8ER1r9Do6WlHno+fosv+k8yAtGwlQeJUrYGGpmFPfa9f30Z95asQOeJlU3PUCf88xtpN5r37inf7lqnjCwaYnwEaSaWgCTIqanh0vODe/GPypnaqJucdVceyqiL0wfxkUuvM75fsYAmwOaxiC2aD92sP3xU6xPJNHEKyj4ZMw1X/vrs7bviRe5KnN1oFP8hx3mUxzvrymSYWAOXuU3dTxFOG0GfAJQHlCJVxCGv4Wk9qLwv1Iqu1MS0S00SMaljTmOK1fCOl3fx+d3/agUL4kCdEeKygOkRIKRTHxg0cQ64ly7Msht0Mxa/k3mzec7ZFll496Tid8AD0qFE0PDkyXyp049Chs6SnM8I7U5UFp8bjCxkO2bqw++W6EPk57D+LtV1cwGwK8gA0ODMr2Rp/64/VTPyYjQOCiKUy2upm7851w6vLiddKmVLW083lCXef4WC7CVmNJiA80+bcjzIIdFoG2yRJFReYrV6XqRqNSHx9W/JqaZBQcMboYYxhZGLXwZxh+QDbCdwBAZH58rrBkY9Mktukw199cymDvOZ3VqzxHgrBsKFdA5G0nLIYbHMZP5nYlKwHsm00n9VXtl82GLj+8hHgEmQ35SXidZJ4Y06YWIO5/fvZVJn8+fktwUoKiNjNTw2NAnu2KDg2WZCP5b77NuxhozK/szYehsdYpYvtsJjuIE7qGn+JNk0PIBDM//GiQGVPcpssh5D58k9X17uyLKI1D2AG0/e9oKjzonPZXNIXthw2HyeyYWNWqtalZ5ig2U2iMKw4Z6CtX/1M0G6PRzJtwzP/pmcbdxroZNOqsGKgJGhviDsPFtvs0YXfl5KI2fcHtwXt9KhAFpWgFZZe/Bkw6k9pBYMJC4imPInGdsGOhqbfMmdKG4ngaRK14gOwLTcbLGq2UZk3s/jgnxWy7yg+CsqcspebkPdffZQdd0XOB9phAbGixHqVsrRfP6p1vhPM/jlbYGdE+sBkCo2817O0piOjUoM5jeAbGBsEHNd7m1X3b77QvccvWVvt7TcZ7wvGgdp6+7jhrc9gEM7GzS7LNPvPv/q6K3CNXRxVhMxnBLtEb4i8iceRCqXz981nkmB42jleEme/vvKRj5t+tDBRGVxVvp0u/5Im/ApmeH8DlSpQuqI39emmxgTN42PILR/9UUGd2CCts5CQ5GB2hMHp5ceJBpArbg4lX30muN9rvJIl4T12Z0A7NPDo4ZiKw2Jwi+K3UAVl4GXXp2dLlwZ+TEeZaYl54yK6Fb8SxK6lffQTYJBSavphGIuuJGvMo5wY46/Vou28iZbgktwMjMBWgCdBLB6qukbEPIZ7/HQb07qqzBM2YrYE9V8ruLQCYxW4La201cMnXbCiPUNvGRSsWdYykl0wl/25Oe2bimx9NuqMPzeMoQ/wBI01vM5R3xA5EBF501/iiiLcMI9OG33GJ8kFlaVUj1ghMkRoRjp595HsmQ/Bck1DDLx1TDSheu85MYRUN6mwmsopM/NrE7fSFl21uVonicP8Q6TV86poHN7dQKflol69u4Lb2Ngwr6rCJ8RAcbYX4xe8gNVQEF42G41h6oUpe2D4bBykJZS376WlODDVI/68a8wQi8qqV3WaLYKt5ckcavVGU92BsK1PcDOJMiooddnvjwXgvxHjSsJ1A0SvgCnBp4cWjxnYEvKXsHEKC+MKKtVH0or5s0yfO5eQp+0jtLUvvvm/GsYuhdza1zI2jDcLdrfStchTMo5ztGXrSQHxQZhXekD+Bm8bZllMz9UJNG5WY05IzE9dPucWHYXYsNkGtKirE+mS2y7WBAdnBbbhCEqrEUKm+lF5cRJxK9ES4GUbNivIPoMQ4PDo4WIrLq6mWZK02fOf/8z8alAaUmemr0pXylUt0dCxVvadd8SB+79dMMG1Xu+DRPWsATmZhUyVHCP7lGsqMPfo9rQ1g/YyTWMhCP/z4Wgj0Bzg7PoA7MVQuiIE/IYRBIkhHj33RrU7pPoaXykM/c3P5plJjy/rwR/UXrGKZbJCrw0WNHknkHtNk4ucxT0uMOMjZ4N49h/BYmApRiXhq+PEm+xlY5iCdWpSosyeI2yajQXsBj5G3RlMfuWMM0TFb8eIpM6BiBYDGC1mWi0mF3Tx4yLXpoeNlkb8wnAZsHhwptpVcQpD1S4cz3b1F9gQWi4Ukr3WsMd/Wzu+hlmKWZPG85qLRZ7nx+RRmbnxSkYnbynPl28z/Y1WU6ve89GjT3PaHa3WvLRNytnRrmMmrSVQLn5Xo7pbem17SWMSkpxy8NEMcncXmYnHYffr/HmAtDO0Y2X9r1Q1lmdeqzpNuJ/hrFaMLQup9pDWXz6NFUYnU5VruyXxC/Lhhx4EbrWhwwJvuUAXuJHDS97+AiAO4aPJUzszmFz7qx6+jNb5ZGVfLWDlTjkur1IPr9bXFjdR9MNEd1gaIuoRavA5gnmZZXxtMWTTukzO+BJBVNQF80AQIzl0ClqHVpr7kDP8l1BdSQ6N+cWmaHpDXMs1wA+x7iGRHxeSRRIIhsQ2ydvyjXkQnivldt2rg7ezj2B/rxPs/2XyZD88mUhJ/z7aWDTYThor+rHsfiMKzMgJdEjYENVDqXRtYaubIzoEjtO5mNbadOSSv2rd4ccZoalgbeVs60hC4K5XocqOrwsThV9A975c7b2c2Rv3sHpCjeiJvineVVzNRYP02bRCpA2Vj5Uuy6ay0yMo0yYN8nOfvd7eNl4o36eA7lyEjdUEH2f/xeI5t+8eWqDM3Dej+F3QGiP4S7twFPcun48sjoj32hrCsq8SP6pm0Fixu3+a50bchP9rcKanokXGgRIeFlpDR6W9kdnPKlhnk0fkplH+m/kWGgAbBZeFxUjIxV8QY3va3zndlW4hufGqPXYhlSgDf6XdH5aa1Tiu8Gng3RlilVq4+jFB2t0hW/SP6sF5BqzpruSocymgm6cxqWwGTyhtuiO2nl3p6bWCZaDn6XXGYmOoZRCjFqi4gxwE69NRgRpui33H5Qg7lfZ1pknz23H80OBlZ8B6m2fdy9+tYHnPOYs/YxMkUCAu1B7Jnh82TWktIS24V7M79q6IwvWQXNJTJOQl99kGd2nMlH7MK+AgYSWWIPrebekKLVEHufXLUtDe17GWC27952Kvp+4J5P0PEPC1rlpM5q6cxwVv2jUEyiLr4J3YKc8WSbskXgNMiz/B151oIm883RA+dZE+9B3ABLgkC1qm9CMYUfjraFGDoLfmm+27XgiXdTsS8msr/fQ+TtO08vT/fmmoxzKT6R6ZOF2+FiAfkVfM17l+EEkt8xsxeP+bacqbGzBAENFc+u0qqz5VgHc/xk4Oz/QLMVFjkobLrdRGLcC4xEipfXZFNfcgeyVEiiwLGESWS/d8hDRfhLCLX1B8vwb3p6hnqObE5ej4LepS7OcapvnzyjmfAkC9/XXSM/ya2O0/HzKqombKIuPoq4/g0Z1ez71gOmVf7DZnz4sEe/7pVEiXF4Ug3cI5dCX4GddSF41xc8jcr8NnJWVOkPllSJyCn533legPpxFIw9DlJr0AbE0i53bLhbfFU6iYHdLgLMCFn9EfHow/UXo+3GZNSXrJJ92G41tC0JxxrGxkKqAgdV4S/kkUmRmAgi2UxichCxwP3JmXWpnZLxC6rDHI7F4+mypTMtH1CKys+UJAZZKPEJc7d96ImPqfMWzYejZiqLoi1NsUVT81blGuJAxXk2K+t7QBv0vweCSftv5YA7L1vZyYoSNt785G9lAVaCIsj10AHwzLQxWZKro2tsaaQOswCnIUX+jutHDGPKW+L3Wg0ufB7p12cyPORSuUHXHcbXNWWLZLXUgBjmSvPHwtnJi0uIobkRZELdangDSxvUBZqc6kdtkGtCpH/bD/9ofSDJgwYZ+LmSF5uM7FfymsV2gx5IVSYSiJKIYR/7wZUNN0BhmmbTVBqmdkKgUpqGrPmlM6wL8Sr51asbdwW7ssutlhVSOGvrm0O+BJZBV2Ewh0sUnY4JbxxM93HJTw/iOxu7mQkh1BalxJr27gqpkLWqMp4rdCEwMSnBPZEESV87P3+1wFpEaQQIscllitDGkVB6kArFnM8sC6pNk3LnJklbbOl8/CsxTFe9i5YyBocl6fbaR+jvabMdrXOsYgAvDfefyNpO+1239DZINqbYlAbIhPoMHfQ2o+oVLx4i+fpbj7jFp97LnQS5Gx0atU66BWbA8jemM0psRzZlQxIxeR+jT8w0uLaQhpaySLXcOucpExZE1QxM3JpzqxCvB6tUbNzESumbtI5jh2KHhae7RVuneYKgqpysTW424mWsyanYX17rCIB7coMnYkad1n1r/DLLzF1k6J/cX4GvSag1uwi1XblITd9+1lpUclfBWjDHfU6BNZJQTvT7kqJRBv4AOTs3LGmVqu2QM23K6e5Vznl/SdifDdx3XBGi6y+C45YVrtwcXqSXLAzj3F6t+3NBEedcCYj2Y/uBpYqcwPSvauZCj4j/zcF4+lftg3DigH/4d0vkMQlrIhzp061+fJ7tKfZmpc0Y5QWc4jESpQyAqp4KoHPijIbn8qsoeLYq2u4QlLCHaEHR8PZJGtBdanAagu+MhY0yv1Oo9dKxArntMQItHHf2HpKXk7MLjCXLIYyY8lbsITc0Ax7K5XoDSqWT3bb4AHPdsHTRE+XmydRhZgrTuU/adOKiYs2ql13o/2qSct2v2LEMfRiCzFt5Z1EWqifTx2IJ5h5/aPYvdQr/+8aDAyE0+q/j7qt2NuZ1NNsWfpAT9876YVbcqOBmTRJU+QLzTAGFikPlwZjevhgQ6AIFVFQUl4Jt4hx4fm4QuKU5+cFYdLHYVATijosLzgr8zJloNQifRA4a7AkP8Yk9DKJcr+H6dJKnUS0JvgaAKs6LULk4aYE/LEkCXiE+KwlN38bR6nl0C7tB5vF/oyU5WgUp/j+dmDNJsDthyoaKUU2aGSY8dJ1YfpvDogYzcsn7786jDiacJcZekWujxJDuQ37CRclcryEB2XwIny3n75i3NrxlYiEjSf87outoXd/oMotR/IZ5/eiKVHsZegRKYHb8Zh7uHYrD45/GKRUIGVnB/V6LxIzJeFQm8v/kgzQtKTv1jijo7egb8PXiyWasHaNsl/jBByxl7NI1Z+NMsJby+gQIoyk4G3P8xfMElG/h/eW5zy0JifO+Z0MyHrfqY9YLQoxpCJcXMBOUmzKwd34PbBsgS+lbzj18Sh/tWuqBm2ttS2T7lmHeNMAFJKyn1kIdNZ/GFDHNxuGa42ZXwmmX/ihsqUsV5eNHi2HlSZAyt5D4TnphcznW568X+BMS+9Iq6Xga5SY/oGuMW6bJfJbEsEaDJiJ0QmilL6u3gqOXO7ZVAmM+QRN5J+h3uwKyf3HXFLN39/sEqM6IVNSu9KkIrLIubDy4QwLt/OQAXvsGjh+nOvX5hcyg370zR76aUlEVUXStMVjZSZ87W9XdmjptBmYzSFMfvoWAjN4PDdkMR99uL++X4tbhP1MuSluexnOwUjNupdl+YXh5WZGVpw5JyVEULEMzeuFyqEt2n+vvH7Sf3ty7F6AeLWHzOtFMyiHpjHO/s9REPExFDinRYN7mzFXBvOxQs7Fvyd1SF37N+ggKDhDitj9QgMw/99ykYHq2x+F/E3954r8T3BQGTVSRggLQv7ab7GFUHackdDvup2J6D/uHZIAI734Qu6cAN39Ofd8Qk1cSEdEN29Xs+dPPuyM+Uk7M3XZKEOvpTu+GPWSWzqUUV92iaFvDqdZqU8RbUJIWOnx6G+vX0+stvTE68nf2R1MLDN1wCki6jH0QYcNbhWdHEUAWaUh1sLza8frkMNd+pBF8mJuRYHSpLUXJ1YA6XB3P/6WMdPB57sF0ELTkeQxtzudXkaextIRaH6OrwbNH8p3/AfvThouHzYwUb5WCsz0IxojHVWMiAa5pw6wA3IhztbtuLjF0B5NqXp6NzXX0KhjrU6DzAkDQHpm+xrx0k9QMtpwZRM4B0M3pk0RgAtXSfVglvsEzX7FfG/p/BV6DRv1yIqzL9cugD131zTw/QQrfYxeaBBbAhoYaYOZZvT9rHFcuswHX2Ty9/HzonorBz12lzbVdgLTb7A4jwG86DrUXUactQNWiyGi7GUFEtxEWfynwDIUJbxV8WfySFhhhrInwvhMQ1yhmAz9/Zd38BEnomoz2vY0CLk5uDYnzF3Innq5g68mpEd7YTFZbxH17U4lAiQjsQ/bENhd2S4eIMjbZIOjalmBW847dkEDDEGqx8IGSLHp050CBCI6Pv8dq94ogOU58lZe2ZL0kCWWlqP11w1GYLx0rKZafMWG3fxHDA8HkR7tvRQRDxqzP6bairXHiuEfqJ0+gwS3bjxX8YFDvNj/XfMRX/hmxjxENfTISPkcfekiTW02RSyMKdFYT4qWFdT9EUZb7YGR5ETpQzJhhu64zOv2J8poa1rglSvSW1bSEoNcOAQHI5eoIm1uEhFY4nvhPx/P0/zueT9tsdrzNYf7aCGa4SOby3jSuaetCF5R117u3kotuBR7y9pQhF2B05Sd6XR+I0Psv/eNREvCL+gDp7sVOFaFioEjHcx1iPZRcR5vL/h7FbgP1gVMvYGjX5vPhe9GIf4VrSelm6v+NPQYroXzMNuRFXn5l81QoGmh4pgLndm2O91rCla0rVp0Gtt+cE8Qmoe50pTjwwbfPhEaZ6uKmZU85/cKFjMBBc8/ljUe9h9TqWYjLlKLRkOErin1Z4kK0IRzLoXfMQ+SRD4kiQuhuX4BqdjUTtmakFV+62P5KqTu3LR1GsMn6Wk1PZak6iInRcGNmxuidm+lbtJTpfJknDVsTw2cSLwn1RJbSW9ThXbrfgTue6pSdGtUgV6oVz2nXLYiIGG5Y25F8UTK7zEsPJxWgGwWNrYNPHWM1cSJUr/Pinhs1Gtx9/iXTS3L1Sg60iWEnLbtHfTtFUSlbfNb4o9p4OZCa5Hv3Pn0Yma6uNeXWd6W4V2zVMZh9LLATR65pv9pAu43iPj3wjgPCR/d+1PitS2LylliuL479KMc5u6qHoYU4MCmmAouPC1tSnqdB0ejOj+2iExfinCLqbIVzcSRVauqC46oP1zjNiuCBud2Ky22i1libRKZ146HIPPOTmXXH5A9iI6Urm+pCpBiDow7l11/ZqjlW6TFbuJbzxCnLKqm9wCU+jRwy9buI52qZGn6GSDbFvR8AiZ8C3VXsDXUR2gh54bNKYQSUfGYO3gpYE9FFA0qX5va6LGPJSUMGISh9VJg41jBg1yjPoUB6tKJoNYYWg3wcqnBXKBLlqWN+/jzLDvzSNJ5D/sAgapAnI9TrY/Mt0hTot0vigQ3pDJiqd3WZaSra4fUk96cYY/6UukP/NqMij+fcdXkI2YA1uKoPrVM08L0xQZ8MffDt+24q+bg+cNmcNzfXez6QT81Dk2iTK4xbCnrdQ/ssTM71y/oMAs+OYWkQCQpA6i+liH92BKYXgAhKmoA+VM6Qqvk3kyrgKM1RdxQGZhZ8CSS8oNQ6pKJnAxtmWJsOf8uPEw9r675t2+OIfJEmMsdld4ocECnO9JCt9r9e2YkIes+q2YaKz7kQyZV/l3X7xnfKMuAZMxVzvCQtOX7psC8opK9eo7T0alPdVgr34W1IGctZaWqQQqJ08WbVSkIxpT/9upA8V/MTiWEO0oNlml6v+PK5uIJuWy+X31cSsCv00mTQbP0WMN/vdzk9t95ea0F6/uTOoYg2sgz0dzJ07bZlTu9JtLlB4osFQ0mOmc2XmXRA2DHUTw5fvD7QFcRW9GqCms0+opVdkT1EV0Sh8/HhsKzeunbX+Nd+uR2RNU4+mGK1ix9xh4GZFN7YGRtP9S/ExZ3AgsM6PsC2Tu43vXm9cQ/+PPPaUH1pzruqhnh5wmf7Hp09ouwECiOX8sxmvBAUWwhUlM8T7l/aJSOUXWC2xG4P8l0GhH78vmphU8+LbgLpcHrET0tX0SNjvYLunRZ2hWM5LOsu/Ke/fhQL6OJ9kaDAwVncunH6TQy25bR0BII9le7+Kf4w3WuCmK913g6xhso6CdCiTT57eE2oC4ic6zMOjf+X20JDYs8BzQGqsf9w75CwUmbMnIH3nSGrohJ1tr84LRn7KHC/Yt+W09bEKRu6DMK0HplsUCU/2zY/uhFLqXTS5RBQxdRZujJ+UGPllcabalP9Fv3YuYzaeuZW6l7B+Yb9Hd4IK+T/hA+ahPd1gkNFmVFLlcFSVlouhJs7SiGo7CxN/bGxGNfHQXRLmjBzxIT4VRO0bLJqy3TZeI2OjQTLXa4VWEt9ZvTbsokl8YDWaSVGOjiPScOEi5p4HC6gD1vLh6Qq3n9gLf18O7+Ke6QJSGF5KPqrkOw0I5GtIv7L4tnR7kTwc5IRPqzRvZnbEbL825Q2Q9EfPW+GxC+PspVAtOyW7MNTngDj6R9sX4Tdqro6TB+c4FVv8OaOTY3uPhhzcuw0UZFJ3h+3VVlu3oP1koL8lnjmyKM/JR1QX/CWBuMNZ5FP7ZymYE1P7wFjvK9xHrJR/6JgQHrjN5U+7IV90eeiQjc7P/GCHleOAI/M1PiVmESfYddRT+MmRsTI/OOkcIN0xWcUV0eLwucmRrOUkkUagP4Nx3vg6kBSLOTFHJBFqlbtZHVq4BP0evuSKttZoUyOcvgO/9t1CTky+MeEx+lfnmRcko0WJRpaV4BtQ/WK6Ibl1T+aXWNJV5fdhNtaDoH69r69RdzbHKC1LCEuGxgjjYFAJpKona24KxgzSdx3aqKT/fbjKdtdTcPuXRGf5008ogjwx9jMJsHOL/7Ok6zB89f9wcyXsfiDPLog7/6yxtBGoWOhrI7wO3ebvPcHTnxce+BbpFTVemKtK9Rs3eFtr0Kpp+3pAZBYC2c4WS67yfMqVy6S1BY1jl6Z0ZKAwb4tvF+Y258q95CN+OQ9Cc1hIu6Q1nBJGr4ITJAPom/efpVjW+K0c7BadMz7KPcMzTZ16ET+RVHwcNiZYNpLuwCsRnCxVBlRgj6eeZS5LqPkn5xFuZ6oJRsCi/Gsj0ypb7ypYniiXVIYOXJAHBr8HLk/vTmyl1Yx7lwlxcbGx39G0LFloOZS1I2GCSjYI1LXrzV1ntBcJjIWC2u7mJF6E1brPl+RqyNIVKezQK5KRQykZT0nA5DMqP8lq5l/HKUwtV7jZatrYfaueSiRtg/7h/tVFVjq6vFpexANQcOm8yGqSQD5GqCDlYkuOu3FUHCETuLFIKCA01hVghbjvFIdg2ZyeZIYqgMShFwmhYZgpjsaRmkuJvmWkSo4GE+NvWY6bJJqCZKBu9/yjAl6dm8p90nkVOJxedkMfoFG15ILHuFTbI4Hho48NqMG2U3g/Kr6xv1c4CehYi/7GLf5dJL2ZHFky/dty4ye51TEglIRQI5/ZuVOHl1QVGfJFNRBFJac4uOVZNirdQapONR2HL3w67a1wXq3MZKXDgrqBk0xNwLMK9Yt2wCJMVUQHVSY50PpR2RnByq5udTCN1uQxA/dU53zB2pUSA6dS2rqNuAIo+W/FtiXd6KYZEo4G+WgdyFmZ/PfXH4KFAHBdV1p1KC8+26TRiGyC6tuaEUwGSQRWUPGWiTTov1i0fGmU9nFTn7osuIsVrzhyE5uhuBZ8/CAteOBbD490qO6QyT5gBU3J09KigSsQ7G3MJug3nv7ZfnQUvsybnEhXz8DkVXCB8HSHgCez8TIXv6Vs7WMC3cK4OUahWRXaWUDSW4DgG2mLY1LzATLeIPBPhuCnAqWBJr/pWXqsvgJkZTtgA5FioXcL/RFwYg7qkhKkQRNgjr4kMFgLwKz7urIIGY/6YVCSOy1aLusdNe1rBeKq4dvPJh1RAT+Ik486JHdEAqfoD/h1vce89FrvvGa3BUxOyYeVkCnSS3sqHqNwYIki/jfSph4vgtrRe6Ezi0HkzXqz46m8w3+lysDdAq0wF3hZK+tuEcnumyhndOhfWtylq84ZpIRCXsUQSj0sUapPMdy1en/CA9/jbTblTAwZBDKHIWWYMMKxvdNZz06gLlUajFliuWkoF27PVa4ZGHOmbj2/4neWDx8wwvSlrfXH5vc/QyXh1sHHpRR4fusmXgvzFsA7oKnLqrrBNQGTak9ejmNvAnWMZxbyB4a3CRt+FFMI0ErjZx084qGEF/MmHpwvOLnsTn9NcL4VLI19yXOs0Z/sbZZzhmuatEIqHcqvtDQlzSRSbep6rgAp8KOKE6wGK6LtYddkOwGHWFxFhRfMpEgm+3RwX1xZUVHocnjl0gB/Sn897umpwGFqoqJ8fC0qaMN9eLWVr30XA+YQhr7faEkzLyVxMyVDSbAaaPUiFyjGhd/+b5KAsES9QbhZFracjDxxWWFP5YT4o+RQ4TAEKKmpxKz7GwCYHjdqfUudv3StHkdfZJ8TL3cib9L3ZG+/pWfP3UIh0u531harLAf+dKpw51cIrEbOqEUgHWLnRj7TnPPSkZEEuhaAWjpxWwFwXDZjtwRDBdh9nCcMarhcdm/4g3OzkxExrFkYSf5vJevRNKg02toNkpvlvMcH9zohyNOr6qXfHw+NFyCR1+cSzTA0dpQCaPqcgpcNkCsu5rnYlKPLPI9MvEwAq7RMXGmEKWuBaOrTAwMP2Y50iLFc+F//Sv+w9sgQp2m+95aGkc2A5YW+7FPS3C4cPrbuJyaoLz7JCB/LdAWxo8Vn4UT+dbrd+7hubcFxEC++xMZXbBXsPXzH2j6AIvZJbjiD/bVANbeM28Us+QXiSYfL9SvWXtyTRcsxcMqY6MfLUxhC970bQG6rEJZyFcQWMSVOJLR1l241Bv1zFw3s3W9OE3vty39ibJ3OxxiuzB8+S71Wl02gmy/h8/8ZAGUuwqN7GTwaFMAtceUU9z8EiUcYboH7MXsgk1ieA584zj1jNej2fLUirt6HUU13deXUGccXS8PBzsCgHx+KnomBO4uBfxEP48S5klYInpli533JhPEuK+F0uh2lVYhyDzYCOIJ/jFmZqrENGefsXx2p0aFhoNwg63Zgx+qLqyg1R2OwwhzZvDpatqgZIQYaApNj/KEmC/vth7bfx7o46FgGmruh9GOwDW+76NMuxVBV3DHSCTlCracTdiyqQiXS9Uguv1W3LhV0dgm8BkY3f1SIl+rd+Ym74pSCPwbjPA/rniianKIg5zTjkO1BnywRAiJeTgjBUZMU13Q/SpRIRomriBCYbvmsN6JV5xAu1nXEvtjUYhUtNHrXGCprGG2+RXFwkRHmu1nA8+t/y1+0xJf3v0tPw9PFtLHzGQSHkoG7B4+XcAWEKejm4N1OPMDMuFeTymyEVhf0lZet7rh+Engu71O9JrIg5E9ZQdW5BnFcrDY7Vxgfqqus5j0qdssaF+qQMUWpI22ORNtkY9OOPtbbADZUKcSoMj5m+JE4i1c5zKh/nk4yv7mXkPnlx2YtzHRUOxA4KwQdEgeuzIdKlWOZ0W2o1UcfplzVBv+DHx5ojPPbOLyvYFc8xTxpIUXzUlMlCD9kFLggGopW3W7/hlDqv183xmVo0Vv+2UtYjzVtdiwIwQTeqRAvk9rawahCqq++FjGadUZUQKwocTBbLLF7hYZjxOwc3y11qMCmDOoBIB7uMqjQK4dgFeLq3Op7rhx5SnX8O+pd1ByZevUM2N/A2eYMi50Hu4QcPvgVX7v0fKy5t2sQvsdAynn59Ddje+1a4SgwAM81w5cn+rH0jWRKSvdUdbhYgyaKuZ4wh60Y+WHm5zv3I9Rri/gapgz1rRmAxhmtUjwHQwu4K9uIxIixl7dL1TgInyWqdFQfqLNoFmvNVn33vGJGVveTl4l/BDwIF+6fbn4xB2Hj36smwfyFa7hqg1PPm00Z7c0BE3ymj1bH/CJzSk2pFNdIVZ7eAdOmUKLRPI3wIgObTbvxwADaswz9Qnar7Q6586zj58tZcLp+A94XLIUS3hEek9x5AHhucmNUQT1oRbLc4dEst4chBDTkNzNld40ozp6kpK6luo3AgANkJv7cPvahh3VJT0cDqt/n0JQ9gB4TEe+Il7BMLHQmcXL59AZ2Q/2HIUIWeIuw2H7RkQP8fVbjpUo/t5gkpwEjd1LCxCaTc3dux0Wqw2P9KC/brkWp2Mr9Cs9wLf/LJMR9Pg5PpT8I1FGXL7+XMl53a069HDhG4Mginm6l5MZjmhYLlMGgudS13zgHH12KxQKrjUUk5SLhidaNH8o+x3s8229e4lV9KzBNnfkWIN2W45skKFUvyk3SKImA2EizQJA5ZcDc77cfFRUCUU7N/jVMaaYoPwGSeHi6SWBL6rOLzi8BZnZOeUhV5R5VUBVXK7RDedRrh7DG0YaEcP/nuq80ApaF4H+QJePcZkA80COilTZKtXgt1zHX54KJQT6CYlaMPrG3s+ZaHWEwspofch+9/7FksblnJJ1611lSskAY/iFZFPw0/sn5UahiFI+iKHRVVCgMlI6hMsQCo4a0cmU/3oXNMt7D49HSLX69WLCm7Nhvkk2gw339D2FhziWMhw66jEjl3PmRKkejB81id0zBfjkPEoQQhKemRA73jE1IRuyRd9kan2C8BaWrJAAznsgi+WSreK+/nvOGeBZPBv/BmHCmerSJZs+GtC2To5Kr3y180SOnWJ8dCnTRwZ83cwL7LWREh4YKcRkHc6y+EYtRPcJ3HrVF2XLJfuTtn4QkxtMQLQ+jKdSVImVabFmjY4/ob4mcJcdQhQlnlV0eYmvaJZnPcH/yK6nfSqA5g+IP/n+VWirWm8SCg1G6Qk+V+NSOdlD9s1zRiAHpcW4J26JOyVs0kwKExIMEFOabYfIILJYt+yo7xxFiOwlfe0hEGWbTBCzWQ+Tl3z4lLJjVA+a3hminokH40p8DHlD8h9FivW+emJPRVZSlSloeyFoE906cEDuBEETj21BvKoxGXi5YWWeSSb4Eff40R2jrvJ58LJ0TWGbCIx9Qzhcq7jqSYTnGpOJEeWjcsPGhaQxWuzFKKdm0LRb+ZKon0Y+DeVuVJjo1QGoLa9Dr1VzyJNP/HUUNfqDUa5teNCfeiEALT6FOmFt/G8YW0LRp5G8hob7d1MNFewd4WmpVPW1V/vlsJtg7m2wKQAEqm5ZciK2OiZAiZ/URTi35canJm74DATnkV+jBQ3ogM4V22fD0BNsL0ALhhKNO4naH8aUigE6NgWAmgnRa75BHkWigzPF2dTGUiLNmP+tis9hhqUDDX42iTKyvk9iM7xouoCGh31Srnia5dGxFjBRj5kYcFjnnHvnlalw9VMBv057p7Bi5TpdBhKO7asZI1oQC/CX5eIHbQGSPqa8KUIfqS+kD1jnVbw+f3544pj1PrzCXx6Hf0WGGN4IEUDe8HpAnTy4Ngl109/WdUvnFXBiXMR+Ba7zqXSpWSVvzHCl85h47vYqYZMOr6tsqzZKCn2xTrvM9/r3fGeB23GTP1hVnZZSiSQOPe09cAIBD2NH/vFffQw1CcN5y8Oy7qChl5cO1MdhnNi/AzxmV6XZML2zYSoB5gs04/SVvthOtXqI524rX/JEV4kvGln9tW699kYrKIUSKlKKPCecyhR+VxQ9gdECan0Y2IsFbPCS0LNvrdzYaQdsRKwMvcQ3vakIcgeSn0A1lDYvFZYp3uTBdOl5OAZYEm8AWy7cnCwy87Q5rsWt6FubLradTvoMSsm3HG43kCQjALLRUyFtQ6BVAJZ8p8aQ200GzQ5ivD3nHPW0GCH1hyh4MGFZoM5w91TjDbr+k/28lsVjNbNRemRAYm3cyeMoJLiGWKoYyGa6+0YVXCir1ijWgcfT7dsaV2D2ycbePAuYwKzkjrMUtnXkZ7fGztAEHyw2FMN6FURkrmovbmu635YLXCL1NcOh3V42A73evYDx1bwlPLoYItRP6IaUFuwuddbjvKWWx+KczTZIyTZOg1qHe9mk4Ldq3bDz+5+JHhxufNuQxOE24VyPdt2/5afjVqj/DiA4Tr3C1MJ/h9YHBZ+iiXpf+D8cxiygii63ZvAmF7ycuObZqAvghhHnkmD9mVyrpA9ECxIjllCrPW7ldzuHsfdPYoaAszPbe6swZF53sEG3CmCy0dpeR0a4x2nmswep2RwIQ5yQbND4xRfk/8Ry7xzQTs++fHC+MgaMi1d7sDlrDJZZhjh1c8FLssA4vK72IgLJum5LMd2T+SUuiiVzk50e8Lf7uYqpPw752m0Z/o0ZemDYE0GzanjJ8EYq3tUIwO/TWbVZQvpNtOfoozGG6ml6rm/paxLeMARSydl+O5J4n47N4NZzKcpA8cwj0nQxLc1f3CUM8XJJIbYsXvrPhqpz5Rl3LiRWsb2FSN2JO79rXHEsfqApYQdG0Bdkm2kC3F8ItLDK82lRKGd4+e+kIvBoCivkoOcdk7Z0cH6kH5gQoSff5dtrXAmaFEqgsB/7FsA9vxgXFrZ7OmCMXobazIGkJLKfzV4lBMTYMki6d2qcgLjQru7MuqpQWn544I9kvH/LbT7+3VP768VVYVEzhye5nTMAHCQjbqI/3JtoJtv2bcWz6G/de0X2YNLasNLiYVOKPmfB5Xk+/9HolxLp+56FjLwk41+bf+QeRLareLFMibYz6vJ3LytU/bi28MI+QGMXvSpLmhIcQUqZyBoBPK9umT0wZQZAe7osKL60YeFxxYzg5E2WyyuhrGa+526PoLYzDx8Jfi3vBY4o8YAUMPI8BLCdsOyKWCHljs1dpyfyJRQhbnRhhLvkTeQpH8ic7yNH2rWwFMVJjuxDYn/wzY74kiMfXmZaqwIdL642+y7mYyFj2GiV+wSWr/7eVAc2AGkcUBqunGt0gtiJJkCTTIx0GugUY7wD72jUvM1I32fUjzlv1vpR2CawZK/+L/homqQCxZq0Dx2l+s8XRAOtWF0F8HO4oB1OkoRjMkvI1TtHf+SNjusVyZxLX3ElO1uZaKD5bXNoRDtuQjf1lBz/wK0YUUGO2A10367YETFvfzr25Jpv80/uTrarF3IG7gGlZJmIAy9MtXGnP7LF7J0U+CGBlwc8bJVWJzovJO3mhqwAcb0MVYXYclpfCqUB58YzF6wIOByQDsb1yIi2R/skpxjDgoTDd1+6kvelAbSU8Eyz52jwrDHrNeafBx5+0IR5DhJ+URkKf19qzDWpUbcjzopo0beCI3DcezOvKnr5pcRZf5bqqbkNj2wxAddSblJUNVtYD1VT7LaTgtgLAk+CX+zdrNvhN1CAnMPwtF18KtR+gBMOPEZSk5eWygeBpvemgEVRUE2BD6pBStUdU95gtha6RbFGlQ9Okz5v7b9Ske4qLZKhegR6L98uMVmDYKU1+ka71U/cwl/41twdq+i3luLo2TZTMcGVACfmGc16JvCdi5WFjSN7p6IQhDDUVBk4bOr9Bv+JB0l4+UDQXSCbbfuo6xxSz0xaTPmoX4CsBzBVkDYn88oesyE2fHReAWeoVuySpDV7vJDnrvgSTvR7+wOq1yv+avt4wFldgPUDFHsvUbKNthRHom1a3cM3XQ7EHNS70qObF7coVzDgToAeBsx670a0fN//zCZLvFhJ0PTdcKsVZ/nW+Q8int/z0w9gbLo3aq7a3GLXe1IEqBTPUUo14ssAcNMpYLH4Ike0kBZNe3fPyISzPqDutf75xV42uYOwtirT3oi1Z/YPd3a/RE5mkDPLH3leWGkIasIGfbPELSBi4nELgoot0kgnsi82yUc182qA+T8Pb2OxRkhhQjAlhaVWIlJjS5noeKqzHjU7gEa41Jke05LyqNAWmGywBX7NytOGlql2Nt1PaYhOJ4L50oht5GsSSge4rPhteZy4ojSmTB+s2lUH7+WJEb4ujvWWSopHNi7nb2sJ7MHO0hOXK+wRQpA0E0pmY82HmLDhG6RWIV9932g4FTgO0qVcg2sEQLHCvVl83dbi9YiEVunwKl2pcTyg/uRw3sDk1tHXjAP1AQfjMRuzklJPvZ7Qn6l5f6De2HRpv1t7G4uIfQ9NFiHpgTgJ8IDHIiKmgKJCcx2N0asMpSfr78B++9kxdlUlfPJRAIc6WFwRswqu/RQZw4+AcYgq+//XUoW9HHrrnhcl5HY+KJrpfNNA6737nh50VpIK6DgQJlDP1/AxU2bKQ5bOKW/1Xu08e72I5o8b3JU8CGvNHr11PsW2/eERs02bnpURR8GbRAGTABDRNvoJJ0ExYs8x8iMw/o2MENqxyc+Jv+VT6+7A0VDKsGSGhbGd/LPa7UrvIKLlK8ndhbXTiLkPuQBWkYhOwe0LDEy0S5QvJW4ykvaX579mY054HWBls7IN4eIOfI4R6zHgmDT71miao90MG48SkSLZ/7vDsiBaANxvFNoqGO4s/THQlZkPsBEtajz+NEyKV7NYwGn8um141eVYlu552fJxUhvhge+XIjW7s0YqlmBWnn6pBshTBSFKH5HBy1aTNwWNx1cx5GhKvKZQxyyi8o0Edf66/lxkFWbBO5dEBJa7j7dYyzQhDIsikj8AmpFj5ksEOQtBqyJr93KZDJ8A7tLOFG3Z2zBpVp5gbImGuq2y71muIKarYuoOr4ucWFzOmyL81uhxCoIw24Kc56j189zgqWSSKZn8w39wImG4Xiah7E+RDNzVW5MsiCCZ4iR2dSpZyRDbQfwA5DV9mOPjUo8gVkgBt5BMk8z2yk1pGxAtmfRufqNiNzSEjho/xgWZinLN7CE3v84v8DjHa/9Jr8gicEDzPgbVsRzcPDQghrtvnTB7u+x1II1SfkPcjOfwKaFtewEBxOLdfvg1zwMM9neRFldQ8ysPi9ZtyZZj+iGBV5FSltDBvEwixxPMNOhTih8wnaP7vr8NQcAbBqx1n2oS4GWDUWrVbwLQAemZumVV90Ki4nGE18m4VwOytwdlf4sk0TfVO1YjnOabGqe6QNu3/yJ09Rrc9BV5XAUu27+Cc7Z9awu+EZCgBd1i18svvMRlmLwRQpUnTg7D8zRGZIFkZK/gNJA7mUUa8y/eLu+O8ebqOmK/TgaUkMWVCcVLWHPcU7IRz/R3IOWiHND5P37dztjCVd5Av8VY88ADSbZJY4IuIuED4z/Zh8kW7cq9NwwyHoQuT52SvWz78n1D08ONQjdx8KyBLPRIhthTGhcKxEApfhFGxuzt/3gXlh6B1TO7pr+LjTV9P1YmP89QlgQB1WsoI2JCV59WSWejZLzRfsGJe6P9F8rI0mYB0osbHPyYI+9l9HdW+nMO6G3CYQsUNNZBYy5uiwt4Jse4ltvk1Ah+RidVX2lnn2ShJsQ0uZC0V4v6u5Rs0qAfCnWVrZa0rLbsfWkj/X/g/NjrFTuC8MenIh01GPSZHB7AqhGERAuMo7ngnyM8qxEAFdLJ6xJHQ1YMbVfF/al0L5o278FWMWyFbJT9sYT7sZu0bOyUDH9/bPOuOWYp63VLdx/C5UxHbfyl4zribKekzON8QHX9P+m53Drk1GY3VxmLW/Y0bAFjvxqNt/pnizH0ahukpms1hFJDzkCpBvJ0Wd0FJxiCVhBS/s76YCGnQnGSx27Jmz+TEKULzquqhRTEN0AoAtQb7kFbFNi+f9gjmrWSVQ9kksvh+ZvaQwEY+hZ4cm4zuFwfchV9FMNGnGRNCh+c2BhVbCkJJvVLI/jv+6DIgZo8Zj91PGvl4s2gU5QuTjJeQlC5bfxn1At9BJR5zphDzbJbiibezv3QQhTQfgngofK9sEEj/8eo99MQx+E/EM7H9hIISywJhB5bpQ2Yr9hr1RXaBiudN8TClc1Hsx60H8PNa8nbuQujfv57oSM4ONy6zW4P4dJGMrCV+WGWXjr0ccmNCLQ6KB5cu8g/i7+X0EtQlonBuv1tEveo6q0gQkjOQBwfufNRdMjRU8/sIS/WYvxnSLJ/m5GzFiwdqV55fq2wg8fV5PjERfC+1W71sS42UGW3uW39haOA6w5cN9W1N6eBnQkJeVFBeI7F/dzZcbUHswlLoVQbWynunh8IZCpNcchTsuxstT5EWzz7q+se2YgNyKAvbrasve3LAsntFCCWZQL2POm2xiHPDijewWbHKh4NnusuTD3L4rW4V62Tr46EBuYIRmdLYzqfUR3HKLYkBnthvKtCNJacM81pv4m/wOCt1Qtf3EHlLZ8Z5LSEzTgWHQOc8LWu2RZGTi7wJvSxVTFXtRJj5SCX8CeTAU7AvUp5eBmRnanWffBZHIb2+efygofQNulBpw6Dt7/9W7OKBnCe0bZHZqZ6kftZrxmoQgMSFWWb2nYaNCVSN/RGnxiBwunq/L4hlhpf65vp1z30kDFHkn9QpfJO+vLRvLaEdsrEojgVEWfUtV/vpjPiRhUVnr22wjV+VOJhv4XaOmjyIJC+n8lXTsoieN4+N7LTwucxREF8KEEpNYYfJdbdUVpxXYBmOo1q4qrh/XdA/STywdrYCBgQqwsSYOOc2N0haSuaB6K69fQzeEiw68EaJGIpX2B3hpS/EVQ2y7omc3YvYXRfcwR6AuyRvembJYybMbQ7M9i16CcgtwZpAt1ZZrbgHAsQLzf4dMeUV1y/72MhlcF5M4e/4U5AV6/z92x+GEMYbdCnS1+jdtfNCggd8sYYy5cEPad7tTTtrLNzT8McF7Cnbh32LV6mWSL/tpGy4weFWaaWcseN/S95v5g0GHbgv3HDAUIA0vgr2M8yfyKnZWyMk6KRRM7ki2Np7FpXzcc6RHPCRUVEML3MnTirE/g8HvpYwknfbKpptrEl70cK9tSuKMHpULKzgkgzMY29w/S6JFknDGOu6yssxe1G5BVvVmKYStz5FRYVyWn4ui0mX5v+1/k7sgq8RwlNhhinpQlJqk4yva3tkw3L1C9HpfXq+A/wEB3welqB4gNR5RhcKkGcO0TVgP9s22zrXV2CyPR2eCcEAbezfSFK9fqURMd7l7FnhW/njt2i5SeR7EtibY8fGGrfsQ6/boW9uFxw6UlXWWXJV9Ei73p/Q2xYTJFrb7O/lGg1p2haXsC0mVIcplA2BschKlDqPa6AjkgPeQF0vN9fOJy0Vmoegz0MGf0z7h9zGz+zfjf714/4kEB1OeWky2v3Bg6eGJHZksPOU95LvrpovKrJkcH4jIcfZ5ZG2LYymRD2wd7g8DpSTBgAeIACs0QOwE/GAysfFJdnbP6UD5PtQY3Ox5Zd95jpJcoud1aPd0gBY7K0hOBs162cOTp3OGavqQ7OGFhDHAD4BaZlTIVvvHfnvzmnZ6jXImuirUcRtSmgU6p8xYfeMizlOmOTGf4rUrlv+DFg5HHISxcBwKpD12jrk9PFs9GjQoiY3dqNJPiwujOLCFKanq29z0OC48cFPIIzVWgODg/HRBDu8uvsPvttg/VtHCAijdP3zuuRDv4PZBE3DzWQFNEMuIJDQYLDQZ4UVg/AH5izuOPlwrmcs3mtM8OYzBgY+j7Yw4z2kFZn2ODJmqsM9DGNiATdbKKtiK0prQtmnPgQSG9qdqfNegkMvgyj8tJObguX9DTBmJM1Dzmzlm7N1W8/+tc4DrJPhx+Em9gVCqn8+hrd4UMLdX6/rbLcPGBKmnfiaQ7iiWRVtqTgiu3RQvS48Q82H9uizuzWcCyaDTABrZ9IMd5f9ro0P1KLGDr2lUin5s1oFVO3QcVDPpJqGTbrp2NXUMOxXo792FVYQW1bXElUuvpjZ3cKVosepGScBqG2vVtI+XTWi925TBvHCNJy1y4r0fwZenNbkwsAT/KG5Zb1H3cTqSK1vYEdxubCxtOgk4Vqc1dFi0mLZ8oN6URDeDKaCW0Mfc2U6dzN39HzWzs8fuGIt0Kamm09Ht9UT2bO9AjM02IfOVNjL1AC+/vt0aDaU6XMAImagpJIJwB80h3RMUYqpQk6hP266DI1f31Pog7jjXfRyd6T/dzvYrrTrRlfRuF6njUB3MQ3NvuEjzqr/HE0you3PGfSqvCEovryk/1s9wJQZL8cJLXKgIAAQKLkYBDu+d11+p950TZe3deIc5sa+9UpD5mdqvVR0vgnhV+nY43UUBYKhlBnit+imy+2lnpftRH0c1jkeLUijRHfGwp5LWYrx6xIQhYKB40NpiusZVQbfqDDwtr2jFdI50L7T0ZX1BbfE0kjsQUFzc9crXs3oN2gSDD3nDBT9eOwwNY15lSrSSdZ8QR8/ZKTgEYAQNXtoYrG9nt0yD/S4cqaO+UVHQXz5VeYEYyAl2nLA2I9XeXfcEFNQOIby+huJ7tC64xOAnTBH7zfgnPPZn1cdYfolvi1nQ0SKiZpD/YWphzAEFVRxGhg5lWTpKKk+2gD4J5vAVyGyUBlFcZOUcB9OLU4k/FOvBn3i6FgNIHSKd++Dm2tWcnoruZYsCiYxpncqYbxovUbcx2KE4uUIUAkzbFIVZiqfFmIwXGnfuQWb4SgPZvSeOJNUaMp12n7FdCxfU/Cuy7B+nVFSMSp6+ZVgqXzlsm/VVr83/l6zCFcg211tdEqouBoiuimqPqYC63xiScZxXaE/EXrnFSXbEJYlyKHM30wH/S+fdCqyRWaAAArU+fnMLxppx9I/Egg1C/sE9Ogn9UVFBMP57CYxpoXL4GfGDsAD942n+eBQztR+j6Tp2zT68/pAMkLXt+PCdXaW0it/DSC1JvOBwbYU/cgVaM35vjOLoo8oT/aUxNT5YaMMcQV4iABByc1MYAX0TiwAnMbttfVh8aclnATXyhvpMzkY1/ODhpFDv1OyIIbRqA+Nt4QPu30EGogv00OqIbOPorUzyKP42DBfZWy6K5KfxT7SKHmAb5e+AAVVPqc64YM0/tJjrPotPd+bbkol30ckf9jAyvBixf0Tjy2XRb3YGeaP/1cy+X9oiSilRJiH90jCgTZNNT4p387CDZ/fIs6EHbbPIskeBpR4qOMWJVsC/HvX7tKoVlgrUkOKHLMPKTI7LAZtvHQzuDul9vanUToZWoQAh28Te4u+dRZDe4XFDVJ+EnUnvsbLtcBJ7aab9f88GYrETzswIMtF+wERDSYIHk/rDa68TLxYSSkDaDiOOyCUJgtGA3YpKpxW0t3bCOiJhbBDxqMDr4RJ3c5RWaL3lCfSHeGo4xIbEjah9eZsk/06OQWNdgd67tuMJMJEJpT9SeucNm/bLGbHGKMIoOt7UpiBVKNHGfrI5sxfvmuCyxytOc0A0PHJK2cYATqsxBZH8Mp8ZmQtElyV9Vplk8+TBx7MNUu10mTaQ5RxdFzPBV+3mejz9FqonS73eTt0Z36yWZ0hjChVCJvoYe+b/+c8cfsgVdI+CSsGB4AzSZ09quD/PnIG1XUN6DjvV8zPtAFeN4KJ+x9yP9R2dOYsWxpiV9my7UCkxLwRD3pSJ77rUTNN7bfjI4GaxlX5KYoTZvW9qQoHf/NZPq7iLjFu9IzJZadGG11cw6CnnhConwauz/+MuJ2vVq4iuGXl1932T709uu+jidVqnxG4Bk29OHT1pAlBEk1EvScAjXmp476QFg9SWFFcZ55vWVkI7743orwmnMG5w7dn4bmaJh+5c1m3gmfURua0aRUUU9/7UkUFWHMFZH/MTFP8NxnXYd7xdICz64ghLgIUJyxMbDumFZck+ENGAsOaOtra+UQZmaKMU8EJn53PkiW6rqqhH/YoqOmPdanqQ5/pdzmFtdfHgNFgziD1UK48STprZaRI3aRl3pFlUrUXvFAnXY5S3C51ptCSj7UBogyS2IauxBWHzlBHcTPrSM1o+zc3IYyB6ceP1clSB4diQvrJjkyC2NBbULQT6F0bckgEG6YvBobuh9yHvP52qiLQGeoCZ9oFhrIIGRyCMsjRCdbnIkzenFnn1knXWG0ACaNpwK5piJY6S1xiZbDRkciFow5Vuq1fjAHoTI8uyhUDw/pz3qquXe4JxGEmcTjMYyhFcpsL3l+sz8//D0vFkTvrcuk1CcIO+zTGC95KxuCFw79InUtbHyUYUgefydK8swDzsK7NsDoY8tf63SFq4WC5+vUDGYv7JzzpwjjAMzHHH4kbx/82WK2h6zCdYiy1Q8EPMwyi4TSIv4u1L0nIDlc/BQ9fBKs9vIWUTG9uhmgGMsjLtFEbWvhhvZyqfzswx5y3YXnopw74UMzw2B3nYU6ckUsCaKXN1+NXi16zGAvZynA1AdTRL5n574LQUUpgvfBRK+oLY+uO37cianotlpLdP+VCdRNLNbgt+I9H9C9b21xR7Rx+w769BiuPg+aFIXj+OZHzLyRmQXw0edD1xV8Y97eDFczuMh28QSp/xtPGvozfy/ArTGnNfnCNDpS5FEGSYKjmCaeWgfXr+rct8/OlORvuVwv9He9UtpcqNbYSDpAzj5i9YNTcM40UjaNfXgs45JLtPJq3R4/yhlbxSzBSUaYYdxTkmQC/k4dXsuS0xLHhrEXSHU8B+FxSpR38FdOnrK8VpVIZs97DQj1DvtcZyguaGHnkUnY4SHCSt0U5hrMOomak/EZdNxyuT037XY7DbElrOluzHbv3x3cxru3I/MbOvAjhh3w8e9JhPtiI6KcJrdXCtyTllnxAAT1INQMRKcDgKeaXfyprFhq868zcXpsJ7lqOai7j2VAv48HaNl7nvlMRERWNmrdJAMJ1JJHkhqcaT2v0DREWGfATH6Ayj7tauwvx089ZRVr5XI0PdnQx78A3JdJV5lrdJ0the+JxMADST80x4yPZ5Gyzpw7qsh8Q6C/wVywKBCLqtPuMXwtQAZEx7X3kU/TZqRUjLaCT/nC5ggyg8M86NrM7sbWHhgxcBrBaItZUOtFu1mc3i/YwtHqQGnktthSvTHpvljce+ZvPnfb/Spn8V96BXwFotS8udSA7BZQR/3w781WZGZVQtZH/9wUlV0uy0NdxuxkxitS7HsYYv80dSFINht90HSG4JC87lD9lrbNPxCsVwgZY9zl+qW69HMSZPWBd8WcQ1nTlFPhbXMxWJ1Lkw3PHaVDPKlzQVUKkz4yZg56/9Pwca9gmo9Y6SpZ3M2K8hl6MA/KyZfBvYZFmwfU+SU56U3kJsxVaDVP1nK/uDpyE1HCQlAMh0de2n2d0+Ki7ZGhv7rhc75WRHVhRCcHDToHoVPmJSnWyp25sUoewUulMafjG8TpupW10U5gul+igS03eF/Cu44Yrce+zRFRHJs01dBCC7ZyqsbGz7hernn89kS9L/ijDjkGZTDGvblTKI7tR3l7gcTomdd3/dC6t5rFyKLXOGSGUVdJ0g0tPaJa3GaLfvPUYMo/9sXvOtZ+jDwqKBihJQJZA1JHscYjNVNPapdPLG6/xK4HeOSGWh60CafkwwAAH9uJPoStaghUwXrZXeTbjggHucQ1xRXoh6ygnPSpTuodcvCfvbwgpIcaUQVFv+OiLz0FpF0gN3RTtfmNWV2AP+x2ks4zT0iFb8VJT4utHsg3Q9M+/wfWXBsBFehYDGCwsCJC9UWn7S6zON21bXpsKDOKV0gF2stOZ+TwgOIV8QFf5pj4Bp6RLAY5UyIXJplj2szHQePXlNbf81YtOyVtHHPCXvW2VfHzqYz3sRDkQNLuFeBrFNTDlZSd+LTSrBNAKnzgfnxlJEO1dNgj6QpTr+FeylPSBWopkkcteZYa96SHZ6ZsV101TBSm4qn88Jqj6hyBKTxfVxuhpSguzq9wTMpuRuhm3n9k1bkSm+vnwfBPU843ua8kxJU7khZvfplqhwjEegDIildgvXopHanE/RmvwM55cD/73oe482GE9vTZpmHVdXSWEs2886S/bxlI1XZMTGttp1EyCBBx4w/W/bOTUTPzI/xiuCrJxhdvh2BHnGYkjhwFVlvyczMzayGF/9gbrKbidR8rxhLx+7GGS2/s24F1P/JvMD9Pokp58d6rby+zJYSoIwjhCpEo38ID3L2BI+653djVIX37h0nripBc5+k1bMwIp9s7NJbxNISNW7ZqMzccXviKfwiBnPORs9g8q9Q+bPvqKZ7fyojb1Isl+RH4xwHFi3d4AmbNkfkNMNjnOIJ5XmCix4X9/9lJUXVzip97tbeMt/8ov5KekUphkNeTG5vqiNH67DZNAZWc7Q7xxdxsuS3ocg/wQ/XwKlbnpdcFbKhdjHp0Q4J9Otw1McTGOb2gBMhsBbndEU/RqVVapdi+wyO8gwB+Hqs1rqy+GKVOXGgsrPI4ZChDnQ9avfunq9pERFNM02rw2fs6q/9CSpXHcVyYgRM6XDLbAPOfSt5nBDaC16v0BlaDgMeQuWZD+M39db8UaMt/FUzHrUfRvuufRrbl4L+MNSNUbWUkiHMvVelOTaRSZvN17VsM7kVeqmAjkcEQmaRaw51hXaNc5OZLrbQS7kr8ORDrRg3WQRIPWsE7p+O1WbM6SuXR8BUoneOPd/pVjjHiHYPh0451PbK+Kfg9HHou+lYk8gc5BMtfgtrE3Oc8IWxhx5wI8hMK8kDKN1Xk3+fPwA0rC3kKTsgpx7fDdkWZbeZ+kyyO3fzeQDNg25r2DSRA1/17rWX40h07dmLShnDb69FSfLNPwrtY4CigMrtyUJgdX4btyl0l3O+PlM/ZP48DMWpdkC51U/X/24dsFnk3DAeENKGR91y2PBwbQJS9VT5pK11OOqfKaWKL+TpIKU4ZNkgXhbtv898Maj6Jq/zetCxq7b+hSzaYhH1DonIzUiRMuowZOINFba+noFMqRxMA5s6wkLhV4J+6OAG0IDsHJVWjG7QXt0xjfKPU8E5qb9tpQdenN5ngzVvg3uZPuJIgq8T8OOO1/vEM4Xfw7jZPptCAHei7Kaz1I/6dig7vM3IqMuh0Mxtn2l14FucEYTYoiNoO4Bep54c3M7UoAglthf5VYSs5WDjt6hzlZFWgTvzFYCeTmiNgMoGpjJrizshnpxmzCW136ORB2A2bt8YKu4Exdo7eqMZ8JhGW8aXkrc4o9Gg757obi6dm0Lpoy6hhh08tJlaZRb3YH3rTx4BxsfYzAUlFOu6gG6r3W8tKYP0YUXe6tjIKplbuRKaxjKEG8W7ZRpi+/xD971JAZhFpfyWQXn0O6mqVHGEQA2jjt/C5jCShwsl8tNpHPq0gUUMxy3NeckAM0gLdcCNc+FMOurlyPRrH3F+7dFXKa/hBVM4boF5xVI2aF4ItEESnRuoJ9LxFlFYjwG2d9X1IVJ6eXUHhOTOWOJB5b35iNAuqW7d5ew0wlDIBYwJGqaHqQb8dY5z55EDAlNDa5phsqxgxIRzSvEDEP9mEE2cxHDd25+lzYLkZnAoWtljODdyP3JG7VatNgO1VjaomqAx2678ceSfXbEqjJkfRqR+/sVgSKEnuh3rNqf1Kv0NLCbZFUj3XmD+rhKhj4OTGvVONbbJxeoXGd/ctPfU/sfVG8dhLhU6CJppL+NlWMVCPjKNNsfUrxkbPXJeyCfNJej4+XrGmp6Zi4scqxLdZGIMri62/wmnrC93dWLqVIeK69xGvBc9aSuLC3qwXukQgtxPw+FN1x75dyDuA8/4nCxZKSckw2j/GGCo37rwsNUwabTaIOfzBwNmQCD89zjhRCZyWi9N6pArqyXrsFAbpYblH09N8wEZk8MDowqxww8FnL3C9iA6i9MdZtpDim0tg42jQRJDdDGUYTb3cScAaElYX5wjexDUxeP/NLvvTA2vJpaLqQXycJBHSobOjPtxBJhtYc4u7b683b5URshYddzy1hLWmWOAzoJrfXkhO5ODp8IUzoBuxHsOyk77xzD4NWoUj7BKuohPuVmD5r49ZIPEfNmRMMy/qymlaLSsQosNqTxLVN5TcUuFV8ExYUZDtIN2cgBx1yack/6YzGyECsIMeIC/bLN4f8fwnlAtlrK4/479b2n2YBrCw7OmdAxttLkoKmxJpOf1rEpdl3e9lhUSAHyUKC4J0Bo4IChSMBR2HEslRtKxsjnstnNkcg4CDKOQxhV6/LHLeZyFdNqb46Jinqyw7C+tldCx+UDzEL4+soOYiDmErY60rxbI+4EHcvm/y+Qt2g8GzQwvnpD/DESf+mcK4H/qNivELbHPutb8e6gZpICv7sc15GIRCGaeZNrTxNjjFwyA19Sjvq+kkdGiKPIZ86jENqCuVW64IYyQmjGaDaHn2aybRCjoHqm68P9c9d8xi8iRu0V2p15dfcNxkI4ZjK51iiUU04ZSeGrj9tS5bydDXHOH0IdtEl0QByYPDwCXZZusg7kZexz6U8JMpXODXHxupCp1fy73+jCYbAjZRhWCO/Rl9deMSrdmOJ5/SYb0Af1myrR+uoGxXaYj9fW4MLjWYxkIOOW/BWfgKY12MrXsbdR8KszwAGQ6s5xFeEgoEk1KcbLXaqCljmMj/3aHx0wKyC0Ui/T9r0vN5M9Q3doZwbgFRAxQURdL4c9ileCDHLVSwYoZXA6oHliTdrcvMsG1VMauMfcUChIXjzm6Vp8pbk7NHkRKMCgDam1nzLFSAvOzP33m8x8E3IWVWyXFuKaCNpNTKgJEPVmpV8D2ASkB7eHoXlqjhjeW0ZXdvhfEOXPptZjg/+vOoo9r/nMafGmkl/g/PXVTx9L9ssUKDpWTVUp/BYCdK5072Um2xbIhT1g6zshOQ8/hg2QU9z1mTWCEhMwT8va1lPr1VQcvMnwUC0S1M9ZFwPl8SpaG76PEWMTVXD3XCi2DBG16JgVsq/cvKHCweoKFHIyRO4frcBI1aBduTYhQnJ3Ld7HJbZJ6OecSkiz8YiCYUHboHbNZ5VA5Ijc673291jUhM2rWeJHeijSIX/9zZdvQ7xTkW7gKCiIgEGI0sBBYErtK+w+ux776Ln9NnWq9IN8BjiXQZ38tzeJdf/4lh1KUiMR70Wf1CZdg/dIq9no19MWi4TCujDsXmTR02XsQQ9OBnkDOaCNaB8QhgsVvo3Y70wzD1yrPivgEX1msznQyHETiQJYM44Pj5BUZQW+p3Gw1opIAzAokHeI1jtujRvGjBVUAqQ3pSxl7j6I9jSZGOCdvqz0/tj5cVcsrB2LOXDaDDIFODL4dNJDnSoGms24Z9CH9eyfrMCHS2L1wofbHMSMts/weObQSkEp4ztQg06dj7E5lH5hqGg8SJw+sUCBg1yEL68Y7zDz5Buh8uWn3cLxgB0YwPdvyA4q3Ymd0F2r0pZ8S7327Ner0Wrpkktr9nogV0GCwHgwGc/xYh8WsSbpUEOd6gypp+CfTPXsSKIjCRKTbXvvW2BLIHlNe+PBaKz8HXaqvrY2TdhHybDXIHg9lE5jIbgQJ0wpbv+V/p/DkAVUzv318JbDc/ENuFFvZtcm/Y7ZWrVWhAhbZUQdEdBlScDOYPfW9fY4uPHj6ZNEAOPzrYaouj0inJrZiG0wECtoxlcMmxeouGGoO+kUwXH5G3MPw3hGNL+2xSW+vurXuecj3iriLZmFAOLjewxWt+Rjyk+5ulUH7gS+wEcUAY9DDUd4rs2ZCdQAD7fno0WBYK98jieACNnjDsoxG08iVmBwJGS3ZgCRwVi/KBR0SOfL86np9vfYqAoZDMMJH4A5DAz12u8xAStC599/nSYMqo76XfDWg4gaQOKFL4aUQfYkUYXsu/occuFz2cNN/UqjiCnMg0lmDboP9kHK+ETg/sVvHfjeSMx6ydFTwVRin8w/bNxlaq2AG0+3TmG7VWyztpdsrFwgi/fEXnmGxDYmfR7DNnP7WJgF89dU5VFSbbFB7jBWRsw+h14Y/CgX+xMFq1JYscp2OTEgxCDh3BYGQApehQvdrRfCTzsK3VvNX72h6O5trGt8Ug9LlpgdCTyGX4qcZdQMJcZSPJuf/YpvRtuhTr5BanuBfKmP8x7aMN4KvL7TUTNx2tNgGIc31sm3R5X2piFN3BgIO5L081dKtoWlbVI3uunrTq94ItfCR2KIyRjSB+bweaIJxVO5ayU4OXOltKiBEPHMPA+D5LOHTSDMbH1JB4UMTnMUMk6EKQIlxLNdSHfStaYEJ7FEmBLn7AYABahvWxUsLHM/JHQWJ/Z6UrQLuYs2KwIdwPdX3TdtRKrbcCCBs2aVq5FSlU8c1TMNExR/6McihlsxMZB+9ENAMSsE/whT/WpwsWocsWw7mqKNUqmDp68wKGwLM233ZVV7chSZ+txTlCYsfCE1C38gnONOsED9D1jZKuLZ6ewV//UcqG5BPmGBUngj/eGGOUAmqrW+X9jhcif82lQhNzO3jtBHeUqWHLrmzvTngZfaYEHZIH97yjUZpneL/8IOQYm3DmBAxTGuNdJnKfjdSIz3kBqFbEe5RR+CpidxinZP31M357aV/xme1ljKj03l0KwPGLyAqzhmj7oobW9GuPoL4TOxFpfGkt6QeVqD3s/ZmAwgTGFpXMy/97PcZHwvgBB9nl96zOK5LZdyz6JWSZ6calrkr/x9x3e0itesNE7mNZQXyYQRmWVFNsrs9lemCCDuh8w+bb9085SkQ+PIi+3qqUk1P/6p2VKXqDgBuy9xVwf9Ov/Yr/P2SBRLx568hJ/D8EuKIHoO/uLOIrM+QazJ8jVndrZKT35ogtXvPIJNSOd+/kqCeRRnVq0VA/vy9gvpxzMn/VBt+7+YTJQalFfE1V+RQDXywvjndSkQpuGcStTYMhK7vmUdFzc37NyQQW7Gya/87dSeba9URxxZw5M02RDJRjnAF4rg1H3DnRb4sWsiY3EISipCnNbrvK7qCCgUe/jh+cojJ8D0k0r51/iY3yxlT0+8e8lk/R+WRb6Q+AYCvBad330xC6lRPLzisA3qP3zPTVfpniaf/aqkF7I0E42Z6Ht0EJaLeHVn15Xue1sIxHXs+qNLS3C1WZ7l4mSwnJvKLZviXuFwijFw479355uRI52oZ6q6UCYYGJjNAsgoRys24Vj6XrIUzUQ2EiVka0XeKBzYy0NxrxTqlqVy0iFKdmzCkJTnb23Us58YWk7WLlc2TTP/Go7X8vMWKvHlnjt7jWIX23KY/O+8yZ8Bs0FLcYKwd+K+4LiFUdbxiLt5A54iKEjYj9tOIGc1RKey7OyZ4sSaBGf1Z2KUji7BiZCFarjVwhTCCOtCkai2eEv/evY0+/PH1SZq7LWVcC2aN3cvldODukwqRnxp2mPsCHExZ/DME7qR2GG9GY5fD7prlifwIKV/mkIlDkUuKvqfrD9WZ8gZrqIadC1SyD+qJ87dsg0TbZrn40GOAmIGBqfEI+qBdK1Wc4D/J4y/9M9WBjx67jDjYMDiKlklPDb6lsWGxNEjl54u8kijuElen0/H86BLaJ+5QXGvlzBcJeUBnffRBFZGfpMHXSY8O+tPDmnSichbIJg8l3zCUK85fzKRtsrqQLk7SPonYA7HTJEDl3YIP4AGupUOCiQZ25bGJm1Zw3IrMcYYkY3zVODRM0SzCwH3EkdsUV+owgQly784MLu8EqHB4XHYnhlY6w46071MrsPJqahCUjVAezLR3yzcC7Rh7OFlBnFoTQgZ8iCpo1qOzMyWKBKNEeNDnmnYWDAZGEtZPFsUvJHfnAM409aB/Xqgk1S4+fNjuSlkXJP+xf8OUrDulB0XDGJpXWH4y/Tb2/tdmMvkQnFc5HCnJ9ro6yUS3r2RSm274qpwLVmT64/LqqWwWvekzvd40gwiUGPCrbQIMNhRLGtgIZEUrHcLv2kTILeyyHBiujxnHrVHk2Hj4TV0qbNw4j6lX6c2t8SXyNsEw8DWbhQ+zpfKLbF2/6CFtriJY/X/YiX2aylqcO83QSWwgJz8v+aUWi1wpEtELqmq4/k4NDrWJBdkDsiwTR1Uatg/BC6XBrrZWZ+ttzQFOsqCjIaw1xoCx0H7RRrdwaAWsGzR8zK5PDBHefUON63FTunObm9XQOrum5eXGMGxun1PKPOX9e3yyJpzQTr4PCeURHrVRhPlHO7PfnIWDMLpHP3OoLSTxBKTPUrVVlabFxr6vfXF4WegKwRIe/WDwq8w2ytBVRFGFw1eG0lrvQ6kLT/Aa55KU4UZJ3Z3GQWptydoCwuIt1nEXzLXLELVf0d0WWfvr/pDsyXo9X1koorym0RWXIXWrv0wP1W8wI/XOKcvKJFuBN7CEHF6CUdWQure+UsZ9p9YhWH0BqKo7bbJ9bryUgqMyfiO0ekIQj2D1uQcfsan2e8hmJhs7n7cGY0yEYd0wi3tlD6aTc4mN8NI3K4tHmZpLsKehPNelzx9xgHRC2wNKRE9wKwUcN8K4reIWpwxKfCIQe3GpeTmvg4Pbov3PB9vNgNXiKsGjyW3JGA7lrji7MrDsqKo64fW2DNAWXgT6oZ2ca53OpE3Id9BVpcRd++F34ZXaPyEiEbDF+7IfGHK2Cca7Do3xIkHefWj8ZwZ2cWCoEAlP9YT6mttClvqdnRPtyEEeXdKnDJCjQhlCNrm4u5DFf9ZCKaGHxAxfbTI9i8DLAjoP4KbL0OPnfm71ux1mzONWdSzW+MIcuBEnvfpECPFDJuDxJ8+BaUr8cnmfffNuvpnt7QvVJWkp7af3adpVfoIcyEI96YgRx8JHEU/xiwWvTuPDiwSo63RB/X5QhZEV1b87rN0oUIA8zjxaYuHA4N2luIUhYHHPYGiZsgk9aBTF2hUyqX4VB/qPSLUzgfnuoWLlccXbfZbe+Db8L6UXp31Ld5yDrAOMPNrBEKRzD0YmJ/mOLAo01BurDiZVqiKsKX56eprKPoiUfGSj+HtP0FAUTMBa7lP5N+PbFW586ZhDe4Icmf1SGPJ/UK+kIjZ9yR2q6sayoAHPn0qhckdx1hfpgjGaTHFHrFqhoG4czlw/7Sco/wIA7CCaAJqhdwpvGR+YWXcDHxvfLUcKHYgrOt8f+Jjt8Oj6Hs54+oQPYub7wOkaTU5U9IMDI6ncxYfKXOVuKF8n0zvGAA7AU/3oTLotjUlTjfMfP/eySSAn6VDd7OcLTT8iCl+i/mbcYUHeAWjLucdB3SUjoddXfNVh5dzmkoJh7+38m+AmMqXs4FdGJhgD4k2Wj5Or9tUCCVxOGmAuvA2uZPjCEBusIeYfxrI99TjGevW+1fpz4Vk6mygiN/spw8pe7UQKWwhU/vUvLyzAqN4+qYGjXnUM1qaJBx3AIGmL0bgLTZhsMBF7eY68toHtgBE3UojkFOLczEcq4pzgDDxM+4hVW4EEUDFCjnmHHNqeL1upoO61pRz4m5WrL+VC6jcybmkvSfHDnoaMrJEAYa244MxDzsXIAjKB9vWHjz5pMbOB4nxgwEfCReHGcJlkTCxDoGbLBZZVH+NKx5CEpmCZazqLmd4V7RFBBgAIDN5zaB86xwT5diryplL45OfYQTmCoeaXWv/uoJA6daSDroKMATsn02f6fXfkIZDN9Yjf1wi9/XMZaomyUdP5PodiEVO7Vr7dKYzeGnq6bGr3+VSGaO/lJLAnD+t7oy3zDsXFDSvmzLozxfoOtQmx0v2lINuuTC/nR0ZG2nX4+TZrenV/mhbrJS2GEGcehqnGMwIoP/jMZ9JXxH5jtzOhxbHDbu7G2OiDQordI70n3m4nv8j6eUaU5H255H71n+ZAsL6kVcBif4gX2BvcS7zksKuqrW3YJWT6gFkHquMybXvqnVGnIOInK3cF4tVvqsAyQAbDvcLKm+PncYEQ/LIolFhPaOKPQBIKkh8WIlV9d/2s1YIaTr3fOtdPXCovMWkeTinWOsPGubCqb4gMN0QpYVcH4vSmfQcQceotmPHuxqZHSuyZzG6rIGQceVgWM+Twkchy+sJkiZTwO6aICiM0ut8pDceGNnsgGBuTAbgUCR7h8ctaT3RjB+tJl51q23H5R8K/Mq1Hl0JFTziz/61KkzrgFMmy/1qac/evNirYiuxaUOf/1jSjHxiXwI0u/f/AFiaymgElai/WjT93Auaz9bBRjzUCqMEnLCs3Ktv4nXsbkA8MyxVmy9HlG+GUp6NV7Plv1MokjMevUQxNFCt/oK7Bu8GXybN057e6VC6ESh+mGsEKRFSWxKUyYiRBCZJMznmUPQTtk3lmZUJB5s2llMSxZDmnnGS6JVCwBZg6RkeGskzzKXYIjr04Y6aXo52H9g5OYMdEx6rXu0KB50/K8QZbXSDG4SgaLAEsoizVk87Fn3F2C/awx+tROboyDCrxQDbH+P7M4Xd/uieCUoCN5dRSFkNFh62Mz1TdtCxBm93Dj6mEkqWXisXtwKOFyrTwn6QqmFTAQd5WLrjqbG7HBoV0qowRW6Kp2I4Gi7ypMS01lACZs9PhfbFn6j7a7V3QSDuSoI0NxhCQxB2oxZgaTu9lrjM+pUr6P7NbiWuHqxARi8h+jW1sXAlGfEV2nTZrLdmfSkEcvfVAsr2lMRupoWZD5Y90hVjfTlaYtTjoU1JjVhRrjVOzCDsUviCvQkq2pohwxysVo2BnGenGDcNKNTNoEhNZijf5UavhhlGblO2D1os2txxWselHp9LvmzyB15dyVkB5iKx1Gh4I7lqgmzVN2BcsdMk/35Rv9vwy1xsOriAt6IOa6KkcmlWvsQSPQO605aXrwqIOTZlupOzjc/0Z5StAZb/iaqk6QVWkpMTb6fY1jC53VGQ96qU40vMKS3QntM7bW32gImgNV2BZhQDmUWD8n2gQ2t6Ya6Edw3+ZL6FL9969wpQTcwRJLAJmdypXNPng9R1mxmi8HwDcAf1efUdmfxprWfrL6W1eZfLpLzVZOUhMW/DJnYmA+hvvo14YV8Pmd6I6EHV6+6OMGa/zOT13W/QuwnS7wSNlxzl4SL10P4O9Hsz/yk9fRTfh/dnD/FUifxs6iSsgCyZSzHaicX1SMGSiP+9eR4wI4HreKp0gLClWcHmq53vNLuLw/HuyB8ToglVoW9wGCcZSwCy2GmXNIlEsSihTWM9eHqQRJA/yFOhgiKnlRlOjStYA6zxclEmW3Rn435yWgRY763IIidHoZ85aXBf7XmaNTHpiekqkka3BaofTM1rgzSV5wELmVVSlvd7KneB1GKR3tap6PFdWjNpTMkHpS8m2Dl0nZ1vGId4x649sKHIJzdZmHky8B9GjrBC7gXtjJOBEcr5WvsYueHtW6sqxw/wJBDZStRbGCxKbpAheK7N3KBlFMJdq/2Tb2S2GWWggXET1mDn7y29QG6FHDT8tNa8e1qA68PeXpvAZs+KHrp3PpTHTYEpV0JLy1N1HTPQo2PM6JJHo51eQDqx6qiszWJHclXaTNbxK0N9RTU+nrGmtvxJmBDoRZnBA7t0aV755VMygdfiCFU4KfS//Zj+7WJpvUXD+tANq4KPBtpUv9I1rxoaIY7HLsahmVU984ebYL9+iwCNoovtWnlk88QGFiH3lwiLLVJWcyg4Nj9Wd3DAmyEjuGx9D36CgCjQdUTKL/qBEQe6piGVP5LHVWZhbtBywIaXxXGV71CAHIs6h2kNAWrApAKBHhsUZ/cdlHBs8N8rQh9h8HeSN5KO5J1ca01XN1PHZh3Yu9CKnAIHb173sY8hMgZa8AfDEDI1F4+h9guHJWddXuHdjqN/UFDVFtpVi3C+XeT//mrBR3iRoGBt6kmtD6ThsjOL8TdMmDF4DwPBKwMDKGI/fFwJA7M5yR+S7Rqu4KThxqN6lWNT+TzSANgic5MPhqErOxPkiG5Gabmlnh/oECPOimYmSXeEkHBonfqrlYXG3hVFfs5bibBikUIGLTqmgnDQs1Ur7VC5mfli+Sa00Kz8v60Xr+aK6tIdPdlxWYCsjaxwXJECvauloBc1GS3BsuyUbvWT01hp8IAyzwDRywgP8RXOmGPbDlBaisOUhmQmX0xQAB91O2SIzji8MZR/b7vkf0wiAZsf/agZhsiumPzFR2AHfkObGtChS7uudxsR158KoSIg6Pwo73zkIrovGDQAeC2/VubcGW9ywSDV37Z5ZH/eoeyl0dQzh6Ea+1RB68eltRclYKfTCQjuqCfbgEXiZd9THwXXqwIa+dtuUAJXIqUBVBku59TF/VF1EyiW+B7QpEz89NKmeZJP8ErWeDlot6iACihdVc7j7BMdrizQ47fXqkWeOvkuFeKZlFB3SjdsGLoXHKyUcjwYDECqg5vsPoFaqCkb/mNg3PXplfkT9rdqE3NBB7sgcOSeE379vhKfssaMl9FZz/VeB8MJVXny1hvDWYGKr6id8H4RQg15avzPCMP0KjqAML5Djqb14z/8DV4d+muE/yBycWpT6phhon1JPeQ2Gf1ghvYWrQ9IhrfFoYU9SPLheN4ZQg37vYJVUQqGOsRhTC/lXZ2UgxRxpSjCaNaq6/vXOy/Joseu9N7iGrzZpljYXKUqP9pG5r9683uPX9lAUs5Fpp67Z8Xk8RLDRcPXpTFobG91MC3juTItIzsunqIdkpvmaajuGWWK+9QFaNd/8NoOfcAy2zwBeAKXgfmk2uUPP6OclXzpz826GnJ+4cZrVTJ5y77V0bQIkjapQeG8tYDSKJcuCroarGdzaMOzChynS5UCLk7uwrOA4A5u6fJ/9xDdxXeN8XqhNGk7eYyIG9jyhqkPb9W1C8j+XIqhUyzwKBCd0+ZT605kK8NdA4XiiTWNSdT3Z+/L9M1nKUfObg0pSD9m6nXC8JynBl4mr9uyQ6EvAvbTMP8HSxeC5wwDbwFu1UkRmb0vrgurjyEdntQY9IVVWlnJ8mJZQ7IJy/NQWh04MCETaPsq2N4TylOKrRgnrKaK7+MbTpVA74JMHfpH9KZ8sZAjPlOVH2bYQPI2YvxEQ+boXvgjC584oUiLkZznZsseBY1X4dT1JHjJMtEiaiZxlzzhP/L7LF/Ip40aba69ffwudXHa1mJK817Fbu4LpGkjelbPCgPrniNzSQHZ7v1TK0wfP6Q7m2CRxd4fwWEbF03eNKW2DO7/YOk5fEblFaxHdSPSgaAC0ALnF4qGqLPNeJjNGe8BKRV8UZ3vsghEOvE//NenE5yAZTvSIaACBS2B62FTAbw2xFEPY/dXgiuc7wo91FPAOVpVTibK9G7CPf5zW6hxIrFnJCUVySpVmf9Amp57qICsIZFDVGxEEvSwg/rDbftkr/cSLzFSxEvQ4tsdWLlRbAb0v/QyCM2K9JNujGHJL1FgqPj0ZGpederaERk2olpMnx5bvR+AHp8LQUguEtXF2VrGkHdQbHdQSZ7IjQL5Pw4uyThytxG8qdLZxgp5t1VVuZXE/dg/2SotU2Yx6u5vy0TVK/TBWRxgGG5ZWjLsbZ2fE20YPXi/vgmL5I6ilqoMgYb1cp/iMqRZqK9hNI8ifX0OFDvUZzr6ws9fFsv//UQFoNfzEwS1c37Z/rQJi5Z6Iny7XQc8Udl148DkUl+GCU2DyVvVOfjV1+YPzAZkox3pVszm7LD6YwDf14FNdeiFEwYmMchH3DcjmTQU5DUrdBsXwhAxEvMQ5k+jpTwD2Xz61x/vRay0VmFQ1xbMBdSBtVvDRDZQGIbCXWm73E2RAgL52XyHs7CDVudhbCWLM7Ob4mkb9Atr4pylYZ5iUZMK2nahDhLp3BgFmlIZKBOoSDvr2XBwHdoHABQSXLksqPKfwdVrNzukU6d4gpKvmrqUdCCX9gwruLlAo3owr6Fnbc7tBE4o9Z8vXYQgKhtMNVtObo0MqJ2erAd5ave+9a9/lW44SIoW8NnnNd9ZgtEd1ZMiZEsd1iwvm52wJVvABJLRCr72D2fWq7Fd+0KlnoBwo3p59lEbwjriU+4NAj/aAbS5dzWVHA14MUfH8GGdBvOBlNONLnbrqkjD33BWvfJgLPIW+2Dd0xbX42Fg61qc006Q7+xVeWV9SgmvRScsLYyB5GJf4k3xFVsLJjFCu4qDMlDMRvOY3izaEgfHMUUlbx+qYCpjwzFdG3NY2av+FbeGbRe6NPtFC0WndhPV7wqH2ouLO4yi5+rw6WJxrNZS+Q9R51jU1PF+XR/UkvjW4fn5Lh7Cz14BtUUWPi0cz+M8H8LwnGw7imu12z3hY/npONc/X9hWE3JkZyShM/qVGm98tgrMZZE2Ff929jlQfpPZLWcFyLRywtD1IDi3S4xo80oMb0XC7TwM/RW/O+LjRy+ef1WZNLSsSR5Dm6oR7B4+IMd12lVLM64vg/QhPf9cUwexnsW/HKF1E5e2SO33eyaSWkn3uQJq37JHkEAxT2i5xD3V53STlg61ZJ3/G15i4IIx2IFPpd1KulTZyzLaAMUy/VbOAXIsvFQOXANfYd890iCIK7YK9uAP3eaM8KxdcfiHyHQ5T1Zao2SpRD0CpRp8fv+HKH/NXci9s3Ay8fgC0tuL1DCiCIPtAsqbnHrbThE/Di1KkG16TvttmtqDJYx0TIQePidIOK3Y2we01JGIGU8ElO1HfCziMQBewxD1a82y1TrKEUTVlQtt9gzDi4N10FTouWHnpQbz1wK/mahmiI8up9+A6z2e67KV6p8sx33csIgIjfbZw/TzsbKL7PVKWVFscbaAT+xb1gGvSnWn+ySJiTYP00oteE/FRD0x3P86dzVchL4uK+LcAHdVAto1sG+w8PGKLnuxcJzWwUJqQdyHtjylW8MMNuhfcRfV6bCnK9suxQMuGd4pwOCthF/iknAa/YCaBCNIoC05OzF5wZMZWmTugHF3hGiOjI5X5c0sYwwbSV5IWuV3l7/YS5yQLbqzSVpWQiScC8rBo6k0UBNVHuyREer69/UsKAOq5w1R8sOJzUp9mwIVUo0uAEOm1gxQb1pHpL0zPpIQrgPQWzLrtNsaggLHiTzRJfhTXhyNqvEYYmFv14WGdGx9CGpXFbl0B3kvud1mDkPNzXBIlHcRImEMQyb2GT2PsEG9+ceOhUiUEcTHpnx10nzJnLV1rU9V5cjZidy8yqehRve4laWyAs0Kw65EzxkOt851jtmig75flqrIjD7bxFspr3GWxj6g/mKRn7r1rRrGdVywJktu6M9RCv0xTCyT1YVvStYIY3YX2QpHr/vHw8WQ6uQG1Z/6jzePJdlU/u1CF9Hm52tBHLhPR66OFhXA7o5t1C8s6qvXpUUfEYZwqlku7H5Bcj8bIT2rOhpydGfiq/b0uU7lSFiqonuw/lpsfE0GyjV2TbYEHoRnjDx8AzZRnDs5xA64U097bOh2tojD104douSBotyCLTI24roP2hZ/QUHiQuWcDpq1W+4m+kkiywfGCwsc/S2aH8XiAw8jIlHnnkqOkI0izMUO72e5/snjLic8g/8FRbA4LiqI0LZTOhpBTZHD6FyDtT2dukN3B6CJ4sgLT1/F/eyts7Te0WdPW0gTrQTWijoY3/I18kD1YQ26mMo1A9PvcXJFSR3VtqWTMzIsoiT9quDfiEbzLXrOu/3gmMASUnbhWRx9Euo62shgjXFgFDneH8uoj1Hv8XCNg/KkEWswrzNOIKUUvHCGsFjYHZxbw6eToP0Y8A/iySFTnkths5HDD8AF0kTeAv0aGKl864lbH0xAm07szIeLyGPmOns+xNV2IZBXzN5owXazDgjcUAgoXu11+gQUXr9Off0HwVpZ+JBikKhSbIJXZNAb9uh4eZjt981F6GLY/NDiKXbXei9idYsg1lOsdc9ILafU82HvBvtpQhe4d7x65d76k4xC9ztkZVzhl75B6ABSAK2t4dsnvsYd5fdI4VJ4WhK6GuJLtW2HjIsG/dh6Mj2fJWYXwTi52kF7PR1fWyqp5oiTjBwYqwJC4xuxv4iblZa5sPAhNFVMX7X4BFIbo4n/QBmjZ/ms5fXbaEgNXwRTGP6IYaJ+/z31cAmwzfjxGVGkfiR0mRY+B+2t3bUJu8q4AhLjtrMfNN8ymQ56KEPO1wVG87p+xkyK2mX2b2ayJR9ayoszQJJW1Sz4zuPJxwPnyaNtL8pagH+IJu2GI7nTLRFIaaesuD0whfDavV3P4qhLoo8N0nIJKlkD+H67wIFDcASiSFCJNvVNTrGUosoXR/4hm54GdE7M6D6bKYUkxVqk2/s0ljLBLN4EfiCRTJ/yYCH6UOyaO5HRI7zOKyblxWED5muZcUve/6O/elVBGZSEuv1bfAqJYGFVLoHuEyZCXrgeH/ZlAdwOWmbjkqhaXfAvei0/FDtDg9fMPKk5cCyFSCgo7FIY06dFBDRb1q7dAulF4F+RAfz+M4SnyItlylBZ8gq3M0vZgPIgZlQsGJRSB6nxiOLXI/QB98ElxV0ABmyWnnwgUPAobwdNiXXc2U8lKaflQajCUxMhg5gCueK352UMzdCF+k8cNtAE4u9OJ2k2bHeXnv23HSkebSfZuQpkj7XS6KsBoEGbHOKA/4m1qQx68rI4OwIiXmAqQNtoc4noRZbBzTHrEpN8Ya1rcbkPgXZ2wa/69LU6rlRo6C7gL9r+776RRDm+DtfLMY8F08iTt7+1NFIsiFtTHjr1ulHjgVhwJFz2MtT4xGznW6hB1mu7s5S++tM+Jzu/0v96N4A8aaBuWHAMc+MdulXEzDRlmuSOqV5KLtNOlF6meNvpkUko7H+wgIBhFxeeMDb2UzonAhAunFY0D8Cy2spsgtRerCiq5FecyvB2LkEecCpS5/hGJ27XJ2O+tCpaQRBz56DCot7dMfW+6rMsdFYfItQ7H7g922Nm1eHd7DOnnvGxTsbUxkFFeLG4t8Spn4RPXDlSgY1gs2ZDZppkhbHKixy5Vh2Gn7KaFQFBZzLvD1ng0dIkNHz5uh/pzR3cEnwEvfJ/fH1JG3X7mGY7p2lpcl0/uRKg/D7jd5S34ZT/ZAx4WYFRYSeFTZvwlK3IPRj6pjS+miNu7J7ZV2y+xBnyNmui11FMnXA7+X9ABgJRQr3/R4ukgMX3eOR6ujgW0s9Am5vw7a9AEpauQBRicPBoB0VA5gX02bGCj+4JZGkw14QLWn7ANTk2khvDBesQBDMZI7ZaB2B5833nhM10ylfWYP3EnsXiItncl+xwbUqIhL1EnkAbk2bpyhB861B/4HFvRcIZePBUaoTjNn8/MvoPaVxwSBGtwERb+ufqzI6ztVMz1Oq/GjZGGI+NNLn/A7FPDybVMUUatCv/ULX7NUmCvsAWesC9UMEB6VtxGiUGCdTpyESuaMiZXD1xIFGmL4wY07XIJ2FevMvbcvrv38cqSaIbY2pFpSp1sKInqpXBfW3UPgNl+Sfdrw9eEkMy4LYHC0JihbjdmvwzaIcvyaVwnR83Hw9+7Ax4exoFAwh6YUrEkZ2lOPnUiJ3ZMrPOZuDYLMjbvWkeq/njb+e07ojjfeDxT/995RH0uVht05Qm4F3esTBNzME774utKAD1QQIZ/Cc6f1TJeKIoLsxerydK+ecNThMYN1vrOWOAq319QpY/XPQzGWF9geNBpWOuv3UzZd9Bp7rczb1QnhdpFkRtPoArqcGaV3ifLP01CDmImiVo1wUkSDnW1M0Snvq46CcX7gI/nQ/Zf/gAl3Lo4K9oEQt7DuVwu3wIh8z+rODcVXG0yFV2WvgYRIGO0E9K86wWJtX8NMSAvmh7IgsK45pndL4atYjpwYeic8inwX9FMUUiKugYi3JimMa/uJMKBXrAvHQNeSoxwwMUbVunW1oKQiL0PRo75HSLiSKuEYpDNTbYK4oxkBI534fmYBd+p1BsocSe1zMaionnU0syxb2ztoQ+nCmhLVnW7QXN33TxYtyv7yuuY75zbAG/W0hhySFt9apgIPW/x3PA4Vl+GLV6t7JOoidYkACFh/fz0PdrK0vdHEcYtkNRl6lIX5v+jyYRDoDyUhVvhe9NwNN7v6f5XNyf4c5DHu3sZGW4+VD7auE2J1jmmDCbU4v39CanBNfNShAdsQbVvRHa1AvPIACB6kUsf4tEtVfg8lqIKzjOsiYccSJIhpuy47wIRNVnbF5f/xNZtw+YzqW4pYlyoc5QsoXoHtsrnC3eL6d24PzHMPksG4pTGl/3tBZkRQuQBpeDJaILdKYyveDsQjtjFiqKHr2UiqNxYGHbrtSVz4+CtJeKsL+IoNZn1FofniTTtlyKD6QsTzJh7nLs/A7T989BQD07Kl8AgQWGsLBLJaQa/h9bB32gQdSxI3T/31MPz78OaGCJUGhRofutorC2LNrbGkk5icb8z6yozek0JpUFq33dyYvb3litx7m3ZAmkFY5qqS7Ijl37F2MIOuUgkYH+nOXtinO+dvXEU7DMgY9wWDhHtcZglwrWdkUTv/V/M/XeVxGJyU17R8JlGWru1/N9a33Czr1gzgV3YAoogdgof1R+wtyeBmeu+Sgh+pfRQCt7tIwC91ZRoaQZHPY0jRfc1qdSWayMKCMpHUJJ5WrMp7o/fAFmmWA9mr6/pzK00eTVG9jxpb/1m3xQvlZpQ+WqVl4pB19OQLf8kxTmoWTQJ35w1Xk/sk5BIXFElpUA7sCAmiiHy1VfxJt93A/bSydgFqvSh0z/qvXj3HLIfsaqaK99qeWy98DSQxnaV+jJonr5Itfnp0MHnsQwHBhRiIZ5vzLs6FSX4siUpQxYMX9py3RpcLpu126+gtA8rOH1MHQscAxCK0qVUBr/JZuwxvm4phQGsz+bJvxETWzsmsB2Y0j9OPmlqZV+j2hk31WQ4+KOaxtfJdxjY/wKdyHbBq3aZreRwDmf1sMKOpFrR0gC7N33EU+0weQwX2ijeymA7WxmNoulzaX7Sp/bB/Qj9dmk4xjfrHzqMQCSiJTKka3HhXdIStCUU799q5KSczf5AJvmerKrzLtH1manFcjLEuGGg0TEnyoSXHO/HqkxawVy421l4J9pS2J6oFw8Amx9sm55Y2c8aHxsbfIJ44YPPYKjm+/RDxH7KWSq6UHuI4+oZBoQO2KyJ27rBeoNBwL6qCpECS/wX5NsxYECs5bSII5KwTgBR9x3mvQLoWjhOUmtOfvPW4y08rwexoOHEHfrKiXWrMOBhZPERK53IphUnD1G1ZIhW56moHfmZ+zzkER9zEgm2PLTEIvmuMfxvusknIapAhoTH1n1+xm6TwLoENvIrK0BzXtYFWlFg7Z/TDulcpwZiUsJwd3wwq0WDsDunujd/QeQXdfj0NcKLeS/LNeMsmztPJvt+r4CJjNG+rYxHZq9kFgNmiwd4voOGLRRbmHXbZyg9e6uHwsbWDb+hdUOV9mZ5yt27KONM6wJx+yKKAOx8Nc3GdpEBwBkPMDcmiI1VKd/dph1peNMg078/1Ge2Y1eNAPodVqsYk7mLMX46kwmgJBn5r7g5Uk5NmCNGYgQNewS46j8nm1z28E3tK7KmVp5yf0gyec40e36BgEsr2gMMH9EqRwMfS+seIlVnvmz5D7MYFPygxGrCI9EW1s4VIHIL/eTr/cQWokPxeLFhy+0vCYMorS454rQc2Bo9nXXb9Esu+agDuc5ASUxTKhagoL9GlGjGExNZoM8FfDcWHYRWNqHvJiKZ5XFLI5E/aP8AFeCG+67Ha+2fQ2NW01hr/BX40cDy7bLzl2nmSIIhjyVb9Wu6a1wDv67UboYc3E+3WamvGQMacYHxszKjPWUREQUi0cdiZ27xZMAYA3gXyaHS3YtQsyfigVd/a3dH+PB0QPguxdGUtAbhkDEJF8a+kCSvOuSMuwAZ8QyYBn8RhZ5J88IaCKFfygiKNnqg1DB8JiJBs4WFY129b2QiCRB0HHnmxA/sxFvUQFXG6D8RSUFDPobOLTONDYqXQWtwWhEUr9e8vtFew1c5RqIfKLi3pFb3lpA7VkmMvyWlqIOk6Ga2AYi04wji68nL10nAn/635TbIPfqd8tZoo4d7zIUMNXOe0QCCtk7vncR+KEDdZy9Qkkfqm6H8M7ohLT01K2UPepXFM0ITn+DVIQ7PwTMDvkJEN65xaRaSYmTwgm7rNB8+qNbQ9eTg3L/KDr08Dou2ppSlGZn0NwjRkDE3upTGRz9diZMGRQHATLSUVUQ40qV/YNKuyckncIjgnGBHygOd6hgOuMTtDeMYl+h7Zu8//ZN3YYiI/s1s+d4SRkJGhjoqEx7vktN8aWyNaDMzsjF65spkyvbTvKiv/unYXCzBNFGqtpeYDOvlaJH00uqv43lacuVFiyAUq4HfTS65WQZHUR7srYd1Pz4krEAJnCOPZo9PKW5dK+UwKtsjoT9n+3oZw0XhvKwF4gDa+S69HlGNw/oScgq6q6N8NVD+4eLwuz8CkCwe6o5OhfLvjR0CoHdEjZ6hI8UvmViUXLixJZPfsAGyYsX8JtCUGLyg/9XSvIjWhy0W5ZrwVQPA5tqCI3+ZVj05yDUuj2VMBLxCEL+2Y2BA0yA8l9vMb3+NsCrNykpPeXTV7Q1CXECjirsInAH8Ls3TzcvnyGKr0xj07oYv5m5xj1yOWEqPO36XXp7IVIRT6fuZsD2gO3ToIxKxavaTGluJbKGwPqwQgwslwfaBLyTJ8qi2X0DGjPgPHVt7j9CV3ND7TgP3lMzdblgRE0A/2/Cx0Vbjad3OGvMAK5xxb0V6nxZCe199LcbXmmrcrXRtOqi9jxHzL5C1vrBi1GarMMjJpQM6i1WVdlEXeT0ZLc7rN4ky3b7n28OBM07I/xM+kJDdYtuX2bhBhjzlbhQtKrxmhetJ2ChDU2C89BuJoUQSAcOlIvHdy+F+mow5PQrAHJMHgAssrRcfAxehLYeCjbo91S0psbaFInnzqDSJSit4P3EOP05SJd9wFKq9aVSM3j1IZPhWFS2EJYzHumei9Nxnx++3mFgb2ll6cNagGJuvJG/Hd1GFOVgXXA6LbC+UXxH3WcJ4bxH4YJaT+LiWfRlR/Rw5Srx3Nyw2T2Qebrjk8PsKRo73ruDJFY+JqhePBhmde/4oh3gO03DRem6ifay120ZcTg8EqcQ5PIvy/SomKkTQIkkpax+WA/SWEMdeBro5hRohEW0ghpDcyd7m74IZRwDhz1AvFaYog7+z/JCWwkTjlmXfhppJGKhCBHxetA2z9DXwsM2h+Ep8qL4uT0gB+sSJP1cFE3AjK/kDHbhkXqofdgpxjBQQ3KydBTfeheqkOF6uyC1g3XCvW6DaJaT68arTkgflM17Vqr0Uh1sMi3s/Khzx7mADXMRrr9oDVi4FtWIgJkftvPz9zeDg57txL0GAgC4iWnfY51mU88dX+3SYUoc4D8uQcyh2F3ERF0QFsg0eFHCbjyX51CL4qFLzyzfikwiqMkQ6shuNY+8VLKrpOjBSmQKaGpj6Nocsfh1ehKUEVHDBP2kyj7Qkw/JznYVgN/Q1ssS/5M6Z/XaFmOqu5jYsUIxr0G8roDlrtN8QMhk67/nt5STmcgA4IB1RFraYcotqV87E2SlxdTUcCNPaE3F3kPkKNFXzxdvG/q4X60cq//+4rfEUaEh4WBcc+hRWJGn6ex/G+a3pgLMupkUNrL22zvElyxYmkOxWd5vkrY0gwvPnroM3aAR+fTVx+fIIP1Saa8wvQpmgF7KW5Hkxx3TMlriMlgx8kOkDyJJ5E2RTVag6zNk1zlekvHTWqt4oQ8pGx0w11YaR23cRMT1yhXvHvWtzp3kfxg/RtJoT/kqIR0D4gKRVXvG3hZ2CN/RlkPSJ1XM3LK/0Kx++/fX2Tr00mPz6RPbKtg5ebArX2Owrxw43QFoJ/VKY9Z0B1UxZPMUtTdT+TuTz3wNMh7xYOBFsLcjmQjoMEyuUcorku9xZJJ93KvMBD8ZeLdkUueqh+Qq/lwLaWKwUi9LTTTNyEDIDDUSosCa/36+Z1IQjLy1nCVnC1iEgNCdVxZcUrEhC0mCpW9SiI2qqOBaK6e+y5AwpbDQj1e2GVhqwKOV35grpGsd4z2OInsJ4L+Ff/vMKOyCMcFM9LchdmrsJdzgCC8IpwerMnr0lKceFabPORFE1iphn+Dld4fKdjw+5hTdNnAQvARlEbt3Hamt9zA1zzGSjIignVRySfNv1jF2qddLiTaFQk1f7QxGqg82/Aq698+CpygRbghZ8JudwjR7weMmLJ71muLZxYHFoDurS16kDjvfY0tneShyYbSyFGXVJeG79xJ/Ujcz3fFwZ/RjHrT90EezGaTIeizy0dJMd7OZKiqjoOAw1jcYRD823gLrrhcW79AWyo/T/9qizaEprLCZVqVYENoajgCa2pTfdGn7PvjExyJidqBr6F15bSTUQJrfrBGIwKA5BB4BxOso69RcuYWICc4UPXnA9658WsLDPuEBw8cP51JpY3qrKQr0ArOtd3C9TbjHWmIOEh4Jy0yhEKmoNHoiimNTHZD1AruKqoGwsMiSBK3269a1T0TTXfIdrxEdt6OWOFFTjnPTrXoAQGB1xLAGkNukTbScb4nwB6QSW4ar15s7SaGG5jv8BxlsYCn4PZQmDIgpkYX8ZVYmtq9h9mDmtvhOuYXAVTv0mBLOFVLv5A1+8jcbLZ5EVE9T2b2ML6U8V+llKvTxWYKHSugJY2xHKJ4ksOq9LXtxNmhTDwYkCdsJZbVaWzkff5dkDQVv+GfMKw0V4e5Ogq+6B236EdjS2TKOrMoxGgSWktn3BPVNYBmazWX5WPJmjrdTElnU/XRvBv6JCq7eJdLZwwuCHCNKOL+1JSsi2XWYJPZTAWubgnv/yN36H5hKMVwR8qAD0l6AiseIUG1tM64WZ5oeG4p3HYA1+hBjBFl+U3RuyH7COOsYfcd7FH5s9MtCbYQGL2sxxML9CQF3gb3/b9GeVYvtF+IhD9oGp75lMQiHrnHGY0QdIAx3xJfdFOPcFwTy9GyuNlLi1VFoBXe6x/N4yejndSWih7roPcoYsMnBKGfLssCcIFgFyIUHq+8st2GrGzlivqqsB1x2k6xsLbm/8AwmxSHsPRtKGfPy9Zj/yyeu4YBe14R3L9XuJy5Y97kEY1UGwYM9jAFTae6xHaUpPWZRvvj7lM9d3IATQp4/Wuc8RGqsvXz+k9dpdNC5ulgb/WAtGgoA0iqNRVRXERSclGg3AzAN3QujxpZGHL7JC1himyurAPDn8yCA5SGztBi2xaXERklhqnHRyEOvjHXEzTahM8HX5FRaUfK206M8EYVmses6G/FQEjYmG2/IWXF9+a27YJpUb8KHHoHOFYod74hDLhv4WN1Mc8EbdswQrA7iIG2fZp8e07V0vW4hb2XDj6a/cHRIpvWqGbU3d09rXLn6m3mBr5T3ybzltmiB5Lk7S5hvdB3fq9IyFoI9rmaUOLFEiikvMttmj0/zvrzmLB+MXLA1rVcnPTkeSAFjhQRZ8TnDbWkeabpEPdicMIl8LmoZkBpIfP4aEAvQyTwM9qSOwaW8jYPYu0mUCJM+YnqoRGzihcagireZWdweIFKn9bi0H0HXCHyDIedtCIl0BvV5IZTVDp7QAcTw4+jT9BEaIv1e33wtU9XVU7I0I7HIfBMntfpvqA9FEzcP6sX4vwTYs1aEv44IKUvDmeu0+LIvf508yNjq9QO8seOvBqzr5OeKy/1U/A+QYY78kwy/MhZ9zCDePA35KRYCCzXckTbB/cT+MgnxZ4WW/DfJnzp/9+qWJz/YRGTbu/pEaMS2x3gU6GWeJXKY+tE4ocQJOysaiE0kDikvmjeoquXvX6js1gB3siivyQvHedItvM7y6wKToXmMdgXZicfGb6av5QVT7ZKtwIWNn3xzIteBtFOLHf00Fbyl+v9vOQ+UcLXMJaoPz32exLDirCq6uusfl5G8wgaLod54JQ5yQCMl5qJ6BuW0lENbx2EXKaILEiXStfC3uhZ2KoDTor97yfzoQk4yTHyiQIXvxG85/QSp+CDxbK1lSdztEGLRXAv5Sad5s8tVeWyZ038MhCQ2GPC222Qj2fGFrV2xOd72Eq5RiyrH0DxBmeV0kmckOBvu8obLghlnnPDBTuTc+IxOFehJ7z9C8/ElnE0PZwZWJwIHY4hY39EntUQYt/9Zj+tBYIbrfzZpms7cVZx32kzfEDNu/hk3Ft2emkpNzFyEc4qWkEhvY4I7tsoA2WobKWI56A4AwL7/wcl+/up/MCpsINiPAqf2eIrEYM15PoUcOs4ODNvXsjXphoO6c9Yf2WmEj7mB+ck4w3PwpKw7v5cTNuqKVJgVVoju3d7L0eqHYifYbzGaRN6In1krJNnyGpcxC75c+jQhNeKU66cihINT9lRlf6SHnNv/EmWw4IIJDdwZDwMBCqV8608NUSNSk7lUHEjLNVMCY4RpeN6k9b3xBrcdN74qFxtKbjIe7G64OT7gk6GNhFlxUODMMEkFjDpA8xO8+xDe41fVoN9YTuxEWh8iaHtT3OuRpW2teKLzGZA+2SKuF8ap5V8MvRMEUeIVIB708kj5boxlCUKRi+AwSxidOJwcn85lUyzFd0hxXK7j+3THkNb7ND6eN8hGdRIBg9N6h8sMroZ5S6bOTImeB1oZEHOFBjviS1clIEhr1ajmKI2DxAsSM02fg9qNjnZxQWflHeh1h6lnZWuaH0HBnduKLRGSrgq4MT4GNruBmx/MEqsBTrXj2ON82005uQjmDoi6c6YX3++v1Ikbo1gRjb7pTiBmfsd3gtCSi44j1SZ4q8ssmsZ5IztuNFO0OoHC19gf+TdIGlLPBXrxrIBkhW/0/b0hRUj2/ekwnDRYTJU1RMaYt9DnhFua9E0C1Y2LiQ+dCQHAGKK/E2q8+rURPwlqoX/+a/s5TnCSoTyqSzsiKZVvwHer1C7heIuu7bbFCQQCNGdPK5yGIVVM/84qkoonpigOtlDBuoUaGTsWHlxTPU0uuHNmxOV58SFXwfSiuvQqdjWTUcddoj+RR9zObF1areVyJA4khcmST0tu/MWFbuF8uJdA3BjHrWTJyT3j0KORFLsaDEoTaqeoWCUkcs3mYpa5EAyMY5SW8E4Bgl1FVnfM8YIZPA+bAl/5TKLgZIjrXbmtk40uNJ8xombmjUM0GB60lf6FUqyPM4cUrk6Ul/uZUHckfvhh2PB/qECD+hVaAt3IFe8CpHAZS76qTsGIwL3U1pVfLJlWrL/D+l/i1WzBO5nGHRb92ndnQxl1ymZYbAvM60yw/PnRZcNhXamV2erGJs1zJOYVaw1Z5fbrEXiu/EXsjqhCK0ySdEkvzFyLPIlzNovEIz9HCKYR4sSaA5ji0hrZejDtukMQq9HaOXeHRYoSWKq1Hg2wqaIqn839t/TWagHdUyCv63CTtOUuUvBvcklLhAzWWnwXreAU6EU8e18BWBooDQJXRHK10tYAyop4l5Znc3ln3qag52BtUnYC70Yn6yeEbgp1vIsFjE+kSJSGMoDVy9aZGI88eN52HlqsPmhxtU0+bwTBF332FLzFAfoK8M33m8QjjM6EfjUkPKT8bs9ETY8pDkYDfWl8uhXdUanvyTY2YC+jV4TZMSDxCFdIvakzbKFNpiqot/8aRaVMS8Xv7v+Q4zsfNUNT50RMM5WLCZJIYl4Vs7KuoHIAMX656cF+osyzuxCTFJqN3xtCToG+fUe25XVFKM3byARDmSOSMhnjgayZgoDpf+mFFEhaWgOnFKxichcvzu14OPbOOlinWyfExfe77FDC48slAdmGuAozL+VlAEJsHzPGYUh1MlJZvSEDUCtiSr0gfxRRc0lDTCJAjRpplfpATvahQA1wpqolMg1oWdJqesrh63Tv+3echL/P1jQQKoHi+J4toN6lOZi0+dMqML1AqpcVLHgzDit8kpNGzWS7Xf2ZCLv6haFL27G24jgWOWnVNS6B00oYb/UkYM0ylQTW/PMFXQO3+z0lXPgDSXpvWHEa9jp9fWZ6xaApxcAK9hBJFaXgrCy5p1g09PjThxxMJP20+VY1PQ9370xRGiuHl1EPaJ9pvvZsGUDijNPmDChkseXFgvF11i3Yj3dsX6HONHzxaWen9NGimqg2QsgXefaGDoieorSQKR+sLa+v36ERBzvYtmjK9GUFxWSdvNFYR1pGyLQgeFfflznQHG2GnjSlCE9THFLg23CTRAgJ7KePDY1dR6uc0uIXi9DTL5rUCAP0fDFRYexvXT9CpIU9W2/dnmT31prU51tF/T+feWW3suRTp3dAfvplkQUfh5YFMf7MjE5qdjPOfAhrQZGa3DlZ2JQSQ9ChR9/0a9Cc4iiUi0r6BA4YVRbE/5ovPjAnrw2naYP6rhyMHI/BWC0eLccIijlwuScUL5MzeTTVDAucBGOJ5ChqbZCVe/SpXhsiQCBymU1elHH/+y96yElp/l2F3j/xgeO1Dq/uMYIZxTQwWTyNS2XU5KovDkhGnAvAOrqxe4teq98Wic38tEKgwWb3AJ2dR4CgCpoE7B2Qxon1I94vRT/Gsh6YC18SJlaIxZ7aZykUQiLoR6I9IMoxper2BRWe4NF4LLaGUyrnCOfI1q79gIDuU3AvIIG8B02ZXv8yaL6kvqYi0yFh9cEdNTZ453XeFVSxJaBesb/NzmzvSPU/HVf2qJ4M5I/eS0R+nobCorvy5/foXWnoWVMKjsUnxwsQZ8RsFQcnHaYPENBO7w0cH2Zw3WfU94rmBpxx6kuXcrPpooI4pJKXIgs7+TTle4pUCzcXJVRpmjJpA4cficGsisHgYUEp0Ih2s4AHSaF/Ms2hv0zn7k4R5fUXIagARKV+EwKAYsWGXNNiyuKfv6pF8JB08gDiwUTRCDHMN2hvnUTDX7EBPnijNKyJGO+bGPFnj74zIBWV6Xks09zZ54/4pW1cidZ/JPavxjTL7elPEBKzYrzrDYvzD7FPeYHCSeO4GzF4VOBpH0HXzY5lk01BZADilZxZ1gX0ziLIFnBH/yokwhoYhxbd16PFip4nSyRuthHe9hps7xCsVxYdMefp4Cv0/tebSeMfbSSa0DGqfZasIU9DyS/HOA4lh7ga5UdL6f8IrDuh+oLK5MYMuYGlJQtqaxpuaKfnsnKcE31jTSsOcGgZce+xba0MVBbXEca9e3Oy2fhscS9wN3rDfGosIEC1x53v88u7UGGL6Mlpan7znlFNjbvmqME89Rdnk6DRl4zt22sEvhdhMpWshCUARNEbKfYuy5k3xb9j7PN5Qa8UpQnwHdhAKXZhKGbI3FoxLjw454t/JaIZ9L6Pdb0OoB0QAg8A8hS/5NwMIJgtPG+1V8rKgOSfdawSYXE8nGnBU9QVCOLUicC1GP0HNdnJL4rIbxAUyAYE86H1Ju6spcY5A1K16IrggFtcTQyW9qUi0CIAs35QKGJS1Z3WoL1IEcFp5hh8E7I7PCZ/W/TWZzL4SSy63NWut1E/abCOu6g2Rx9Cy7zuoFgOBLX832BUwigN/PBOEgPyb2Exyv49WIDP1mjjFalA2r3MR5yI3uWe29MXIUmrJVB3Z0tmUPP7WOuIYc4z1XSDRPC/E2Zt0tQ6WHv8C7G1VIlPwjboddCIJXHKtswznWynLKTbLQ4npKxMZBrN/Lz5FSR8Di2koIqe1rfivTv8RZqGZh2x6FKi+1On9CfHYZRdQQP/+akdBs73R4ulPt7bZbfRHg9VaSy8vH1rRx+Iw+VWdlgktrpDiVawlCGyI1Jm2wXX82HFgfNnR6S9pJUINu15dFUT2ufypzXUvLjhFpnjLQLUMPVY7Wmd4CUwJdwJD/jRKPU/GLuObtZ8FXFYadoOAmEIKRez+HKwxThqj3qqMDJQVGIJK1OEbt6wMBQZ8ByZs+4+oH/l3cgu8LXsJA88hBOdIrCxyEgZPQkhOFL/UAkhYklfuig7T3wzBzzkLS5+2vBA3Y07rlRTctVq9nfeadgrIasn6OddG+dHmvipgDHDha4exPcUS0Yxrsa/Pa99gsbwD/HLjqYI7/9T80/1gIKCMUfhhJ6U2yLz2WUfU1uVlHkGINLL0cn5sZAzlqHvZFBWOAkyzpcN29dxqDcBD4vcrFdBA+mXi1wxD87nMqs4ZTr8CRTFDCuXkqeEyCypaV2JZ7ufFFJpkxKa0Yh+rVTBFR7na7PAjz2rti03xrYtYMNBJSfAJg3rMG2h2IeznxCJTCKegOxXzIQz9TCk8zRiHLA1NI7a35r6nle49FUWM8w19KntPO/4ZQ868rs3mS3MDoR3e0yHbOnwyizMVPj+cFjkv6c7bRRzZhi11snSa3A5j0Xd/X2LSwTErhIHRghZvDRrarNw3pd9XtOneMuFNoC4GVvITfOHWuJHjBq4sa6rkUnzxdxiVe1wnifvS+gUf/TtZzzIVMClljkwhjyXrSusWGjEvhzCf4AHj6oiME6ZT8IU6OOV61+knI2XGGs/QUxY8WD3+QIo+rWPyM8ZgTWw/g277RnayBmn9b1p0v6D05J7jv1aMuwAcPAU8DPXS5AJWLwh3IuDDg/yxM2VTqN2w8RKv7IfMI/RRdoUf9TyhvMIlo4aPbtORipPUXXaAbBRYw5xDMRygqeYQrKvfcCqkNxukPTyapCKwHoub+nhESIKFG8MEX68+C294ZiyvPMnbvXDrhhF5aWnMw9oL1YLpefTMy7OlSzQvYI2OvFC8bSVZgy+NTrZCA9oAr2FMEtRRE7aps3cdrf7fa3YuKhXNFkPbPAetD+8dCVQA6N0NvXlfggcokgyWnkXZAbVaryqpIpCcVU3sa1p8vwFbYJcCwj7gkWosnzB7iVv2JkKmQXNlEgQqP0fIlOX/zc+/i7eDX3+PJhI7eNUEjMSkS85OE3LBwf+3yJvRAOEpOjiqRhTGod7h5EQ/vmf2w2A5T0k9/w3P2Wc9urNspXeTHAs0QESNFSSOEoAXdWd0q7PH1jRsChOuCaDUAk/RgW+AEW5h9l/wv3VmXGpzvvejuWrzEfuZ23NwopPZF/dWmq5/qncx7GJ1awtDsYpav2eASUFZuuaji4xcM68kF8oOBtDACwhSBWTmNmcrCPbTU22jF/dPLCVQuwINTYXtedRlApQxyIA6fqYECLepOmUrOdKsMwgIFd1XAcGmnwa/R//D/WRkDFDzzpz4fqE0tr8Pa/1TMGkl7UDaD6rCSrK+3Y0aczP7QVhOC9k/ry4FcfCSViG+b2KFMl6VM8LP4tzwqO3z5LoojetvliZOoxUFtU1NPVeBfLJtjjhQYrESDzcwgoTwiko4QdzMp+b0h5EBHIypF3WXHX8STtJZCDF8rhaKklWHN997orM2ujJwWjAN0/k9MHWRd2vMq1t7AaRspN5LmT7asaRFKkgi5xQrnQZuZ8fR6jhT8qmDUMeJ7Ri+avjE0ntkJejjFmaiSjx0xqqAdHH6u5aSA/nspDtgy3CAXXfI8gKcgiIvyR4iNsJ6+dSR9+aQ/UVPUtzK5tCPUNUqS7mQIl2xHxsOZp7xZK8Y4XYqEvPlawq3nICwCGwcEXbqz61dKfkzYpw/hUg0C55/bBSlp/3qxkyp835lR6oRc+MzIC9cuUyo3GxdTwBGDQZePgvAUkcosWFnvAreVEMmGvSHSb3D878jfoebC3vrSUbC1Ebt/xuIT6UKJAkLpOA7GWhWfqrtbiNAtE+NUnu727HU1vHCiBYwFCNrRQgg2P0n1k7A4clFSJzxnNeB5MAQ1Ypd9uNJNs+jM5LggpoJEXvuIv7IF+zfnJgJ253fx0LoAECm2R/svyJs118TmtaUdxZmBUBpB0fVkAAbRdqywI6RWYHJgTBkWSRuORqXA7bqghanPC2SUW/ZT+uIhcPFBs9WbGr7NiIwbg/KsEnaU3w18sgC4HFJ9yiSX0euPtenGOomih208/PhEPgCobh/NpAr6XtvKOQCoGNDtzcyvQZmsHP53NOnvmXlZaAjbvx8DwKxOVUKzEy8MeujsbNCp6FhnctBlbZFo5mxJP5P/NqCVwy5sIPg+dYDSruqdXMNYuCugNm0M6m/d4CGat9ht00pDczRj959t6uN9CTUm2QmcEzXXcLG3ggj3HdmQ9y9fprcXLapYe7KLeVhMLiCPQQ9b7nk3LWFLZMkpZyh5ajusaaNAcGP9jfFE3jw+Tfoxvg9nlJwxtmZgUhZY2twq6fdT78tApUzhzI9bbvTLpTkbzADeCk4xMfDL8EBuy3XcAAm74kPZA/Ag2Pj+qyKZMm87nNzPqzB9JzEfPBIaIzfSHY3otsF/AvyGzQp7bnyzSpk+Ec5iPsq5khGgRPP/GFEHVbaVU5VqcvY/VixQvmnAteA+IIhGpaVVLr0l2YL3uYzA+3KYts0jP+zvvtw3frNu5+Qe8T4FWCe3+VIpCDGIeytHLmicN4rpU46gruUKqe+qPkUxoXnXghOkgYeY+Bketd8KuBs+xldkeO/p59LwaZ+mJ7Jr1QHojFdne4oSB2Zp0ABq3VfgVXk86ptFKLzuMMT0X3PgVd3vhubjBLAqxbYheHB5EFFU0Y+dqLpUr6DxHKe41Wh96Iv233AzAiqiDYCEUuOHg2LPUP5HSq3l8VhFhfxS34008FddlvDcMr8S1HGxLD7YX/Pfsrr6IY/9O7/6PK6xO5lha6YG71J+E5zbj4ThGBAkGYlHpFUznWI3CW4WGSBaIC+Krlz6Hr0Y4pFb+X7KJKPNCTZoDZtTMNcqAD9vehQYmsEdQxYmb4CBxkhd1bk0KxQJc/VQ+/igGk1m7uSBzA42OJJt2rutMkyEnvxO1dYSF1OWaSWTueFnX4GBCHNi07JfkoRGpjK/JoNkx8JWO4iih5l/XM92Bk99KRzD3Ug7KNucHESEIOydCAkamFWhuSgkCjPFD3psdDH7xDKDBNrYFUMKtAwmV0ZdlqqZXvPt/gAVdozTyNqituAO16mBsvQdv8d8PyZomG0BcOAIcuGCo9XsBk1IKWYbIqhhvqRAIhImKTa/qyg1hmaOyobvALrULr4I1p84MmJR5ZR29x2Djg1aLmIEKqPgCdXne1fQvTt3ruCiZPMD6s1wsqNtBBaE9jtFdWJxAyJceRcFZLo3XZdrRsJmV5R27x8u1xgvYg4AGa93Ic5K+yewJiAZlckaG176jZ9Q0zGJqpNdKPkc24BrDhLBY6lcd5LTxldfe+A8IdXASWcDoHq6GRNpTZEXCsApGk4tIV40V3BVGHEtWMZ5OyN/XLhnhOv14mBjDbsujLZbPZ2HlhMsS2+tMm+tJnzGvWsp3iLGnlgM1/9eplcevciMFZbEpXi24m9zGKbuPa4UY/gz36vZwDsUJcgkGYPW5laBrPjtZGCYeL4upMTxPzM/CZTF06Cc3Vr/Gq8JtI7/5CUirqXt0pBpR1DyXHptEbWpmSsmfvQcJUzkTZ9Crx3CNqAlm9XzgGVjCM/iOCOICyiqkIIOuOVpbm3TxUFBqbzTQDiDxJs3UgpxTlRJXVAoGrulhSlbf/OAcoj9qWLP56CN+8Ts5Wb/QN/tN07UdKvB3bNMz9Z8a3GF4Ikfd1XuDUdWEejKiBjG1FNcufx1cvNnUp1TtSge3yEMJgtiMvY/2LVYwRjB4W8E0h+RODU2kkrxJ6JC3sxI7Dk+sonBv3E0PXJ1OUnI4xw0P3wsDnIhGd93CYhwdyVMDhQxdhZwoHlAhQQRE4ampRmL47EJQrCx3ZxSEFhHzhGOKjBidv4wd8u+4En2muJxKNNOVb8Zu1DpMH5MPBBbP1nG7iKbgCyVTZRZTYrLFcKYvu7Selnf87nOmYmWRbpBlPA8c+192351y9JBjb7YpKqNA87sLeeJshEmR7zriX7DYrsLvBf6WsF6Oa33u1t72cUUm/HcVvprVb6fHSouPCHxLm9GzKTZ4XACzU6uT6QwSbYdTOwCcOIpVObjOBjUb1kHoU94lENI2beUOCHWEWl9K/9EOTKRQHANRRk5qaxlTCbgcIVMmb+itAEK2Egq9w/xKwJAhO8oK5oYd+0DVDQ5+MJb+WIx4mJz+L9pjUfiTkPrtxV1TFymjWpsCnvJEeWd4/49j8HwKVNP5JcEAK6AFoxJbnxMsUfOUrPkP5VDzrCTxosuY2bkKy45oO2SOco07RykbARCcoEF39Fz5VgxyAZhsCBXu6/VR3BoTYCi7lARuiOyrdNEYwF2uappQyCL37PhcLUflnTiW6F/5WZITOHg2bYCl24ExaGrUQ4fDCj35yn/ROA5QXhusjY9POnm7zLj6y/fodpv2ljmVit9puLuCuj0lN9EnBY56dGowzjFT/MY9gL/ujx1aHanU2lD3AKp6C1q16bR7PQ7zmaDACzBofc8wan2hB1yTH1a8nDv+WqjQoSLOJywCb7fad7a9prShsCJkazyceNezyVaZlgS/pRosmX6MaLxBiPBBv8ApT5zQ4M2B5C1ANgApqu5YUwz2+YpdFLP6FEQesfHG/XCEsH1HIEES37fVdEJBko2pxeXk3K+iTYIBdeQJ3Y8FPSvY6jPb6P1vdVtf68tc+WuFH5O5SuzkEyDzTlgB//2FB7lNyYKVYK2LAlV8YHU9WYRJnD/SoCz+B7687EByLoKoRgYjrk2qiCXOjFpf763coc/AdE22r+t0RVo+K2Tve62K7rXpEC/VEcdwks7S1IuJsKEgXy9kNm4zvD/GfF7u3unHXgnWiTJaLMt4+ePP2P5CB7jl2+YfK3u9T8fT34kmM25MAq5A0wxO2yUArM6l7Ru/ccRdrGEhFW08fquKDo53IvchlCLSia5ZyBNul5e2o6m+h+nHZwmoGMaPWeHGXfdvC51OajrNEfnD4eygWWra4O5mbjpffjkXz0PPk56Qc7wNLqxgaBb6e4+cyxrywm8cuW5xp8uj44ND1wrlvR804g23vTzZy05z3hDHIudJTRnzMOUpUWNioTtrC5p8SasV3/oGbhLfFSddaBXAQNigWT63B5Z66c90H3HEpACPnknDZfccSGi1dZdzuNsh88vSKeUT11an7SUKwMLRojCXk07+qcXnRp8F7esdz+FizHPa69ePLnStgSPL/Ak+C3aNswdmYDRhkkjO+meh+li8/Fq5qNCghNXuWTmc7uwZYxHn1OGVBS67srcpRp7U0sitzNynZwRZ/RWkSKGHos8NME38YhDi9PvbN5cAJCVk1s5gzAN298vkPX026UrKvGQ4cy1paOA441zbyYFA+n8UWoVjiJZmtuZFEyvNi0icOYTStx8m2Hh2tJNw6CdSGJR7MX3RQFd3UuosTHwOmni21UTSBLZBxQuDe9a75UUQJBgEwX6do9knlyq7BSxh7ThC+Uh6FYgCVIuxeWg3NFpjMmzyD4IBG3BD7RWo6YMqemGKpWygobww2x+JAgPl+UteSyiJ/8ZcAL6c2MaBeYYNBGJDh6A7nf9YBCOBvNEfBjYMIhv6A3rjHA5nFG56/1thGCCDZwjhNZdI3sYzvkMrUv1cwc53s0dG7Aqf0VRdtu8eio/ndajUxnaBABgKRpGnIRbUT6Jts4k1/nUl32f9Kn1papswJrud9BncNcCl84titljH0r+b8rq/3s7NQzQqf1HhxVZXFWWWq1whSdhsRPGUS5bWrkboImyRzmwteQqMf7odYCjRrZ49jEHyZgsqz/DgrFnBQcT14T3AVxvYNeQQXc3CqSpcWv4wn0XW2tumaga6xhnSPTOgDUjZOCtdOxF3+kDz10NNSWLglEoUqEasT9qBc1/ni53r2ifqL+wIQwhhraReep5LJBisoPQUBmQXX6/u9YTP2sYsQ9JMXqnjDL1NHeY44KdSgUuqE8t81hKqYNq8m8N9mAt8JuH+MopUXE2KH9/eMjN6+DTGWDUbhW6z6GTA0GBWRvX+SgxUaG4OoNV6AbqqmHeMV5fIGp/loq553a0wrJpDy3lr2wJmux0/s+6sJkWbqdA8zL2AnBvqKI0BRKbQ1Qh2yfE6nSc0u62NK0GZehccpAzLpYTU7KSTsswGRrCBnDr4GFZnVYGPflSrf9VV4ZC4KSUjDRRl5fIe5bnVTxWWN7dHHKWWL6iPWLrzk/jcSk+iWXWJZEh76GnYq4Zxv6s7RhGkVhud8enVg3eJshJBUF7isA5/r+g0aXqgtlVcFSi/ju2tS6MG4sNm53kxn8uxTCZmrYo52f5S3tibRwwwWitF5M7FIDfo0O+/ELPHEN4t/0uW2o/CsSmcX2j/jlMkgPgXhrxGt36rdWTyTqzDNXhOspbLm4T+RkzCS43UfzNuowqhniGfgVird7ytVE5lTL8BbMtNBF7CEZxKmUGsMdU+pAva6B08rXne1cuby2Y/WekhxRkaveDOGLj2MijQRCBr0xo0Gw2hk8G+dpFzGkNj7GAVnC7XsmrzXMWOcR0bIYFUtHgBO030JLPpXp7vMox5tg/pRMeXIUwA4p8itF+DrB6K5w/2adIhqpXvrgCMeFuigN26xOgAlXY6VsgiVlPAIGpShtE22nuulyVnSxHfAVKU4RdgGkLFq6wiGx7TQ8M7EvTs1LHVW0wwxvwSwo1/tJ/AVlqDW625EZOy2YlfjCCkbF7U9a+4PGE7DEgnqi0B1HoZ3BzHbkdlLmYGxRMGIP1qYVkDDO+xVF9lmrOOQchNZFHfTekawKClCy8lrKaVYEJTTUwx3hX3XGDF8NsEpaC70BTWV8E4D1SH48hIf6UrS0lZq+qfYujTzJFmDAYud18/OyMfqbd1etvvKCG2e1guc+r8qy9s1Zdbq6byvJb8uFyze/WE94nJurZ6lqtKnTTHJKi9WGC8SQ+akpqIdOXBmT36itm8CjYTtdrN/WbFPW2u5v0nXl/6gpHTmScwNDdlkQdoaCxR8+TCZ6nd22nTgOxghdTZiOVIAcJxlbqz9jvuAcliotqEf9lqrw3uMheP2+BTXWF8aP41pxwDLtWde3vHaZ0yqJ0pS3JoUxuWvDiDG+V2+5X+keRTn09PHzb07v/t5Spy0Y113yNGQRSWYORankcijZsgJF+/v1VtJPJlmuwhicNPLrmqWHq+1lLQeIeBbM+h1xd/XdpQ5RBhCCg7x+L9PjWugKJGDbNBhrMKAbAHewPQ6TEnio1Or1r0fKSHbf2ZjOFMuWFN2pygqydfbkVo/yQlh3sK6bEJDFCrOnZyaUiycnumLW8HVFt3CtCZ3Ry3Gf9yiideUTtsZcCFwuMRd/X0O31AyG4mjE7MxSESFPo7O+KXWTOcZHMcoPANLNzFCztOP9HZ/kZ32MYJ+goiS/Qh8qz0K2Mz+BA9a2IoZ7CrUevbzyzPqswv3bY7a3l1x2w0GMMjABhbbudPElGdvrlYvALZOszV/kJFKC9QB1798qr47RyQoarC3r2jBV+TYZITxOPnohJNr/rO2L2CLak63eP/ks6ya6wK15+NJWFf883A06c2kSlvgQhb+lgfPdn8rYVw9pJpgy+6D4U+XmBCo4oj7uPyeVINRySBD22k6mtzr9cY8gI+4u8zUN2eMsYO9RIixqVpXoZopUQTw3gHQ+ZxfxDLTCOJW/XsOaxB0LkOLDzlyl7zLRmjhrtNhvA8K/20gAknEG6h/Qpz1oMNH/eXxKuWvnJgiMI9qU9rJdKYGsuFoSu/pi/DffoM6WwGwWFCCe11egmOQ7j0iUpi5KF80qh6Yyr/ZbvHs1kNgm9QRLf3p3VM3rlzLiaVAbOVspmpuqUsLIogYjcjU1QzVl3B33vkP2nZ0VRyswwnUrLHsAuA/P+AwnjP4govaf+ktM+8oChVHbeJSxFqoZ9Cst24aLb2kQAMLFhWTkp15iVouELYzmpPB/FqTYR6ByL6TnBCfVyClJzfyJc4Y2ec/2vWAIt3gTAtD1oizKNfZTHKZsuPAMvIMauhhJ9wc9oqApi9xJczan+E+vpDjmVJ7UBtYFb3WjrAZnZEUkHw7ACcsC088vpDLa0xU2Vhhq3b87f29MmCRPHjpHgm+0uMNPHTVD3jUaVhj5Aq/eRLSKWmEk5COFm0xvwTieIkTq25qF8Mwy02kjzn1YLOSpnVGzM4e90k8Dzb5nzrGAZmRumA5x5xDTKy9yZY6liN/oabmFUW+qda1NY3uqYVUsefdFKC5Jx8rxdUM7E39yHRdpTacLZD4uzicr27Jd0V4zxUqjgYpLJjgbssV42xEqjvfVCPCxbTEWye8p6yXV7Dn/Ae42qllxyIlpVpwQ6IxR+2BKN6xIAkxU97HMWEqj2tP+vyy4SK1svbEIazEUmsAi+WQ8HXpRp33lkfEfDpcEtFxWBhuGCHYqoI+hqqPv1aiJvW9X+49GEXjcxmFGF/0C3al4+iAyoxSLe93YB3MBBTvgLrkXJJpViLPV6s1C9eakhAbIFL/DyIIWzwNvYSxtbomebtlki3mGDNZg0p5NaAPXlAwbHt6dYzweq2TYcojSlLORokkbVGhTLc2JOZ5uyoHJ0Kyrx4JDUbtlCfgEP796stEpGr6wgztfRE1gHyVvp42AvNU6Clz512iijfNE5Yj15+zZkkB7OO/8wV1tp21PaTS0HJGoFVKnK1Qh4CxSB/sQB6IMZ/kQm2Os5g9cbUMaNzW0fS0bSzK74w/1UKaC8uE0C86Nud9VZbum+Z3YRu6tvxtxyHKJAu3790FyM2nz1fiG9ftLDZT2tBXadkXa4acvQH7CbBpDKbePEDE2xG+Qq+qgfuPDa1vwFLx7R1Tf3vnZvvnBcogp4VylcjB/Teu09aDtnZzaXVSE6Q5td4a/H3/0sdtw2H0Oh2DskAmQqLD+c91EdJYlNGkNqwqlkMcnK4MgXSpGxS+RilpHuzYQpgDeBP3uWyAMbrBeSS7gq39QB6nPRTiWscyl2OcktLn1q90mUOcgVpx39nBceIxwVM8ALFrhOm0rU2FcVftWb3cV6AVE2g9RDcxWBVjSl7nJacakecX7yg5umcrrfZtWBYUZ36g/qQX9PQ+fYDdp1yh19xEYCd+pImiO7gZ8xAQ5ZfNqJZS9dd1p00/gKGumKnoz3pTxcplQXjq8x+r1+pZ+8/UJ9Y75W+nrMVlF2s8kGOORebZTuWWMRWs3H675k7RRMtfrD2tkuY/Nb2lg+jLB4RNJhevA1hdrH5jvjeWn1zLJWrO48qFocC8kYI+C0ZhohYlKsG4/si30QVXawFFnwEg0iGXN8j2XtCPN8dRUomJx9C/tHqWmTEDYO1wrCkNGJ0osa0drl8WfzT+iDP/5tfGETDkScbnQjPTjftr9PSN05y/3vYet9gPzfiO6cKAn4HBN5wmnh/lej/fGJhLK02TnAw23MzIdxcayS4lrz8I0scNqOB9pq7/0gdN5xO8ppGNZ3UmyqZEZp7Gb9aqCfA0M2k0NY/t1uz//w8xS0hwn6iHc3orcQH5bVAF3JvoZx7NIAXqQ/dBDn0M8nfJao+rQbMwOQhkORN9aOJTihKWdvtNPUXLFE/AEMI/CXLn9RipdkqmqBKYt2NbPzRTB3mECjaxbsvX2QpaaJ5+0dIPWGRKQL3K1vOl1RzNOL7+r7Dmrq7CJOVURNPS59UTR8yoVuBIo3VgDa1sZqLwrPfCKiXxMDx6+gS4EKN2N8uRBjjiGFhDjYVkEBnqCzcdb8Tdd65+MVoIVLUU5erKXqZJpj5CfnHi5irxmhRFbRUD54Vc6s7PfsZppH3HEOPf1VxhtfXkG+/SzzGolJYfSVIxCCvrP1gnAN9IaU7a6nRSGHdQ5upQZTVSPZIBUy0drXC+FzyN+Kh18AYv/Uqunaru/JrCHIrkkJYoly/7aB35RLrs4ITUQjDJ1Q7C1I5XZayRoWDgam9ozBDJ2BtXZ1ynmE7mBfGYveRBvEx4R0g1SUf8I+BK9FKji+gK20MvWSEKoazrkJBjhmHDhLqF31CnjBW7+kTSQnl+6CyBR3aAPhc20PdEVLwwnRt5MqZLUYmG1WUuLMpSCmkwKYVS91HFHOdHD80HG0tE3ajqr8EZRq3qOd6PD2fPXRkOAIxromqpSQhA5NZLsz+3MkLhKaCVKQHkFqwEcI6d1bux1u62Mjj/QHMqQ51CQH0h3aKnUWaePp9mtvXy5+JaVe5nu8pvoCh8Ks+R1kYDFTRMUUhVfRgoy76RurH8WHTBHBWejL/AKM8dvUTRAxYWbukoytRK6GN0z+iYk0YzzXZnp148zYAcmwk8Z0Ep3kDvSxVwFZeJLvUMvxU5+DTciulFWNsLL9pLV6YC+ckoIB9KUaE8TCtrh7vZJgb/AwIGnk47KYuJfUw/P1sdsGvSBmUnx5gEQC/3UiAxlm47gjA2QJ78Rfrw+VlUN17ALyXCRlTV5xGKDIj1i4lqG1InspfzaEKYjqpg9p2wAPGrQB2p9tiWuIGJ8BsoxMpueu/TRhzkutb7C9WsLKvmBsV76s+79gqDQ1rbKZ534YxF/+uPuhouhz2ot+vjVa9rIRwrEKzbh4tqyFFIzyDwv2IyReOUhYbYHxYcshZofVhH6KHHrdGiwAtRKAs63RozkElE5XmzlNo9G8lgUytUx8m6eEofBfSSpTYTD8kHgnlZHBcWHKQh6BFUYFaFMsV9RcdNrwkp1peylvuGbYEmHXrIX0R7oTgLQvSV2h58ALC9XtB4CH7hbRiYWl2KiUNYObXbitqWcODyx3/XoDyRiWnfmbdeb+3Q4twMBtvq4aIcJRcir0DLGDao8ucNSfFluuCAGezM99tDsizRUGhwa4i09mCoeQVxI0fPc02Da+y8stI8NszxeUmFiJdI8m+SkR5cn5kk4VISG+ek+tS56a3evEubWKqlQR1TXqN6lybpSXbYDg1DFj/oal2seLffE1fS9sVGS2LQQ1rONgxpIT5WNcqe5T1N/2dyeB4iJoblLn+9nCVgQJ7/L3Is0EQxOiV4hWvG37rbQUKLFh9OeTeXNzsRS27AW9iXwaPCaHQXOyyIkcicGoZ5wJQq3wbZZvYX25B8mAgMQ8CcBvuscb7xZ7cx3IaeslcRBW78ZV7QrG5t3QeSFQ53XEYi4lmvvCDeszFtsIsd9c5aP/M/G0BmGHwgSFkIVopSg5qYWGsUWdXOFi4MQ5NGGI+23fZsl3UNxtOq/Ln5HM50T9ZRouL3Hj+UBoBwXbF3wfDhcv1ogVjKTQYWQV9s9ME8Xrju+asHyb3dy+tIPECWHrS6Q82R0UMKJazke7ew3C5VH2v6qf5iGAq9gVp8whnuD6OjW0jaawAwODTHd2NedG1s46pXQS1abXWLfn7otJXEQdo6GosNTwUi2pMVhEZ0FYAY7P5tc6mgUtzj+YCtV7+3iY2/YW4GGuhwcD5GvtykXmlO7dq/LQ4Cd/3CFOo/BR3NIVm+rTrYRxJrCd2yiTc0ZrkEY2hy5SJRqhvfruUy1QvFi4ooKsClr2hl+xxmrDHvpWZScTeJ5NYvMNlLGwjxIXdGEpe1EMBQF/hMjNdicuaoaxHWKGJvkbIHwgXHFq7UjSajV+C/TuoHGFdwitog/JJj6WpONcZq5UWWwYAmzBqKvYeOPdAgvV+QpWIMy79Ji1gHmGztfAFnOQ3KstDeXwim+ytdv9sDaJfinJQXCJNDrK2lrdQ4IE9vGfkdl0UTCcJHLzhxcFQ8l/ifreN+hnPm5d+/bgeuP/XcC6QoOz3WcBFe2HzhonOJME4vfTM8U1oG3vBcu9nimJG7ZTqcStEwKvzXXwsFyGprZ+6CmrGQx9fssyoeCwPlw5GYMVt+CwK/eGYqoPAYQL1PW+g0+Ou+2PNetdZ1HhlJzx6unQpiPiRLBcCJt3PaCLgTh66J6CUyo7CzkZHlXAjzCccyOmtgcZeaq6O+RKSbhCdeSSAu1p9dPky8XMXmZbkRWNzjEUqb2gdNi/6zbQil5lYg2eXpN5TMpTrsfb9wWvUjtz1jlFjA5nDrbHJEaTFhrsK9PVVIsA0KmbTD59MdNu262BsQPWYYmFD3Kt1ipK6+27cAPNBZ2ppwrJ4wqSzB3HdNk/qx2JbYlzeurcUSaJigewNE5X+6ELFvCzRdfS2dAFf2Ro2lLNHZABUjypyRSYt2yBLn9YjLFCx1/wVKNhZA0+Uudw8K8u0Y6rr42j8p1PtdyNAjol6YPtxVuZ3KxDw5GN0jBywrGTbGgek00kIyaHJoAulqEGLmI5vmRxi7xjycRS4NOzeYNoYYZl/p09Bzn8JEC1AH4g/xVzuY1rrT1FojoCGHZE64asMfxq4hAXLuhCroc/LmLvONvQ6je3JAtAn2tDbV1KvI5ku8U7aAAW95xTmkzAStWC35XogjMd21OCRLhSYF4Ip7kTMQRsPV6JD+IM3OVy+Y5msWbrAKx/JGQUbveyuvUj4+Mf8UptDmovQtZw6T0J3DXG8KKSq9DX3YZGhthcRavHXFN1QUYLCgyCNXbW1rx1CQSMX39+9xiWFzAN7lgCx66HzglP6nzUaK8kR4SQaOQ30bZJVK86ApgfDaupcGdAfWy79NxJEjP70IAx/hniqE2bfuULqW3G/xclKm1fnGm9hgeGAu06UA+oz0mbBVlWhAIZMSv45p7UekxuZ6urQmE/+WJLSq+yOj8jL3rvHKWDZRYikxA0M7lrtQTp07KTEBhFijn8O7UHruLs8j+q50g4QfwE75lPyQSA42wLscX0w0AMoSgfZXY6tq251dTHczeMQtgOUqJ7HVBkrQNSjtQP4WPtM5Mzs3GGR6CUA2h5j4W/+eN0dnxwu9QonT0NqmP2fsC3lR4qYk+Mu+M8yN4bzDHaplFe6xhGnGmb+4N+OmuWxJPOQsSx4BLPnGS71jsncP2Qj0BcArzg4A7kn/C1veNipHLZd3BogY+APEtKvJpXrLjn9okZBIZ1m0P1y1mIxWA6fIo+n8XKy+FQrEqhZzUUrWQ9/GNO7+9ZIwMAuedlCG0/AV6g6wajLh9T8Tf/CvbiOscEXEPDB5HZXhqjzN9KDwsvZe3c6MQiUEDKbtB5iDrQVjoWGyd69ckDAImvZyMtgy5XKH7Nkpy2UzucA0Th3n+N2oxgqJ7aCsfigBp0+/N5kRJjRfX8LYpHRh6lnVlCPMOkXgpfxvEuxWf1qN6mBBT9aWrUZcGCH3BLVFEer3I66V7ZwsV9rVM1gErIsB6SLrhxyrSd44rOTtZyPn5AhB1ZuUElW76yWsTSUG3S46uyLXBz7lPywkc98KAHBbjByGkZ5+w/rOjU5bg90HmoUzvCHOU771hkz89BrVAAhmdvmvIfr52PlsKAqlcE7EynpcQEQy0+A08MlxmmpxlmUSHDFrtSAKC1/877C2gxYu/cWxxhzcKb2RQo+UjExKN4VugcXJ5dyjwhwTqobgZXIc+9VtyBJOamLjuJRF9cN1DTPp1tSATwfHTN7AFtCRT08Q7gi6GPzc8j2Myoj02f0696UhkUkaNQ7j+m+H4zk14/iOxgUHsHqNO8UpJXXMnaBuIZBLpKIx5VXh/x4fpOkG/4nXcQVZM8mBTiljrbOgpFt64gB6TG93dncn4jJ1I+YijRVd6Ef0YTn0puf5Piahvh2A4nuaSHzUECPIuDO3OCwWsHJlLMQWFVixHEWle/pLDIQ2fbEOyaOXWpBfBA7hPvJJ9a7c9OQUfLkT4vI31EcEw0nX1j4IFTbOTO4TuMM/gTUhaAq8VnFEiUb+l4xH24H81ui4/AQy93BDG2PAOZcmJbeSDpXM7Q4G3DLROtBVyJjJbH3aDKJBFKqgkuZ36M4qXvh3UvFqRKTo/Ue4cOjTQYIGC/0gIir4jJh45TOH6SVC16n03c7Ti2/cCtg7UbxphQGCQNCGNU51QygXoEMr/zd2KLcCXcvLBIvIY6ZWU+qba2zIKw8Sfn2LKxYop8pA8KLkbsY3xGRagDDXGcou2cw4jhWR10leYENaFYmkCHys3bYSEdiEdxiGL4TnQrji16DnxdsscEQjtluxClQxRizMJW4NEMVLNWidk5A1aqbwki0o4GM5doKk3lafPyShH7eM5dnsiNC+KuTwkMiPGvOJ5jx34IcuKyZQUBL3F+MpzztJXjgKAbjyv5Qq2Uhk9QydxWa09UjTadAtFsSaAUWcRmJ8faWUvq/TqcZ4Lii5OPSqacai6tJaFIPpsQtw/tG9371kpAdY145n63MA12OxsoMcKCGFguVozNrCXKwbSf/lpKij7askItHwENtfOBk5YiYyztSLMscwPFpYFArKPxw6cKITt5Ll+BdrLgZFjY07/vo8BLZvjmGRRyD/lqizclVOu0s9Eqvy9OCjHXEna98V36nb7a5oN50HZicqYgqYz4exh3tmWVTJj4tarAl9zjY2xT0QhM4ubIS+9Jn3rTDRpj2NQD5pctI/fd390tbauuTqdslgqDB1lk9bP5sl/eNR9BYYjWL/Vnsf26+EtWV+QnR0JYeb7fY7A6aFzeOBZfeFzvpKQmrk19yU2wZOBuRB8fsu3nBRmkEEFU5/VZc9W7Fc8bsylGw9reTBKu+CaVskJMdqaFdTrk7hwYF6WFmYUwPp517ElCU1ICKhhBgt52vBXLr2EdvfOFYGHHy2Iveiow/BWiEnk3rZm004HJUEqPj4UFLfT3SA0IyMzWDq23+I24wnjIRuVuuHWSzkQRRLxA2zWnLo+Cp+lIjMKxqaPaMqhF1alMqH4S6DQRW0JaSrobFQCHXwbNR3jBcLOAB8r9k6VY8BedGRmjObkvPcWMJIFPx+zpIwiFlp0cU21hufERhXQ+/ohMlB0BY1QwB51iOW1XuPzquQ5rkvGQcFk+9LtyAA5+CcB8a/e2MrAUuBfshoowLneEXVIRn1pLnvq0VOAh19FsGxS1Fr9pxjcQu2D27fMV6ULHBEprp7VFSBb27vVYGtgMqYJeZBxWE8LTKWCPpv2nPi5yRdDqBypBl5RhiCXMELPVh7Sq85n2RwMYx70Bx9+U9n7QPiVgg4KhfoItDkG7f7wXjXiWjm+wcTgSQmvlXNTigwmiiUrtTUHhm+IGiqbygGD3o41C+uWpU0DsfjnU+Ktcx6gCW/JRWVUQcJVIhQ1OlPKcXmJq3NZ4rtqsniA807K3MWh/2RPsGrNmOgsJuAqgBPdmeSbUkbnGBOCsdvO2z6q+GXzjv1u9gzqABRlUkG0KOHpg4PdQ+PJdhL3cWywO0Xwe1DgoP2tyEZzwzWoelARRza5asVM7g/VJqZ8I+yuHikkwXjCS3LIzJZveK90KWynMNLV6Zjn6dQPby4H3nX8lNjZTrk73ONBUwukdlqE1qMICRwKgIBXxCp6zQDldsDqKurYR5NB7NZzZLq9tb9Nk1jsPOr0HYm7XYA+wtImBwkdmM78NrEQ15dtZfA6f+Ju3GZfx74bt4cc/6XVcMAOcrEiPiSTEbaA7wVZgkyp3tCD/Q6vySFsyuqbsMxXxaqFfiwTl719fgdDc5MiHrP8BQvecRDX3roWWUdtR/9f8lb3Lml6oyPE94katkhyzkw+girG03Qj7/gvLY/o6rBSqUefRwOw8YZXcuSmwEOwEl8ZSW/q22elTLD3Lu1fD9nh4ErWdp8w2c7sN6WLqJxQH8LgyzvOpaj38b1h7hUQr/MP2J351Xrwbnvur8sWwBXHWHV3Ajk2Slb94sD/7Spjf2YO4RT5+qlpVReHlySYE4N1ljUIccp5IotYwY2mPYp1N9qAr9FYOa6R0iL+LSLH5AqlUTK7N8jjcPJgs8gAPYT1MJwOV6Vq76VbohBNcMW3SC8AEmIVdZzD4A5hjJsE8ykZf4j/7NjuocxARIvrFtPP0olAfKtZLM424QQfRO8ghQ3hBiqHjjBytNVvj5uBYVeADj+PDNv0LYoZtaU0ZV9RADQj7RbR1ngNzZItPcmrDs8OmxcCMuo6tXyOshnmLPv1IU+yLxiSE7HPg6LaQ0XXlB5fDWOkBmmxoK69CPk7HiJeYDuWgBQm+ma3CXPEIBg0vJPx9veeUINLagvfveclpAsv1RuVWnRkDf4Di53PNS+GuFHVR6LYKXr8KovBh1oj5YA1/XegutE6pQ7qwbS9sSvHg1lO2O3wE7yjmLBn30RVW4W4vkOzPxZOZd1wGteQq0uUL2QyU0k0h6JXgjJ+2L6U6GhK0ocI5zhx/98mO7FnNBqnZqEWjG2JOLcnWckkRGmaivCELS3xTdlHk8S+wNWXghP5zsDXbu7eOlaLhESQO0HO6FYzoqzBxTk2N1LnQUfVQhE5WWz8j2pAKS5u4cxOLdtUaO+SqjHWzgJvzXqI2bwbUQhMpIbo2kqvFJyrwEVSE4xaRWy/v20xbK2rvbRkUujq4P1SUDLM71/KY+MeO8XgHCvtsoed+uoMNtasA+Jz1zaov/wt/sH7CviCIoZ9ecYqtrZPnzTGXydZt7hU1UMml6wiiiR7BkpVoJ9bcWC9c/mpEdXyizijOIZ5mDR0sIuXen6QQLtLFqERMSEcEiQPs1vbin0lpR812haZXZYRlhuI2PLWq9xQJwXKhENDlF4M2PtO3XVbzNWC1Yy7/XpRr7xxtJwpjhx8q0xUlomjoNs3utS7uzUaxLmAFa5802FLa81JjWlXqeJ00pdfyf1alhwz3/IMl+fcYKbj1WrAtKUsHx0Yt8vYlQlJBZYu8GyNH289k6iio7iU35DqlFSQK2OXl8QiH4TJUwxV6fW1FuSq7w+r5ufpfBOJZxRtioo1Y3cF1/3lxb/8FZm05SWERTHa3yYO2nkVhhxYyXAwuQNSt29skdfGeawtKoBVWjotcz9MGcnnhyd4F6hJ65vJkMUZT4yp8o/c6c82tise/ctonPEAI/c/RDi1gI5483drQI66tHQBlZydyDEd5h6u/gs7H914ZIWliWkbrVxcgomLkxPFouiPPAV8iAJgFEW4dnkE6+3ng5hYre1zUmX7QVV+WpmvDEj0o9/wa/VvNdokMuwvRE2O6ktskfGMyUn8g49/zFvetbfD+/Nt6UwGjh4OHhGS9cKGZAg99ARU6mNfo+J4lT2lgzwDRpgd+aIg1YntVKwLQAyRk5hROQ4koTyUfaO/l+0M8BXRVHvQbXMpsTULMVf/1RFhCTIWOSlm4rFeJBwxx/9Qloa/hjS8GjOUM6VB1BSGxwXOkux1QxmEYF8zNnUbL8rWzAaklJE4ulPEz9qp/ramJOKeLZBOwhm610T67WuRnF4BYaq7TP4F+XwlqkEuiryV7naZF0tqX+VYtmn/JHxLMNjgOQVkIq0wc/YwNwXTx3hz4diRfAk9SUT0rsTWqnWBGGt2BAKAkRlhLhUOIkLBMHfOhp9ZX3vXW7cBbzNEkNL3j9yzZUwo7pKYqR7v1S8KIte12bt9c4qxtsORLtdQHFtT0e4dUSNJz/nZa1AdrHY78VQIsiZSRSXzj/a4rdp0tTkJfwpJYTg2wB83aCzlwqqqOwDixv8IizQTeGbzaRLZJo0T10RoTna3tBxHSCTy3KAOWbNS8XpY1ooURpF/CgHRZeBzSkQJGZpueqwxBGqihExJzlrIbw0Bi+EFD5FJwGT6WEGUNJ7/5KZOaFGK/N57DvGCA+cUQM9TuHVVD9pp+jrse54mmPwQZjH71kMHpd6759b6TrFEmwyccu7dccaiME/Cfe0rMqk07XmUIpD3rvAj5kpuAQtCMHeT6GFrnQyP3lvbEb5PE7v3FU+l0UKzmA2HROReRnutpIlkWiYKC07b9JY+dl/gS0qPydXgI5QIzOWKH2936HASv+0k9LUznQ4+sXhx0yMDuyoiCM7OwWfno6AgQsUJiZjKqK6Vs5ep+Y/QdWleh1Ew4D3Z7KBMKp0Qw10VA51wgUTK9gx4tV06fYZzPoDe/C+6fgkIJMlEUQxehXcOaFXgE8qZfSbVulM0kFd22x+fP47/oJmZOn3dQDhj93kmzuxgich242e7bQgJ/A4TCuvxVtd9n+eBikIqRb7DFQurL6snrnsX2mtuPSwv2H+xsG3PDY5o3SIJZ65XVTYxljTCu4/t7XqF43LIgi2+RJjH/cIRsua6fnWUeTBSLCylHvHW01ULxDLLgl0rBVeceb8ecg2xOSqBED/AlzO20XwuoD4nP8XPxnBGD7H64Pxf9TW1PMYqURl1fLzximcLNw3ljrCrQquAlARWil357pEEMeeamhtIrT9bRPUy8i0qfUephJt4adfqGII5uG+1WHE15tZSynOKgdT4/c2mXHDpyXm5XygsC1ertL0pBNQm9L8pwJnuua7yt9b5/V8dAQ67ghsI4Xxp4F10oXG+/IVFDhp/QEELQt9yYyEg2XhzCpF9A74Phgd6XlfxDGwTdHtHfLdlhlgGl3sBZmL0TE1gLWLlZdzdF4eT37LE206LFh0jzKOqrosGluus38U7Ppd8XIdhFbYMMUQfvxTik+n7g7ohwOHgQkgD9LGpU37kTT+80OdaZW3hLkwfmt5GT+P6czgiWysrlBJejHdZsqMF+JH4jdKrwrwF4Rwi+WrfwRP4nYJh788VT1CfZa6e6CTg96aKCnqojFS9GiY4Fp9FlFa3dqbRA7e/eESOP4prPzgxUXfuQ72UUyPzuqyY7PB9erHtbY4aHDOH2Xb7XM/hXsbW6Qz5XbwV6mJlrTV/pr0z1T7dcAwIuLnUklDjKZOsmBZPU7O3wvkMB6f4Gk/VrImllYFXG340YjfAmA2l6qctdW6//0M9OjJaU21m8aGLkynmsJciGLj5yXyXtB+riEu/nbCjbPrKAgF5bA1DuWRpmPgfgJQGaXfIQGXf6zFj6kh2Vxen5hZp9euDKh9TbnqmrdZ5IStjNqsa+4en6mOtzFp9Jo/85WKAikvvlLS9g2JMir7aCLGroO/51G0vFHE5KWc5THJTKnrBkUYQiikagH0CBJI1SD7PrU4p8/1b8wF+LUNcUMCek2GBAgfm6AoyBWLTNOH57i4uOyhl7QVKHouyMnoP3U9wOLCoa0MDYZETOsUa8YPESCVcw0kiWTuk3LGKGixqlrwRr0+h6sL2YSrDfHHP1lGkwkLJextGNGBLf1MSE52zpVvyO9AREuzLWBKbJqfykdXrLx0NjoFHuMJajWlAX2D7RaNNMVHellmtRHkwwxwjjy5UJwBjSlpi02zSB0/w4OGmcw4pgHDUden2dqDlYz6GHiNtTOEuTleUClS2PEnLz4V13d6MYuFgYmQ3L2EXgmzpnUw6ouIvJBkOgeDILrgrx6hOAfx0vp8O64n31j7D2Zf9lpt0AEb7N0m23zeSridgENWDFLm9ubl+JKBD2gia2LcGvp8d+lntniwHn5aebT1QlI4to8utnbk21REjXLet/lTe3J/OuJig4KFqy1O5Id7KZ5AB7/Oi9rRCByw/TmZZ1UW0dUmc8Zs/3SVBea6J12WsWUj+vs9k6/ctxZBJq+7ity/uwMaUGhZ4aEHsBxiEjf1TUVBebhh6NF1WW5tbw5CGbIA8oOr2h1ZMj7QFxa7bP6OLIfNaCK8hb97pQ9KaZTmH2ffH0PqOzElOArEmvaQfBM0XFMBrJH+UW2mJr/qUAEmicvZdRurHDTJNF9M5ekXzb9P9LNJz2BoupPz5phrSw3ODBOh9qiMnaoLFVTAis1YdCmK1hXAF30jFi/ilI6j2k5LCDX9AHggk0cJuDoYBsCkEh1vbgSq7RatMARoCUgBk8orlhA/mWDtdH+Doc8Yz9ggZqDIMV1AXm1B67rX33HsEnyYbKxjsdZ4M3eqORk2cCrNUpvK/J4aFMwkE37V90Uw/wvF2PgKfhvCu0h+gxE1GPChE4m4VX+JN9ZHUF8LUIqdiV1gdfDwu2FgJp3FMEE3/wd5m3wqtGvIrpdhE7V36LaBoDTItAdzRf67x+jRUF/OahVS7IHzH/ykKAayhqwixJMBXk1NnRjuh7GNEjDSkL9kyHGyqrXnHJFYw2zaqExTy5msjZmOfzqMRybW5kNrD8SXu2hy0ihSqCXhSuJwSko5/WDzOlgBBIANB7GT9KFhcIz8BJZeIPLRYYTpXmF7sx5aHRd6bak0ZkQaL5Wp50RcGHFpr/mK2lIh2WEgq5VO8hcdCiiAXIj0wpZ9KO7G7ktj++v/0/w8zHe+ajBiDpEWWTQRElTvVOv+BTGjWKx2gblKEZJRZgLHchwpRAz5ljcSNxzEldAHddEgZ1wL0XLMNAuuKGIkjUMuNOM/FhpNxw2e9ZFjIX6gJA1mnQQ15QkogIxBfu4i8Z+KVlbiNI+IyxtOH1Jei39Iti31y+DhlLtfeZxbgZJSArrLPjIHHCJXvuNHzpuQD/xUK8KoD+q6i7vyBZZ+mjqGqYy0BjB+kgLRwW1zVByKpHlGU8t7l2lAQLYFfZwm7a8K064X1tgtQ5EKBOhjfcck3f503c0l4hHymMCVS2eGXBI8yToxUzxLSXQIv9vGN6yqI/iINvtOvKDxwrk7sh4iVOgc4IQ7tHOFzDmj/mXh5wYEOHuy5wq8X2MyuOCYQzJti1IRnoP2pnshl9Ej/OR9Xz7lfkqADRhBu8m3X0fANNU5gnSzhItxG8qlQLpRiSQxf8QEmhjuVnlUtWdPoeNWQL3qzKFet+rhQ78BG3wJury74jcokzs8e79DPA9AZAU/Kl3rsOGS03jTpiukOquM6Tk8XFe5zUNhmEPAQ+0yrCVwaMieo4mkmMlZ/X5jTwHbFaofxxf3dDNQhD8dyXsYcKTcM/Cxopfrd5O/oSLwAsGXHcaIV5nFmIoMMvDDSACrbe7N7WL69Kruh64/9bfwAh7tmFRerHBCiv3j6Roq7YaBW9563KpqA/EVH0CuvG7mpgB71hBmzYSeR6s1CwiN9bLsW3xWCXCyOOMc3gN+BqJAY9qDUKTxAWFqi+Lvqdv/e/IjOhlvX9b1X2Y9hDT6vaNsIwL6yv2QsTKV71XcGrpY8w0E+fQAUEYo+oHj8q0dy7SHY5I8LrUotWiT4hZ/0GQNDaQBh4C7rYeYEVPMqAamVH14+iqIZMLUTDRbPI5jdpAAMxBXKlO4E94t7TA15Ab+8AO6PIE6m++alkr0kwrPBOC8xjHAWwwSB7m3Zj2GWgy7H1a3OMtDLCoBK9jNYHoUQR43bjKf/4I6ljED5QVCREA6dEB67AUSEaVi7Aps/TKdao4BdPpbKw+AX6ZcljKH+LaghB/Lsi/d2A3Oz3Qa8wE3dQvg9QKLDCS7D2NhMss3bKoe4KcEQJSaAfdXrrFS/p2dGO5sfbFQqDPqLWQmLDj+uP3Hbf2YUG5bmIhWSEiC8QZFl9Xaai0CtJbWzlhuJCWuna/6R8aXPhvAdQ+QKVW0jj2TsRYYe78CnSqr9QV1BvALvm3wpDo+cFpolMEaCrvliHSjiQwV2rF8EBLDo7BEGoxPDhggLXb9NvmVHVQFOljIU2l6aqS+NV4GGFqhqDtzSwnaxhP3hTFjTTlyYjlLNhu6QMfl5qs9Rto7NJylDdF5y2vStJ71p5PngGtpOtXakU5JB1eOfCmMQeY9iUbV74fJk94yKnGb/gFul8OOkKaY5053bNNGq/4oLZVzLM3HasIFmL13jaNaRbpMd1Kvin4FWrgvDjYY9iou0JzK4OJa3i6dAr6R1JhocxVDuPOF9i8Eg5Ru+FPmDYcvGQoFfBDGQFXefiqUv2LnpRaFFkAenLN8FekNTdefWgvAinYVH83oyMeq11gdBfXOLA8oaNZUd8Zo36A9av5hfJHrMB6e8YIIVdPLjMG0dxF4zE5zOn11xhWyHfg89F3Fes0gTssVHxURjpTOPTOdRlgQl+jU/6ZNVFNZIDpHKtq1Avid4a+eSbR9eDXm6MVqSOHqrlfFGazqcEr2fGNV4n2QXU3sa0FRIajtw+FTYFhDbx9DU5UmCeLgj810ZB1uslRqXa27XPTFJ3rQhWviLY8b8Vk1iH1ikrxegpRj9ski3gb/IA9zSKqZFzKmsamdJjsVVI6Hwm/zScmNDNpGHWx/1DbSoUrtm0kZgHZyh6rL24ztOJ5MD5E/rGIl8Ka1hcd6YdlOO3NKVyKrgAtZH/MnDgrv1yLacHyHNm3kexOvNJA6xnCHZCZ3AoYKjfT0Hg66+p8likq1hoFxMdIOOtJzQ6ea2YjIBycXeoHVmAR5CBnD/0MVkhLuGTBsfwtbN41dRjgOi3nXUks/90tK3acDFEC+yu3TN/nSEVEtJqOvrozU0N1Fzed/0JwE3NSUnT73TLUYAMLMgNYdFzgdgGg4pCllsWvVJLoNQbLzMn9YEGX63nMhxlcsJV5YBr1/kB9DMieoUfOL0pGU8saEE0CDTMPHrMC4Mm03tAWaRt9DIsQ1ci9TmaURCoRg7pk5QIQi3dElHK7YZaNC5S8wS8pXYtu5UC2WVm7aQ/revyllXTajjPAVuLEPJxZYHMKqJWZ5wuWBdocMWriKrIFjjHW2JwlBpwvrQ7KIfC0abFbciXbE3pX0v85PEQqu8bRmKWZM1i1YVBPGxwB7dTaZgFXoTkWXSmNVuN7PfJ5nBWURg/Lxinta2Uxja3iZPYvtz01Eo2sOrH54R6EoYwDMOM6Da7oHZ4Ces4asNtefeAHJaZ9SAblQdhQWKwVrxvTFZaZr9npvsyPfkzeXSVVlagEYkYVsA6hz2Q9JVWy1BfCwhD1Ki5Dykh/ldgzx5N/wqusGPat18rO7unmVZy4axLfS74faH0PVgaaGnkWd8k1pFpr2MCurI0GkIrf8l79IhBnpfNvFDrcRwzdaZLGnYSoW1xEv2Ks4r/ES0mSPF1G2Pn+SEQUeAlk/XKF4+dksV6p6vzPHOweRspEhZlLRdPoWXEQbsz1JxxK8HUnGu4m3Cgq1+QTSExwm9LRtfAtCd0DejBcDv/QX99+vEdnR8xfkH7wOVw0ibm9oZ0hZW+KzzZeipj57qqgRxtSMf5oXovq3B+L5ZATEGTWExvGTNVDMgL2qZ6blTJyxXhPVvaTPv1Uk5Grt02RVz+EyGPF+AWETCIInQWiLxKzbXfUBWInhXT+qQ61tlZYuG5EWdF5HIwSqiM6MIw+O4/t0joI1wScK1RR93QQU3wBfwHE3VJ0L8ghm/Hyo8uzQVNVsR+EjwJ1xD8t3Y3lyBPC1weLREyKf7rmCWxzAaVjhDbuHH/yy6WPshep9CzX8XcWJswZmAIKLXon/iNf5C6D3JmsIYx59uxHh9R0OvicgK8+Rd35bt5/fKE/E1Eu5YTG83G3bHHjO7igSkDTa4b/jZxA9/zmYbgNJoC/sNZzLnKttnxjF1oAWzdtMZDK6Pz23oWnn4FZOLxGaFsvAahVjbzIZYRA8t+RnzsrJ+6qDrHwKxTozBr6aaz1Ct+hDkI/t2omM+VTRrE6HXR/E5NuTeeR4THYnc1tt2CZ1aOo5mXuswmj+Gliu2gVuP7urjhYBoA8Uz7IGB+1LifDISgNuwDQMnI5T5gKW5o1XVXYloC9xwzJTL56vjOUqn3XazR0j/ZiL0FpnewH0tin/q52OMav1KUrV3Hbg7dwoJGIAJNyKcoqQk2SMQ8S5bKKKT5MEtK/kRBXV6Fzbv/XbK7x4bCyp6Ip7Q+C5npF/cOzhZ/M88dagWYcLBsjTynBS7Bjz7K3WM/jvoVx6hpd7GQYMaNHwenOWRxFXgAwxGPUi/kA8wbCRvGaoSWlLQfpmYU4D/InTIp2upIqgjF8HcgZvxi4RjdJGVnhdZnfAEJPG0h0M/8s+IaEHt51P/LXWgaSZyZOjmMXH74rBLBPBclJ3Vr1mzx7lmH584JkTt1XtgyDHMpXe9vuFxBxYROTUQX7a7nbHyhZSgnDg2NLBdpQSB64HdkxS/hxO8zTuFsBz8wZ+wLC67Og91trW6lCzlCODgtlHmoYDTQV+fvlb0iqJXAhQ6zwLH+g/SVEpX2/eCRzpfLrp9HUd73TrTvTu+/CG5+Ntd/jau+0aPwYc34p3BzCsQaRCk+CZBNeUEWcIx8e9N6mqsYzcIRMzn4qfYj+OmKhRuBLhso8Pbm4ytzed6ISjQ8YxcAhK6hf2AKtUt6yTN3ms3gdcls0aHdIQ1TrGlxNCJ4nVSWPW94lVlem0DvWCpBHmcZ2z6g8NJGOUna9KKsYueKB49acCv76p3iqSHk+ESCoxc4wF27Aa9nOPaVKCixAKX1T53eyjUCOAOAewYxw74fRD7ro8VnWvE5Csn/OC8BXDIQz0dciKi0mxZWvFmfJKZpN+5hWZwg58PtxpjRmUxpnWLiJvdUjB0QnsAH/fjOgy9//boi6mVqmUlfP29XcqkXaGyzeEHCQX9kO5XavhSX3VCaW1figRDyBkS1qAAgaVms0KBjk9Qz8GGYF/ADz6DkQLBWzhHTsB06dgp75MxfWgXlG77YwNR57b8Y1sxm6tfW2CTf/SvefsabHnSEcZK41nlVfVuGG7an1VxwXz2tcN7cSySp9PwtCz+mxI6y4hzG39mHNyn/hMHJDAswQOVsPS716UY2G/+rDvpodBBpJY/kuJxM/uw1wqSj9QO871e7q/P/BJgA/o39CbC1H2CVex8J832zJEw8pmvvtNFk6d/vP8RdfsIkUA411SwDVyRs/nopzoHsqt4uN5LqN8QT0yo9+1gs7HdQNwT+2Yb8B2wGJMXH26ctA5VYl7Kf0VdfeiwQUDeTMuK8JpjvimuGXJypSZKvQnwu4J5XbjSp3eN33o2PD49GZ+TSpcSjB1nVX9ppGSRFwoMugIxUHQwRGQXcRos4r/IvL3d7K23yohGRMlse0BKSUOf03kqyELdP6oyfdjfA/oUNyqUT8LR4aopVx0P0GnMilK5lJLXTR9fMcXyui5BQ3i9Tv3ERT94qphJ4tvZrM7wrt50Trts45WpVrTghU5bRoAa1EuIcz+83xkC6IkiVhZnqVwg0nP+ywcJcrrfvBeUWi1Dnu1wFr0ONoEUzDdHjR8p5NC8jcpz0NK0ZDaCj/nz3UYIfbfzYEG+0jmGNYlLsaHEsl5QcqE43Ml3qx3SmKUIPsYXUicdytaHMTGQiXF4vicR4OswnwuZmRifYqDW8wt08+oJntq3YstEO13QBmCwDtDExTw97UREfl29SMjcby3qAnoMQU9MeOgcKNHVxMAf6hF4kAMliBhCzUfFhLJyO8pyHNpgHC0ctvjSh0N41EX3EmyXJ9a5AXN/CnKWZ486i/Dwu1vMaH/aah8dxFyChzd/E8oPVrxA3SsWGjLj2rl//wvvzVDlBNhgpuKgKgznWN7F/tQ5M4/zK6by25A0i9mA02Y42AKFOP8AZ2TXJkoOaWC1faXwFLAUeerJc/6zbHO3K90PyMW+Y5vTjyRWDQs1z0Wfg699cCovd69iWomLnWD6fzHbrESW8vDLD6gqv1HpcMkk6QVBVFc/RRWvdsVrazWlCDHc1wi8ZqnBLon/6ubzueGhRoYYxk0G13zgtnLJztp+ZK3G4ZyMxdQ8htOyINJOLnGkd1IhKgp4A2fuA0wReahBMRxgadY8ZQZ8Y3Lo1Vv5lFFZaPox4yK0MelA+irbsrvVp63MM197cFdwV1g0ZQk8OfxO0G56jDVhTgPc7p/wPJgbD3fY9Irs1wqyWutxI4lsiILjRiMnUvMZH+GtkEBwVvQtXF3Yy14vF2DRBTfuD2ZAibm/iKd9g/CYvp6mgNV1baYffDM4ovWZStEhXRGUN1iDFUcytXY6WzsHK0wbz9+Qzmvg3ww5U5a7RN1QEJrgkCH7R4X6CxprYb/7yqPCa/QZ3WAgvAtzhyoBykaur/mcrGKY1spiCG8Jr/98P/mus9JwMF+VQEAmMofz1jqu+BLRJRrIY20SfaiKtlizh5aObVc5V43j5zsimwLgOYBelmO7MINc0FNpsiqKKSPTZlhoo87RXMDcl33u86PNAUoHzOqABHFMRH6ycL8CWioj9hdouYosTU7DP/+eeZEqOrr1F0aq4HIchX7pp5XYfHBL06RLNREDE+RNXmWCy6q0/dufAeT+zjEcGLYdLhbraiM8AVc7DX5XF9zh81XyHOvD1dgcBIy1opZUS/9MkTUlD/wUdEeYxV4H4zg4akbXffnOvC1lD3M7bzxuoy7xoNDk/V5uv1rikod1RrMgHMr9MOp/nIotVMKhzApj1Vm8P+pSd8tQBHbj+K4oWr8s1c6ZPFLnscNJsGPF7dGZh42eJLsH9vHNpBakOn1DZQ7deqUz3TIiHi1YOsTmphS2MJjluZP6A+cfQ5JZav0YrYCYV01jwrAQoThvskWelPcOflJB6TCn5cmwU6W+n2hfVTtDutKl3OyT/J79F5C9OktI7lzrEckTssoBLPs1gRq841Rb6BB8/xazws+YHJbl1FCMtZ93Q4DWep7m+3WD+xLXjy9HM2Z2Wkh3mSXYryeRXf19VZYlBbWMin0oYYA2Tny4Vq/3je7j6d08oJQMtn9mPRoxArsgnUEKK90STFeUyjTl5zfrd5/VgpJe9IoAcob83/YaM/Gh5VTjINAgVW4NP0nWYaPKCCyZA9Z4pbHbggPAvEu35J4jZ1hY5BXppape5oizgEu+9HIVRNdwC2MzMm2eUkpHiIkln0Oeb5a9cP8ABWLJJz2UkL/IDzPKNNEUQGB6m+CKknV3PPlFs218lZBBR0a8uZt6mtnhEnr/KNM69t2+R4PxrKGj1I5v4pySKAuoSpy1q9VHSF51UiGJNUvTYAbqma37tmywKrQC8HpBbEY0e9XLjaEnsNCIT2I3+58wkLnpSJFKLd2wuFU2oUBDqpUodujeBDqv5rF3R8yve5teYmMhDvjHwDhG2fsDynDDDuvzQcrY3/d6+PCyYq7/Tz736nFH7bPY4keb+wLoMQsOLQ3I1sfxycQhnzdN88QXAmoOGE+xomal/GMR/zQMJUDltWri2ikWh86FRx7LJ4I+mtMQgH8IqppfYfq8t5N7/5fMSKmZ19i5qTWEnkQhRyMHdUaYKOQH43MS491aIVFOlltTESZ99bCRQ9yTUv9jRRLnSuCSPuOyC2l8DdUUjpvPoNSSa2P97hSJ7pElVvd94nYYzA0M9IqcTSQ4htZWyAJ3scN54fmTGS0S7E0cQSq3FLLISX0nG5u9aJM1b6FBsd/XsWsSzbtH4D1y8r+UOCaR8zn071NfFfc+cQdPpSO1/+b8mKh3p0uEqO6LCdP9sQrOVcfl/Fdq3h0iuJOFw+c5YMo7pcMt8wxlicVvURUDZi7C5wbIZ57J56isdnVWsyCOEc7MoFlSgu+yLd+SbdW6tUorKojo2NsawbV4BwHQQao10fCg36l2seOPDpSmQVaIxrFMMsxWFG6rP6CevkJ8l5ZD+rX9CfiQ3WrBei6KzO7jQipW03S1zzGXOAm6KHGzlYLt+Z/vRLDNQBLDqO7d6pCYWRid1vs1KtcwXql+uecdW3n00EQDXB0v8hh8iAI1bYGlQHW/tByj6dapz3UCKima5qjQtEpsd0NQ/CkhKwjB3nA3LIDvst3PsalayFZoyi2w/biWAG41dx6dsALOp/FZ0TpE8Pgl8e4Pho1tIOX9TPvCTjVWPtc0oilrgxavbkcOqp9Uo7hqkIZw2Zt8EoqQx/a8QcvCOPER3At6l275MPbI+TP8kxSAA9pcbOOxGRNjLCwgLiITAHH3PjIlUolPrFcRjEPCYF/3aGHggaxYp1eD6cae6BwJKt4A7RfoEjs8fXWDBUiffLz5ePkYJjDHZ4YErE5+X9yeTc1kG9ylAH4lkH7LjNK9YcLj1sDwbtK4hQj5mAJu2kvOYwA9ywlmjI84jGnqYlbystCce7FQ4M1icSG9rUc7MCSYD6a3uakbXr2O06a9EVU+982tlCH4Itd/nlS6cDEjs627uJkGoZW44TmeFpKLBOSHoEgLkA94mpJjW8UK+Tro6NEWAU1J1Ls74UhwMbtCzPcHlZ3m7KDXHxkqgqtxFyVpbVKPjuEc/gi/v45SiSzv84Og1m4eap+RiLa+pGF2/mZFO7cGY10KCfLs5vOrG/ST/oHt4AOEWZ1ZQUnrBcKLaHf++ljdslFNgJxOXqSXuKOaVYCuWazwKTs6yT4fMhvtrIhn44a6jpApGSJ3VaxzYRBX0QarnLZFhGTPKfdGhR89uDFuv7mHPlk1oj9VZ4h6DY/MNZjyFFUVFNqulBKPd0NmfysycHHdEWFAyQYXZrvwKL2aTMp6cr1smPKPvAJMj/je6ltH1AChL/Y7b+N+zgh3mCig/gLnBY3PYkpwu0E41PgTZIj4OsveoAFLxF2U9Uu9FJWcNxugIqDvg7TPJmTWa/Bg8PIlaiy98r3NKX8nQ+OTLydA1ccyO9U1T+jYXhuVYCDimTO+aVALLAoAH/cHXz0qCQ16RPkFFiW81JkKZHRymNf3sUUVubngfvO4B0mmQK16ltoTK8Vli3LQd+4lROLGHdKagj2hiUq/xalhV/x5+v65lhZCznT7IEGZPL9sjOahFW7E3JCBPAxEhySAtLGzZMkOtrzFchAXEMmeynFX3POBxUuJ7vCltViNYYewLZOeETKCVPzViZqv3cmgWxDkcaloH7Cardluj5ZJhzpwe63nDAY1SVSXn/oeNnxUo2JltOp5hfrauXnhaySuyC4YSan+06NsaM+3VLhud8BoupUJkUEXXCdByFF127X+8TleyJ2Z8rsVp6Q+mWdfYC8rYTyIQuelP/b7Gg0jeFiG0QdhYI+3uNvMw8b1G6kNK0Rxpsotf3IXwxF+RCmUe3I+a9C0qQXDq5z2t11fBsM7ES942p3be82qJ764agAZaeEzvhBS+uoOT4zFIm5FQbOsasijuJvV5NpQid+OmHj3G5wV1WxcT28ePg3mjbGnfY3PD0C2jcSXYLQWeVIwK5GsqJZ55hJLWkQ3DQPstEFv/9v2pUzYZ+JVLiQ7Gqhtb6WHyqL0slhtXK/pzyTHV3ezuq1sYH1WC1xwJQVQuSn5dxCvNaV0QD3E/4d5pXdYzeRiDwcKeHYgrw0wFiXTGAqh5K9XtTeDzLqeQQN+hQWRf6PoGJjhCiHE7ooZAdpjBxR6yb4G0L2XJKKiyrv7VA2AvFY1i5PmN6ReiJuemgeqSlhTNFuXvPOgwWz5n6KzsEellNElNMtdHuNwAUc2MW+F8OamKA+R+X4NMUW04IPKmeGX1H7dasq3IP0OHRZtANbgf9p8oyy9AKNAjRn/6N+K54Iz77S10fJhkx3SQAHDBnJjTouVbwEbO0RxWzU7USGDW/zZnbxJ7tIhvGAnlsZcFUJt0vXVsI5m+wOOGAFF4VKFxn03L1/kFGoBOUw2zsYTP9pUKE+WKt9kHNkUBrzxwkI8+X3scqTbEObNyReR8vMiyOapAoPJt8AtCZPJy9LoGsGJxB4ywCjFgB9H6z/dj4QKtSbmoCyoVqAHYNtV7u5ZonvQUTgAR5WzcbovCtkgE0d2B+Y/Zf61WpxQuJc1HzeGuTUW9BvNBffJZs1uD1aJlu1UBxvqoftxY3gSJDeah7vUBP7NnqTP16RFWvQngtoHhVERrN+OoLXzvJoLS71QRO0wOWnPb4JBxkZmzinMyJ/UBwnlmLxDinuJbo2ysUgj7oo7ntKlEvNiowcYHzxBcKma+v6ehFnOtixyvARO1FnGbVUxijbymWFHVJMlDZSYgI2tiD+LVzl9qowRKeFgzJbP3L5kaSmqysvZ4MHTB6odw6WpXzlTbwc2z/q5FHHgFtqx5vyGa6nWT98cSaFzrB0gPQ4Gy/MbPuaY/APyVaAT9ChC81ZiEH7RcVjIYeWgUbPQot1XeIjdRlHEw4z3z11bJ59zZ7hnrDA2wnJYTsgxcRiuvbOtdKPTyFpqzTbggMxYiPB9q4NmSHttcEfwmXTEn8FB/IS7xOpQ+kcr9+N8OY4TpDV5iTADMYg3+IafwNGmKy/fJocuSNPmfHhUlgOzFRnHHj231a26GPgfqCIW8uRUsWGnzqjJA8Fc0b4jnw16vMyKHDbw5t8/dJbnaeW1AR8UwGuQSUMYTokX81Ggc90vYikP8aQNMgsHQLsYwfEcPLML3umSo8tDtV/9+xXznvg5UKl15nC+TRxEnDpCdUCBEWuYFbfO3nzpEcIPSRVW7+r0waX+Ll+T07Nhlh/8SFAV+qx0Uz1vkDRaz8IfbzhKm4vsOZx3YceeyqaUNvFI7YNE0xj1xMsEdHLVb7gF2d8smkbJR9Y4ZPaFWn4rIFomRu3ihQFpw9/W2jtV+aei4aITLGPgkeiNP2FAzZ834RLv3koEwt05YbwH3z5bqpczDgUwxeP1BiY8JS1LmqRQXHDpdpotLLWHLjlQ9/N6Brx72kr5DZeJfT6H2HIOj+rZGsnBIGfW3WLBO60N+7mLYkn8ZHExD9IiahMQS6LfgVCtDOUXcVdc01/PIEXXsYXeBZXB6X09+mu4w6+SJM3sd5upSH/6dcXhc/JH5UIivfX0keog8A4RSf4v/y57Y9aNEMEPexJKY0aCSx3WiFYpAmuqfzgv7AkabzF1VhltN5iCyRDurNc2hr5Mpw8DNhFy8LC+jpDIbEeRzIiA/CtMfb+nOK5ZVrWPYq2kxwFqDKLRtH3oP3o1ES4Z6PER68l3tOxTcvA+T6pQ0AolYTmOkIvjT3yJPeOykXia70mA2gXxQ0hiqjWhrv1+kfqgvhTHXH9YgqK0Hg7qCA0sUWz5Vz/VWBJ+DLuHXVyXRnvZbxzje/dcjt2lYnztFMPnv4gLG/Xfd4c0YjWAA9lKWF3cWY1DWksorU/EFHhMWx5EMb7wjJVg17xrkh6C50Tb6T6CRC08uP8YG13ULOY0kU16XFa58wDbL+G1Mb63l2g0/QgcUAgz+/SO5grSbEnpyC6nNFYsAFRlAzrn2pdKKzjidn1+Fxy8V3qwLChBNxt/Q1rZaL/3uwK+bMVTV2N5BxZmRuDVx8hMDAOfyzB9jrTfpX0RIO/6bARsP6DCvjLxvKSXVYq9IC1ZeXmNcWVXidYI8X5TSi5Ai5x2kK4le/yfpuehC+e1t3F8rtIdtUVdxIFa3sxuUnDKByJ54rm48dpiR21bjK1ffnx1Wh7zhkMIHe4Q6ZmsexEcFxMv6g5wn/hvnaV9oLgJ4gr46aeO6eQ8FHvwXT7NX50qKfUzOyaQU+8C1e8L9+eRAQ2hdZr5nf5Gddi0/oDFagSKhPoKUQI5mWzx0xdfd/uUAmY4vLXFOpvXlA/wz5cqtHIuaTQB4OjtQHjuUaQ+Jc38gatY3LXyK0ZAKFx6g4zfjCjCcQ5QyAr/OQKbkzLVDqh/QewtHc7EnDqKufyliN+q/QsUGFRh/4Cp0w+8Qyp9uFdAt+vgYw12ElL2ggzSeEEAaHO7JrF5D/HyieasSIKgmMtWSZ5oe+bHqp1PLXCOkv9qIXODJLWqvuEsd4BiSFSmBL93GtgnKCUGe3zR2urA8CeV2fgToFRW7NSLQHHI8nmY9Qxa+mgnCRrq3o50PnKDOn3EUUHnMWT9k9QtsIDyGCzwVQsBwq+LInpQShjJQ467wn16fRNKNQrlBaTbsEjgZattxXm3CC0mFMIM4b4liXakGPOAL118mce1RyrfVf83CpHGfBRsJ2FrrKRn/8CNaqEobjFqvu/2hsyXO3Q1x6RFmfc92kt+victkqDPKd0h0hPCvDjtJ9c36B78Nw1jTdLohoZbMgXkZUvcx2KpL7BLH7ENpzUbEMylLDXQl7DxteZl457O5shFlCG8bq9E8lRzSFY4tEIPUlOmAFMFFO206A7q4OYBDRkSGnSzgOpFErahF7VlNQvWKX0fs/shsoNz6Zg2dOfdjpJnEBxj9QHhDExQMzZ95TumYq6lgfVQ/1n7SP+4nij+6QdfiW1XG18nWHk+bE3UaNq4CUAUZAUjqHuu6LcE3MF8rQ/9jYrO7IKeQQrC73g4Y8G4f8JqNFjkL9/vG593Hd0E4UpFj5JBsS90dfBDlN8it/J8yvc0no0qLdfIAe/1gXodZG40HhP274V3rO10VUo9qcxoT4e0T1fbZeycyspqXZWxpSoGM0M35g69JRau70yLbWEehsqiqg305ra7RWI5N3cZkFWRHevTzZ4fUgHdS9k8hGFCLf3+4jYQN5FqHylT8AWETk5ONRaH4SJHWR9sx0BaqyRltic88eb77+IcBvpI4yGkHDE8Yk0oLH7cNctuqqBIvrfvX2edhPKngEsiO+MwoM/6FsoIJu1N6a1nXjbTAFOVcA5QsmPZQB1F+hitfrJEtGufU1L1jFDybYehu97FkyKpR7whD/lEbspg7YmtqDvHmbJLAi+fUazJjAFoBE+g3r//NEYCKATKP2UZG8WMJpJin4W6oZ4BUxgkgmcLqQJzXP7elvTWKs6YAh+xa/Vfw1tj5fHOFr/ea1rzhB9nN8QTrhy/W2xLmta0El4wbqu4ioBe2bB20uGczPu7f9mnNuIvWcW3oarBaXK627LZkH0DCcXVtPi8QGXjTKLTG1ruskin2FxEhed0R3JuzpBpL9s/n3CAMnrWjRfckxIOWhixIuuCcYZiELjcZfumTM/lzrRQRu4XmebnQUDm/oBnzLuVQ3HknZFpV719QYYWbkOD7SAKB7aFtVyXeSu2vjzNT9sSa7cAMxw21zTY60LskTd/2a/WqHGqvmMFT0q8o8cCHwZj4T8ncNb/++i1VFRgOTvEaBsW5xx19pyh4oucxmO2btCLcTiFOK3F6+5KtISGVC1S9W/WYOnjQTZO0Fp/NGZeNPcaIkMlHqeAf/JDtlL1EpdefVORC+euBZtGAPZpja0xJjfGje1vWQTUOi2jer8FdRze5Gi7nn0LIkO/inhvAYQ20g5dBLRuulOS02fPb+zIE3GFmcY/OlmRNgZgAOf1esjhNcjd/yvp67FiqUzB72EQbtfdwX2IhsNzCBUpJCjbimrE2gUhqy9DE1xEY2pmo34YmqkMqVTN2ZfOUSQXwkDmpTtW5yoWYoiZXGXtnDMcYe7DnW1IrJju2mknoOB29MY5Lf/hE6l+QGJPhP2fzmnJwl+SDUdUmbCJGt+nlBS4ETB0JisUCeZInkIMA7tjJdgh81xGZEi0l4a6lDlLKvuv3r+GDkXQG8Z8QS5VAk2nCAvpAQr+VDHPwadGaXnoixJ7ORjre2KsKV97Dx+7t+zfjdrbxvBNcPw9/RhAxF0Ut0HcfFFxjhfPzhu9Ee94j+ZbQWyiAa9h36V6J3w5ZMthuILBgltvAP20ppp3dDLDAbuSw6oFv0aK0xOemVxpv+hppFxfr/0vxfL26jPXRPtL4kOc79QVSnIdzfMQExS7URlOeFUnBLKMlJruXyyf6a2DG76ZpNREpkobS6/NmpWELtYNdN3rY31jZDypmL+jpC20UYiJs1qSRdD1dUZsvfyZtnDxPUBWXTAvhcZPZPJKmr6V4nxCwDkNzkn+D1dmYIDmhtsXQOAI++46kcYgnaSdv0WIiG1Epf3lhjqGVGpgyieV/KTJCyBlzsJfHRcLwlLluDdyhUfQKKwBjvhca2pWv0/dSY1cbnnwRmEuKu6paWFy7YSG1qwKjqNgJaDozaUtgh1H2nXkqXrSqs9IrA4KuO0ajyYUP0nSc1q/aA8Iig1y9Nk4EosklrC9njBWE3ZoO7MtVKbYJAkwb5ky2baY3p1phuqz0wjNIO+KIJCKad1u/7RKW+UzPPscwH7w2tkKNFRJq9MWmI4HqBTEB3V9JKJFSqkFMEDV9aWTEfBOv7C8/idKdFkulSS86RBMJm2Nye6rGIz2MAPOUO37ImCX0AmJpPSaecbHgqgl01WTpVekEdTwun8WjABojAcY8EZVujkRtKvU8w5LEAqVUATZy1aS0l/PGk/0nGCn8D5j4hGYiAkQVHmuQTQ5ZFIuL6a51xdhPnYwVzKORGLECL06CAbNNQDbZnucM4lvm/DGkECyaXb9tq3eTYGy5rS1ZhyRhcFLfr18WaNHp7T4IlMzwxIqQhzC41PlCBmDn3TpjrY4YdEJwKWotlvmxoAzFpaRsLE4LfIfPWhFpZtCkyGRQgcGEUWWU9RxM9WuyKvtj7IwQPIwptO8J09C+0QRf0spGVLkemPgtbeH2p57QUpV+BO7zJcgNJZ6t82dHR+dz+tzxUommCdYWieJjjwZpYtm9ZobUnU/hYRfZz8TkjXM5goifvqfUgUzWn/v0RFwQSywuLLag//tL8JcclnrtbSzqeuO20zYfugOEor8KHhAzlzydheC5oulXKNapQqbbqQKS7iHFmy30ECglxkeV4tWfqvtEQ3scMJLniWrfr64HYbBik55WrixDhIHriyySqdMfDRsORg3vcIRS+R2d4qMZP/PoWPp2RcL+XEB7faxiSADDw3SZIZQXvrWMM1nrvQs+W+FzAbzij+j7DVTU6b4Bee4aHC5H7yYu1keESEleTOmdSa5WBjZfhxxusMjPo2euO+eZ+/Le1//ctpyJl2Nxar+/chUQyhc8jeKRBkFAqhNGlqiN54yCvhRCpG9WN/Pv6hiM6+Wplz9ZlgKbaJ31YTyvgJcGKjMNbtsfjNTwpCeddVrFmgoVzbO6o4iNOEn8QoKmq5sbmUWp7/9pRd18ZvvST3L/idhw0cm6pQ3JM5sYiuZt4DtoxOf4yYOQL94RjHdKp+0sjvsE5DMz5dGBrJs4reFFY/i+nIwzuzSzF6lJVlqY3szajR+y4o4ki+VFUB1EnDtUDVkba9nROJvKiqCpsrarc6sW7YpLzYJWiVn+Rk5AALKrp8TWedB/NT1uH3ja5Im69xdjlsArT+mL9FrGt3rWDNH/rbAPLaQ+Mt2TU9S9Q+EL0QfX54Rzr7UMHiU+7PrrZ1Vnq3U/r90EABiKVvik2TxKSkuqzegIylh/ePHmrZ084T4y/myxTsV0M61NpzyZiuQoVxbeutxW3D2rQE4P9OsItBdukyYkzZfdvpi3FNCv6p10rPFQJkl8/tAUT6LBCWQ9iHQkfL5qtoacuWHm2pdRRkeJI2edc4bofpUi+qAcZLOV9TeP5GDW9sl3JsQaBlSxYwAnz+EFlkXjxkrYC1aJpZ6cDhtPAoK/nl2ohseFwzoKPwqHdBGoXcO9dy+0JTKP28LUibBxZnDbUShRGQc2iJUZ3e7HVGVQrtg7+op58CdJPWtxt9HJqoXIF8M34Sl0v0s0bzAIGaR038rOBv+DdPrlRLw9pN75/WjaEPHW4eIxtXLo/Tw8q3XafLuV8tYapvLe22IlkcjqdGEUFF3aOCtbgpt6hRHWG0ZWy/Ix1eNGUqiKcZx1pLn1X/DiNj6MZ/j5RImVDmOERpOleV1mYxC2HL/jcACg4tALTzRHqR2ecezletAFZ3bFyLmNtLK3LX1pRt/M9qknvwRMRlzRGgGd0n91xFuIdxM5rnZyjnSDoZDNoeJMiN7BzrNberFpknnLi/wFIkQX7VaPOtUoMQL02PvEgNP0my4uq6mTMszhmGiO4VYweGbaeoY9WVYREsru5cv4Vk1LPOpuqwiGskg7iCiFgCrBYVPeLfhE1sl6Xc5MOntf+prWJbhghniyKG/EVItHyBpnSAZVVB+LGULqCaTvSFKK96OKcfc8lWbQVHnZ+qlsK5UmONlSWgiBmnEzPNGJrnJ80L5G5LMT5TejOpGOU8aH8uRLA3Rkxo8eW+IJ+8Ge8i3OBXpoYKug6V5punK1QRF1y1UuaebiQXPkXSLK90XHP6bTV1Z2NtQMOnOuDoaU0XTBNhOdnN4e9JInI3qJ95AeDdjB55cn6eReO2H8otSnbf3K7BiMT5J6y7Eq5ix8ARhXuT4o5+Ijgk2jZUTo5khCCW0vdUTxP8nCfrl8x0peDRKW3GRPUiCJ2HQx60b4Kz2CdvVcmSXLauZc1HCLEfEr2ofZxeDtWDor7EZNNZSqGNcFcQBk1C5Ldi12DLcMXnBHPzlLRR/03FB0vPDO74p2AT9qJNeLtooJC0YONXi18Iy+UB3twRxp89WXRjLvQioCkGUbB3Wk15jIaysb2BNjwNaua2YHiQbYqjY5WzxIe1w7Hs4sahCfrH0M1RVjOPieybqRIaEA1j2tJmF1eGiXOjVkhr59qa5PSY+4Zrh/59VDLqM0eiseqt+ecYmbDELwXVU/D5ImgqBVFECxaKdxK4ONw1oX14R7B62/C1GSP8gKOU0pjrObcPSeRlosDm8ARAMKsudVPPZeqHGzIYm/PnhAi7kKuSfmsuvfTblrqEbFRUn9JBYr6CsZKq91o47dNqY5Q0n0++EqxI/qO56Y3OT0+hFxhGFcpKM4by87BOklSJKI7PVK45oaUmPEFBhMvOwlw46tAb1YJkTNRU1KYexMZo7xyxstQx8k4psFUnujWLeRc6fxyfWr+6EU9TusvvTUrAAtU/MQst4NGfOfhWyaBw/37g0wzi+8CrV1gmgnP41/WtQJKbKIKh5sp46MSLPXSE2XsabuQfcNFZcFJdWbUw/XmxajWqGDrW4RmKSrQt+EVpkqvIBtOq3S7YtqnyEgPvBcf0fzJ4Vlwl8g593PK1fHv2qJy9uHmveYMEQwZWpsIZh/OHm13P9RS7muD8Yqj7QQrx7RRz9OAlNYxW5RzhJddc52M95P90okx9AOaeXXRAjKkZ1Z1bguIqZ6rCDWmGd77YMTfnrHi+u6vtv3CU64V9tyP44xD+zPBoskwZhFoavf5M6fek0NkS/v9F9gORppWNmyDcL4xXAHmIyaM0Dq7gk2KeFMIXRWHqZ26AxvmHyJRP084iPxE6Mz6H8yp0xQSaUSk748/rV4PhxKx18zbs+tB4GUX7VJFJH4vxyugQLRXB24WDSwnGo0Z7tQIZoQ/7N16Dib0G5WXzxEaT2llHio6aCfKKxYNNUdK3PxXDkZ8TcpQO6a8PUfDT5JMODrNQ4BheiVwGej+fzMBimqXZyZBOn15+Is4QWy/ss6DltS32U/rvxPXpb43Fa45Ij4x+IPJ/Nv8mgcyOuke7ZGQQvlwqoxtXHogKM1N8g6EkRPsX7C5j+z4xdIGMcirVep924QXi3yBFT3hIF63R5/op4uBD4wK+0LAmba92LRiLegq4pkFQuZzmOj71cgxUGzjK0b72niuPRr3V9tuz7wGKfoyjsKDxHMUeS9NDeiHNZdIUhB6Fc1OpAwoD+vLj3wT/yNN5xqqQE1clJTTfYXLfbJSawcJvUHPOw5NofGdYzraggAPpswGua7MncuJfqUI3AlPcFLcXGKEtzTtufrMs5hJvVxM11Km63wSzXxfC3R0retKZZhF7KF//ws3YBuK75TnX2G8ElH5nJYwUyeUg23ZKStaRKja4AgRCZthUP7v0Q0UZhYBPPpeHbgnsfAbx2qR99tjtpiisABRzcEu+Ensz3ugCYZ3sJEwd1GD0GaOmCMyMH7J+F9WLbC/erd8xxQEUAlzMuNW0FaUnMYcpzq9bh4xDkJvrV8Qaxhq+pzohHieNfMgTLZt0WyRn/Za9K+K9I/Rugq5JmONbiBGofhm7qIgmu91qGnnRUYQ0PK+mvuPmklzzygi3/+GNUzHHv2bPhvhwFz/bZSIp020Zd0mtddyFUvIGXFRVo4ZPy0c+BZ476GdQ07/RGZ13hzwHH8i9xu8fv/wcCf/FAcYw/Qr9WzgFSdUf+D5IhQ3S2Ab+87HoQbwPXEg3SeoJaDAXzO8/nEAWb698snWb7It4W2jKnbjlxaqWWAXgTS84xZpHqqkOF4L2SNgVVxhsCocmg350OsqPJZyf6+OO+/NeHGUDX/Ofrla/0H3DdRtG58tZRDa5NFuB+nLGe5/JFKUJBpLBpFmfSgLIr/6wov752Tn7n1I6QVHFjL2hHT9gdCGPMYm5gES+jv3FxZiRHeJS/No5nT4CMjauBH3+/JhtOJPROiXp/Phdgo8yR8XX0/lbOPeuhDKSLTiS9FLi5HjjHATpPUK42wRe+hXvMkbYqKYsvG69ChZzu/WeqWbLIZYAK2uEqq2DJ19ZpQw5rXUYPfX5Eh7KU76iFHldhMZafvURwdc1rAz9XrSI9xO6lppcIadS2qwpoYvBtkgK83KEq1PzxhY5tDVMJQFR1lxQYFfE6kW2FcTwdDL6cE0f6wesmfBQNbP0ev4ONppGjFNQh3pw0Rbpail2VYJGXc/iR4cWXG+QCQGCwpeBr77YxgbO7Hrona72Y0R6Q4lK0ojTYuxz2y17474O1/RMNV/dEg/6dnPW5hJL3W7cl034EyrMPoppU1On83sr3rTZXB9ulk1F8YlKSbTJ+Q/elJPlOleOMlEmDJy6koL34BsLJocguaz8ZrHQWhjtNfbabgwQ5DRp3ERexg5aHoxlNbFmPz+Zj+M5QoWuZOcNwzewmpnc9tG7hej+7GV4m8Kl5JfrkJWdzNIY02UoKbuypt/BIa5RyC19nB/iKizBqKGTp9BDxfC6Ie5qwYhKVZbeUg5W1K5yOo22Bsluyd68NkdKlUf3pcnMpq/dtUIQaDx2hPQb5yuLMI2oraJ4lvnH/o0puEQ14Uw9ceBdej4eDhXyRdQYS+zcy+13EQM+x8/yH0TMBeKAFmbb28AykxiCV9b0D9A3aaSy+K9cbZtVrxPwsnRqqvgOFRC1Ft6oQAkQpIWJ914GdgkFIZSXSArVDKZ4LX6WIuvvPuoNL20dBWCibscWuiuvE9iSEnbKt3JkliPvAHwWHzsPwGNNAiaG4pfA8ROKvbO+VbvsCmjRnYN5qqncqgTnf/1FExqiQX1p3jaU74mWjmqOBocyWc6/aSkX2IW1v0rfGlFcTnMLAUJh/a14jmnVNv9ETBQTIuc2nOtO/5Bvy3EODauwdi4O9NncmGW5zIH/YAjmvKFT1maG7RYuGwxsyKfFcSuyZknH43tAitp7OIIlBYXH3fvLph70FA1sFVmZOGwfpEDJS6Tce5xdgJa8scsaVYBBpICrc3OteCHX19fK1WJFPIZX3VVxQ6oWNHJpYoG6MXcMcGg6SHFssQqZEgBmS7CA7pC+2mMeWuq/6+odKsoraBkSQdfNp82FH7DcIUcwqYOJFFICqqQrel/XQ+KUqSl5nWqQVNIQ4fX1ErhM530Ki0oF+jajn5UOLLERjLS0ooW6GWgqSZ1sAFI/ddXVBRd4lRbeRrs6sPLKglOxndr7Z6I7/ZuZA7k79362KS6x/RAxY305ygFX7NyZQ+0Soib9kYmT+8zrmr3ziGwWEba+arRYw9pDdInbPv0w68iUgxvX5I+AtA1aD5TGNTVguKmBHMoFFEXGZVFk6EWV/lKVQA0a4gFYHH+l9Jg9n7pc+hGLIqkikZejSXLm+5ymFBKvwb0Gk4isni63kiMkAluTSUGPEfrph7bJ/slvWw5tnMU2lWIEIISMhbZCQt9is8RZduVdcBAyRrqM6QBznuAQRyyKs1k7P2UimB8v99d7NXL7PTqXdOsxPlGTBiUZsfXjW8F2ysZziggK97EW2X77PRnhs14DyX0ekocnav2RZiDaumnnuMFxY50D+0SCRsiCMGEwC37EcvTS83cW5rhHZfs2K4p4gXiW8C7Zn8d8LjOGqPmW5Uur6cpJ/7Wauz26V5vbB0v7zbRgVD+zhSHqImLJF7MDf7dQd387GVjpBapv9S95ZFjbxSDGQEZmF9unpjoKNyypxtllR/IIlfrMa2+tdMV3Cus2wOiMAgalUhFzKyakHSeQO7ErCEZPBSexXrpstW1B9tmt6+44VnBbhuzRGO6xBVmiq4JEFfUleZO4FlE3s39VVlhGTOKC5MVHVVjKwl/z/rzjjOjPuF7/ORx7JNZIzaTUKmLG2ct6dn7aVrUme5zL1GziguNlLzH+lCMVfIMN2ZXru94pSUf28YsUswXZAkp4Y5jR1a4rC7cOSEJAaBvLoLCs6ayrMbhesQA33S8l9M/UDPd149INE2y4fJ446f9I6FzkkjtlKcdUsuwnbGq4JLKSL6Ryo9yQ6MBqHf5hd2J0eTy61PHO/bQC4sM1KBSWv5ZDrwv7R4XM3m7108IZG4Va8NojyFoAQgM58rllH082ITbCikVGV/FIA+qXVHN0VYUUGhzdFWxBqO9vATTtqpy006e5agNTDXmFAHRbKDViIJ4OdQysmeNGd0f1/HZcvqazfdsVfSuXKWnoxZP93/mOfK8D7NNW5yqPQEqQcAAGgNJNmd4RIbDsi8vG12lD55Dguy+6AUI4L9lsBbZRNPudtEvMVzf4W2ftJfsX77ul5lPGHLBzpZU5eX0h0PQKVGPj4VIFZrKtGXtYzSoxScEJ7276II5bqAz6Q6FzN+fuGvI/UtsVO76fM3sFBrG5VL3J7AR9jLgj9Og+BS+ZjAmx8JsRYs4J7ZvbjlIz0etzvd4xcsOn13d3/MbcwYjLDNpI0xryrtuXzV2+jjjhVtQVZfM4Bdb2FG/8xExHL/Y+EPYZ19kdNTDQdOa5wQA2xJ3syBb3+sUTjKWz2Sinv5OGh6DkV8ysuQhMFlTXrCdUgWT4CWm/eunHLCX3WnKIjUOUaz5UqfF1/aBjWw477BYmYXOkSIZUt7PXFPcMp6FEJ3CWXDRkgzcJmHCraFPZKbI4yFbqq3B5FQYYXD+6S3Z7Tuv56d9ShWwh1JNhZeaC2GhHaXBbIptvzaQlLG+l/23/AyrPJI0KCYGn/ViFjmHyl/cAH/J3M4QE1uLfOonv09y+OyLkP5LnEddJzf2GAMgAG0ouKsId9m9bFopScl2+lYEmtoNhdwGBtno5o7qlxj/InhZg2MA6QDlr42KWmTeU+zB/xVlP81tRNGbDhv9KTM388cIURnYPqt70QXd3Mos7NciNotx6QrkBtFszRs3few0vzj4BoV4slqKhcNxHFhBvnhw+Eqr+INScveqtffVn4TPgV/5thZZg9iwY/+j1WDStRkTWJg1G7FqL7FfknV5QFcnrwcp8ufqHX6g6OXUAfvUgYx9JVrYcAoOzgqqGH1OFzjg4DSGZ22hsY9Dc0zhVVA+mG9My/3hvo54ET6vJAzg7vk63ru7WoILwxi896UlbrBFX67j+TTCRQUHnZCsJQwFlaoL3yF3iWkpMPC+YNQ4G4tWIZW+tp5SUazCCQEYOwkueUwFF7XN9dr/TtuT8+uyUhQ78i6Af9kafC750QinsOTivRCBxlgy/fS1QC/C2v/OracIYLwR+CHVebil8ZaSrMzvsYwQuExy1SiqfVyUfPRm8UWDr/v8LSBlW68Orcr2JWUEOLhZiSGwV+Wa/OA1sx+9x9rISbyKLCnXRB78QpoOtP4xty2t7fust8or1EXgFC+4lIZ8jSPEe8CPMeWR3BmPdFc8S/WvHkYSWfdXyDx88ZJZYdPNYmld7azr7uvGb50f0oRnAmz2f+QhZoWQoS533AKfo0RMzgMrTk4ClWnOTm7trVQkxT9bVZ+t/BtcFMz1HyOG2Cqz+cyhZH9bGTCQrHb7W/6poSfpNExUSed8FAVz0f7gYR+Hcpe/zv2SC78ajVFnestMKqKlyZZO+pB7eYOc2o3IX743wBuNWSCHXmjGAtg3TdZYa8olKBWq2CY9V8oMc3Zu9eyQX8PhB0DDzeqTg5nxNO/gb7kiMiR4U7HBPXY4uo/UxFBlt8Qv8wjoNsXX9nbRXRb/jMEY/vO7OXftQFSnbpVorhyByxQJRDqYhBPPF7AJEuH5YPN5V2gGIWyzOrnOnOhxpQSFZrvxKz1EcihZsLMrTrJJXIbR3UG4hdxSQmBqrjwcui9ckeTJ+bdxHyI/ewhd+3ujhbi0ytTZhiByqDGOU4f4/Do4OJtddBmP/hoMRdbdeyi51woWWgBsQDD52Hpc/Dcy2893kQRTzGWIn91TmY6bZFnjgHdyAIp0RXZ/1if57ZIamjNcVphGKjn58+dQ4j5r0+ND8NHLqjsScG6MT77Jg/VpeEQdYBnuPHpHR27LpcaFBnm90At/QDEv41q4vq0ZGWA0jePQlXNsMBEFLw4KTPkAL7/UaSxGGzDAsZnlY1smoecl5DtB7tPOWFI7wiVr1rZV+Sk+P5PsKUWMxx+d9h8Wu509igbEQV5GgN94LVR/vnv2HUHv9n4CyxjQzDLFvCpE2vn5UXycZfpNDkM5QSa58y1W3QRa2Imyc4mpWrghbqjXwrHZ00bPYa+0Xrx2CQ8mUcQdqdIFWAsQX4MIGBCsTYpd9hdndarjluSVX+K0YTqV/oz/xgysi5k/Dg0zCEyXyiVf8Lib5+vsgzFAoUha5ya421L5dgb9hdCqVVAFBb3Ki6S2G6RBz8l1pWeNsYUAQBG1vE3xoZwqIGabirO+so3kvypel9RAFT6UDv395zRx4+Akv7pItlCN3UXt3xEfZji3kLacTRYgGqllxTFw0fTHGU04Qx/eSPJitBi3BsgOSVBkyzgspo437IBx7kVNqxhaZab0WEbTsMy7bBTxtNbodiUN0vcQBbQeCJBH//b+nlnUvHDtAgYaMUllf0Y/qB0Pr8ra5aEdQ/G4IZdN8DJDWySzEdcXeEdxmpflTPm49d52Qx4XYZBCi6UtPIHAtZKlZBW65PepoBMDXp5ycNoazGDmu8fno8tayhC2WCFgRBHBMyN325RVSSC4SIfY4otZlOZ7U222FnY5N6DblYLgaDYA6A5YwmmxPwkpgB7DHrYYxfZr/WkiiFDq7JynVLDTpA9aFxe8HczC0gznfPlHoVgn7VJ7mRwJd0WaUZIG5yAByUCvhyq01PLPLoJ2QB0hk2q0G/v+UCWoGaaqjiekU1vRbYj/lDDcT78h7q2xa5iEB7PAfp0xDCzaboYXFCSs0bX8LaY++KIwQ259YgSP6lrVWzHkEAxGGqTMJ0RUeHJSSKbP1w+3/jDXWUBZ/lYLGRG3zWEjOb+cJnc7ZUKw3A8WXlgDSo4Uwtk1jhHRff4QAnByuzitjMjYLr0oQ4lVvOYGMzExoyciBjyW8a+nUHCS6X7bAMLaZKT6z0cors+WkxtPKJlu/4vF7OhFUp8Qrq2eY7znhJMWzVP0VL6LJH4uwT4Pri0jDDaP1S4kQGI+rsvh48sy0Gy0fKz5RK5MIAu80AVpgrf1STKBed8syP8b23dmI3qE3vh4BWDg8Iv1n+zh/+TDIdqfPJVyzkuD7o7y4c5cADyqSP+TTZ57BCDrKwhCWgKLxpMi5ISfbyIpLCLHRu35MtAhdABo0ajOes0pR3yyYI/iaO38Wd7BOmXYs8Li0pTZNZ3lEEJhoZLAORpxOJwPjuRjR36lzN1oTvqRBnmx4Iywxu2pelHqVUGetaSpEYd3k0TaP/LCiS6WXI+4YZ91KynsiHg1MiBg3suitk0VSP4VvpgeWV542WNMGJ/v5Fo9SUQ7hX3jImPmcyWHLNebf4yOLAP0r68MSTcjzl3HbjMxHeJOsaKskmrVfJx+w1v0pWBiYxA69YQ++h/6kumzTa/irTyWZ5YACsp1ArpZ5VK0GUaf1o62tRVR14aBiA2cQ5oi7TOox11fKobpOq/yXBBZB5qr+van6vZowtT79h203BCVmmF0z4PsXkAI8fwC7Mm8NBWks6HKdo+4VII8O6MLTHnC45MJ2xV5tXK7ThxuRpBoYi7ZUZGY9qlw7NJ2CQDDCUr9KUgqFCuldqCZzZ8YyOZ3j4VAMlCHUd4ncwj87qDwxsP/sX9jV5WX7Tx8gBz+JwtWfFLrGy2graUITEU++hcNXkhUC21iFwEp+erkSAysqVHXSCMwdqfKIkOUcXZTinmOFWw9TOpYSspzKk8ePkLOihnlJL2uqGxlt/PEUkRPZcKNnKJHvT6H/QlUBxjBHjBMWR3xhDrQga3gLzS5nisR6JIJ7zcQm8Degb+gTPisth9sCYzE4HiJ6ibNg3JiZ2Ago+4yXw/4BvalS3m7Y9w8hOwePqEGsAUOnzeSdGCTiHQTfDwsxsIg0EVhNBrlUXjiGKzXAiaRZfmQrBjLypSESjlBytunwByrD+GanPTjk7AFVW1f5UuBhAR916tpsl9+dyybUiYci2XDVDzRsPIwf6VTFGON1Nkz5mNzA7uPevIRT2mU85VdrYUuRNQnbI12yFdmlAXuFLgWQBkPGQIVqjGGnN/wX8reQ3z8DaBAu2XXvGvKMe0q7EFVXY1/5Ej/kRMbl32lV2HKkcRN40pmK8uwJ/PuKvU3GS2ih+lzxZZV1imyNO/8BgbtS9EiC2d5N1Vl0TnQ8QJRe1ta9VwQiD1Tkva2IK3MVoyYMPJoW3dCbLJrxJTGuN4SgA54CwkmdbrylMEleDntf+Qz724NLMcCLETMFadg49FZC5NybsgyomZxChtkx1v3sTDdso4e9iw2Et5bP6lKJefrze/vkIfdoR10cabjv5fyiFQPcr4zDJ38Q3orRytzpgPXi4ggEiijLc3wJwN7YKpnnfPyRp6iteQUDOuEmeog+T4rIO265uF9VvRnx3OAauTNBfvIzhhcbcmBUaFOD4QC65RST+eqEeZpMzVqrbgCyd2l8RvLENsmYBdMm4lobTZtmCmXSu/8CL28WRMFgxCTlaNUszM6ncW8cIbB3NUftGB2ev9I9W/YEmO1Sa+KwFfFuOB4ZCxT5BcE4DSTvtLYIjxWtF7tN+T4VylYUMoflNhsyKIUcpnEktD8HumRcjY0EtGNYsU/NrlOlRL1a0CciQuiBflXMiyDrlzuSSawy2ggBbLz/R1icgb8WeIQXOkw+lMyh9TQFSP/1OTjDSz82OkSxLO2/9tzrUS8Exl/PJTtBWTSL2AaiVWI0hcfljuxXtKgwYMGcdsQN0ByeqvPI0UFQQVt3trizap1vu0cxZxbOsQvV6Hy6XnPISZzb3G2QiRYrqig72mYj6StymibCIG27zFeAwDX5hy3ls8d8Rz4ZxLsFgDrvT/Hzs0mRrRsjhp78u/I4EscCtvmPBmAuHqx7Bq5LA1ZEQ1lZxKj66DHy3oJ3nfS6X+vnvSJrWeqAz/66BMD6qeEDWkmljUepdGNnvj4EdkX4uE/LEZae5dVCiIegXEqc9+F3RbrJTLW7Ybej+nLVWR0E3sHV8UbupY5SvHkjTygIXKPhTheNxEiFJpE3jFLHMRPccJDNn69dxPAwnl6nfSl6V70cEDxfUsT0x/i+IGr+/0JiMPa+H1LYWRtTA+FdJz7dWHv4XKA/qe6ocmnARgYI/v3f6XyXPiQIqLcAK5Q3IfziCL7fAU7NOaHdJ4/4AvPPoqfya5bgRQhvjVq43CUZ2NjXjls/a5vDeH4XL5LLnc8kFDjnmTeuTnCtP15I4I8Vzsja8jBAj3y195xjkirES6++l0kQYUW+bk1MUpCLNJ7o1wrEykXHT8KlzJ1Nv1L7KUbUqooJPVfoOY+1yCoBLywrzFlq7B1oFP2MteMcX7LWGa3B9dIDpr4ilFnboRKW/0F5OpgHNRzRYRXiRpzhiPX2xgxYxjqRxkV1lq9c2ZkjZxQQy9n7x9BEO/wRzd1kL/kQql/xFTXVcgMhJtrHELBdVlD8Dvj0sn4530yOf0TfVmUZJ4pw3mDjH+efEuNa6O0TWv7iSaBVfbrKwWyYmuIj5TCL+gDE+2ZrKuGAWVpDS+xuwlwS/c1gD4MExkVjZlpt0D2xhtQzgEJhSaC1jrEYbzWGIMabvN1ODBfVIfxZ2CmgHxyBlXUzduNVlRhijQJje5UETSpBRWvmGCvh6OXLXLYFotf2cJx1tfy4jHEDeRjN/mCAMXl98l+KyjgcO0Aq2VNTiv/qSjtOiUDSrS+Dyclxw5XuX/WpaVdSeMCUko4aAgL09UtzggiF4oire2NBTwfz/S5wb1Kj5yPQkPhAFRctZJcWbTBKGguIjSI5JtCSH0IJBO1dnHvFdn/Lv7HksbnkmAoVuxVZiEtP4ilJsZ6LOjmm5Mtw04ztOtYlXx0YOXZuN5KzNA5TO0DIQOBsuI8/8DxSRmXhC82OKWlbl91wI/0eIBhC0R6h+XJRaIilRcbnFGVw0Kmn6bQV1z9DjcPI/96XUE5paXvi1Rsnm/yXylpxyEYcezkVuhmjDA8pDs+F0cnJgtAPeRT61dhw7oIh1Vj5G+nUbYuUdZ7CnZKt+oth2k/jX3qMrXxM8pZEzr56jxqj6OSpAghpjjxyqDsSCqkPRx/1qgTAyCCn5I/7wur3QRTEs3C/IVa6MBXUkJTrggcAaitChiW8yDuukPcCZTTgFDZYdKPjwXwTWoz4Voe5qS2hyCk2N713WWAEQKT9RconaTIxbYzwxoYqr3eEsTtkjwrNQLMyQsHg+MgKV6HdOVRqMrH2v30BtQzvutGxk6iwpIU5zy3FjpYfzrs3H+UNdNScb7WBS4c8ruOBZipIFluUI/loZx3LW1EA3Y5NJ6UtSchThKyLlbvU38dQw1nY2hMKtpkHH1UQlE0cjefOvWrx/Hu1J5Xmz81T/WI/i1mjdefyipalbW/yjFk8U5PsKJ7yGVqCJsKfjLjW4PeC3Iv4jdxNtBmts6r5mBbptX/n0mDffh/vQmysBf0sDOkteJt7zgT5xDBTw4rBMm2thYlwJIwf48kCBmM2s9nBirfHUZV52wd7fanf0LnKrEDVCu3a+rFUUlaQgqWZP2nVavVOhsj+6CuQraIKODWKuzk0xgY3vNshZ8ci0uyanMzgDe53tqhQfMIfn+A1heNHnNpTz4TdjWui+GkSGHOWhSwtL+KBLm+BXPSFdcspi1OS+UE+QfyzsFSGi+Jzu55uafQIc2SehOON5NuWsaNbt4hXrCrh1GD6TZLZbRmFB3HZJ8BRyY/5bsqaYkzPvw3DreE0atMylHHa2Z5/YMDWicBFUvKey1jKBnS/Ay9MX2ZzcSAv9YPPdTBuN/x/3hDA6+OXJHhPLosSH0ursHfef9C9d3xGoC5LAjQ2hRFwB4w+wnAn4lVrnhhoM+fdDu62ZuknP1SB4e1VIvfMDM11wO0Vf9fII0ovUQAb2I2vbfsam6Agg3V1cy7otf6W2SqyQkc/MeYcixRu+g/nRKhVBIA1nALZggtJGIlU8laV640x9h6dJThiE73ZkCz9nUFxAvXOCFlFylEw10y5zU4qz5qkOoJVAgWndxo/tFM06vM8XdTtUPXfYDKjSQaY4IPyc8p0kxV0qi0rWmeLIS4mUHURahZ3bQnunJUHb9DEl3cgXmvflwmMqD7gM/zznbs5F+Qo8lCfrorGOZaEqHCsSI5J50LBq33HzjF1+2IqNg8yVEmgQhdVQe7lF46yJ9TxUHYsUCZWK/bCUH3CyOsJvAZmhsj74baC9S/kgSBryozU+tsXyfPCCobNOBDe+J5lJ5KcAkaaLTYFoTVfWX/Um30c1M4blnUV4W6aYkmbqpTRZSL9JQPS1er1kQdSh28ukUhHzVXNU7Q/+GucjoH6R8h+kFLwp6AxT+8yrEMDMhfkueTrcGtTjK1VYNv7uDE85HYtJCjdXHLlEyuSxjzePvn2paiuZrSYxq7a9QTX8JDRVcx2nzSw8baT8tXpQCd8ApkEIj+lFYJcC5nqOrD3fAzlins6DyztPbFAPHuGxYTDnz7XXFiaWDALP5P14pTsRvx79K2LY8dP26v7zutEdHBwvjx8gDhKvz/S6Sr7l0NER+aq5obKYu4Lxbrh4i2NaCpugqO2dkGFkUAF08Hdw0heMmfp4gTZKsOuJcrHxcSAPKkij17366L4yw+RYw5pKVoFvP5v91zGUqI42ULnbico/vaaFLLK2gCgtbRVbhKVUDOZdg5YkEvnNmy7k49EqtvPuKwWEDZSsfrun6bwp9UHW9CWrFvMByG7Uq2/LWEfR5hUSh9enSdGcVh7L4uUu7VEVEUDwNZqq5nCie2IKzGzcN3n2mmn+Ci3vqTdtVnV7+8mEttbfN/4wukTgHkXonkgSPeKPjgOZC7lrf+4Yrmbnb1CDdU3CEH1Yo4BUw1cuxlnO2phfTdn6+QEQpIDepAZor1UMN5mQ0VWXkgDxh9LCVTubD27G6jCHoPGQM6pbD0TCoZBpl6rELvIfeIKgSembYzj1yYn0azugjRkK15jK+f8XfAjDp5oPo9BQKRcA1ZgU8wLV/cNCxsm3ijmUrJs2sYr9ncme68tmvvdpaDJ0ZYfObByWNXFgX16LR9lDEZTsSb3R4dFEDO4S1CFg9jQmJtBjYRsNKKgU74wVSO52VNzAp+weqJtiESRWENwEszlTrGsI3y/TIZNhK84wUrV48BPSCrdNUZRr0tyE6JwiEKtLWgGt7XJiSrc83l+gfjLhLnxafWm5AOK6UEWvDBKv2uLUAwepbUknPn8wSGw8yq39U6rcjo4/Be0n88wdpU9SZoNrW9vQYXBfoJqtZmWmTRooF3g04isGgVYZvCS9qGdC86DngVpVK9lfkRbd5FgMN8mqzLLwuPx6zZ2BCMx1roxp3Bbxu8OV5wxxfwChVMUmRPBJaJ1ugbGgfTO1cihRZtNZrWgsJ1kgkiJYs7aWrMWDIEZqvM1vWNkyAZn1E816x7zZSfHM2ALy29sNVAINMb+owZpFGGRfX0eRY7xbNbnccU727dzZjPoLFX+PC4j6TZ7swB37xxrWynfceh81i/JTmqWzAFW9UUuPbU7rkstzJoICg1FyqklJpHPdZCjkpvXeBJhmcM7rjlMlXFtJC7iov9/bd3G7sby+aQrbwMeK+l1SPbzd+ZdnAwQSziulV+Fc8hmkeqGR3y3XYdF7OtFKvvrZF46LL9apIA0UcMa/tsdEKmUS5W5tuCvzR6ZUOYKZod+6x5pTii1yngG34OReJ4mdYmIQofi7xL4U3771twEO2XqU3wLVkIOSTrpOFQPv5oNeSMLumygEGEC+8CkO0g7TGMIht+SD7zuDnKdkRmhqCf5mWlAKMAPwkALnQ3Wx/gUG6dSkzPydZMCtslM7AisFuucmfg9E9QUvjpEsFr6upLl6YCyYK23EqVqwioLWcup7d4Nr/Jtvr5UQn5dD1aUjYzKDdZ0dshZn1wQEj/F3ONKCQxR4h/OZ80jNPEH0ER8NKXFyXH+AbWhSexKwJsjt9N0aUcgnqcEvnU/VEP2XEl+fkPq7WXmeuxkuEuUo+nhjJjX0uWu3/5UHE3KEKe9br3s+x/qA3ZwAZM0FBsFAi/8zsPF7nED0vdbuycidhG8eYz4HrSDA1m/eJd0BLFUZr8Eopj16Xgp1JsAQp5Ol4TMIBxwlU3lyhwWS1NVRxHwssFdKfsJW9zydyFtdltdUJ9fLptQuVshQC0cZ2qeIkKb7MJdTPfy0GGAiYKVyPJteWrD89WUZptlL604aOILS2jZ5EBBIeZGvetvzwwGDgIPQria0wy9/p1qomUy3+CXVduM0FJ3SmSy1I+GQwoXHrc96VdcT4sSirp1vuFoqwxmdGDH3EDqPZdQ1p/6+Hy14zpj0Sfuvx8gSzMJ9eRsqFLB7r7oXnRp359SJiTNosznEtqw1BNrhl03R/G4hLENgt+JuOfxiHK9CO9zTOUuYBl4SxeqD/WyOzIjjZuqc1X0vuoNn47auCWAnsjO2AM2Jqn/aYY01tGnJFevUEwC4tt9tUrPBymu/2vElDxKVUDm3Wezu9evCkfH6YBqdmNMcQ7OZMP1OXzEWU6IWf7wCMgzS+MFKbuIx+Sx7Ii0bCXXemffts+VUUf8/5PxryQk4WM3nuoR+pZ/lgYTJWMyy+aJeCavf/JCmDHaSnm3xqwp/uZoeon59mP0yXn4NXHZEVbOb6X2BrPzQi+K6OetRqVn29XtzOlS0Cr6U32ryob1U09OYGryXwDE6AbdROjH9d5GFxkCzwGNIoPzPT42TBgev1j5GpEw4X5LhUSnZJFVG4l9wYlbba6wMIGQQAlcOaed1hZxNm3sSqpY9Uz9N3hS9qO7G7Je+NjpUxb15N/iZLJR5wX/hDERkV6a6XYPWELM4x95/bJij1qZcfJG9zzkdW1PZS1mAU7XlzYKD57lQO0h5oLU/KVMH4JcSlwucn9psiddn6mwqsjGiNiIB4oemq6PD154GZVHqgrx6zhRynQt+nwREvFEx7wsDUeBOMs6hEygWhtkePj1HzG1RgKiArIghgnE/JmtGjedgQ2tkfb5c9Lbu6wmZoFW2ptoADU0LLHl+PLFwjE++x9DuW+yOENX3cUzxKwPy6ka/iyUnAHcFeBexbxXu1lzA8QryWwkTrXAhk8dk7pQgUbEgBJ/TaVqPW+nm51quv6EAwpR4O6QEsUOBuvYm6/AyHaSR38Ni5jE3QgELWeiFM5wx1Lt/xUx36ou4/a8tZLFgh0ILo7esw7H7hklFxpyBRg7ao61fkpQO37GZ9ghpEZaAVli+D9QzijbTVbzXlhIGjSiUqK+7h26hc3kWWetYPr7eZ0Wc8XU1Oml5UY42sOHsXVuJ92alLF8TFB9DAtDp85Ea762Xnlz8oU1PfAJRaeuCqfHQdCeUWxVeRvIEq8YUo2vUpTLBN2W6lyvSGm0Z7MDLAzZhOvqGmpwlzizv4HA9Y6kAf7Eo33NRkVk4motJAYHzq7HljsgMz37m+ov8knYfoNGhKul4K5xFz6p8b0VE/RHHOq/h9j3TExeNnl8rOlWs4x41OzKb43jEwDRNq9wfN586P544INqrpkSq0J+18IH4EyrHEAlGT0sZaBVi4JgBBfu+KoxJ+MZrBvzaIZ546KW07jcYnBKS0cBLD1y9he2VzAQ25JzvT34oQjXg94Ul3Vvccv9GAhW2HcHwE1XalQNruUpeELld2JYBU6yC7kY1Hm4OQ4dk3tuHnQLnmkUMtmMwEGoxLky3fixo7DrNS4Iw/iSFU6M8Leh8ACsfM1HlFp+W1+ZCualZGA0QHl7B3htaBVVjZxM7QIsuBRgg9iP/DYaGmF3rrLVQGbGoglTYQ4AnaW6ueTlz1wEk8ckFoj9fu8mVqPtEXdG7ZUxTpOKdTkCbjUMi2BXrhOlmhFcgtfbBa3If86bzIK8domresZliCBSezYWo9pCJa9TBygIz2/aiguR/VCItiOnwTfHIdbnkZOHJ2bm9b+TscMWFGzPsxt2XO7QXhwl8pRMYRfjat34HG3HArd2owdH5/Co5iPAYjvFjoKMirg12BFKJxjdwic8da5SpfTXr78BdCDb7r6GR8ahO+Sdkh6cIFVsWhM3YWApVFeIKJNtqZFEry9wk+ChWvHu84HuReyJVMWxuv1nEQRjM+E0m6lebOLRJPDc75bDGzaG5Omsi8cLaga6T5sWLiKsbp7jAfJkBZ6/twNTLsrJ2COdXpypaExz3TFBEqSd9HpbgnvWc7ppR8fyRslG28wm1LGDtPgPhOjJ/DN0CSixHe2odjVkyddvi9PLKdzfnpSyAuWmKds+eko9ENmAZnFx9L0+VZZIYIJYu7DHhVOFpg2aB3yCfBFjRHc7vx5jsPx3T+u54Sg6FMBo4MPomcFnh3fjJ3ZxO5CuKd0pfcy4hy4ICo/m2lr+uVSVMJPMqw1Yy/jxBcxSjRS/yOl88P7KP0iAkoB8o5AG7Bu0+3gvpqBDy8Ll3cTuSWGZ9FA7O+J4KZz+sKPqhm8rOVPvxObZqMkNiOc4D9aj6QSAzHKgeIm0uRDFDvJ8fFoxgmS99YFydyommKodJGBSnrvSGDQoTJpkMlyxVZS/H8fVzKwycLb9zzM+wjEw7apFbGOoi5pPVGiQ+cj04dej9qT8VFGEw4/BXUWMlTTotpPnY79YIp5PZ2+R1laC8fHcwDEU4OQTUntxfbj0QbC3kf3YvXvUoy50KcuYHXsWRK5EYnoNOnmtf3GekjhOHXqb8EGXwxdFF06N+7nNoy8hmIiwyqZLNCbNMW/F7BJTOuiHTwzYSmdsTrgi24bbmdTvw1CoVrZZn44XApya08KWehs5QIrGtShEn6vBhc5mTz0F0hM6p02fAzAjmUzHp2fiB+fwkJClt+Gawd5WiQUyA3TlC1g7l4kTLpKSEoV6elf1wwutXH4B/+xea8VrC6pl/IqA2DkdXZk+3ziK2+ndeT2tdEHVslmFh2YaLCF458GA9OTes7NjwyOLIXUkLTlggsC/OJRb8jZMGtrwk64m5OdZqB8NQtu0UbqqVszoU79K0PDTbf56VFP0wroTQCNJ6tU4ZIkbtR2SsYim+pTFwaLRWZT6y0UPZ77myYWxSyRPVnRTVPN7HjhWXw773OlCfxcJAqus9zquZL01IamOtA2nX+TDU/7s9bQDMtfjm5bG5FNU5ADV7Lgk3J32md2qCKxEWCHVlxSxfUhuD7CPV64GglMr81gxDjcWz1cRyLZN8NR766IZF/CmVOxyhxPZDO6yLtzS6QaLgQMXPxyZJ2nsirrQh9eafdklbx3P6uVn1JNIP7nu+9aRffbFgmLg9T1C8hRUeQwWZ+H+iuN2o7ms+/dPH1vH8ljTVHvZFWxlmAVdgfOk8GgLD5UOXU+AWtoqL2/zWmWcyCiXp1Kl4KOkPp9UKmWuomSubWa5ExhoC5aVnsGm6zNQGBBHclI/Jbh4F32I8PnK4WgBfsnz3TeFwtYNEkFEuiSOg+NWNoUk01Nl+PPsN3pNO7mFUxya22dh4JiEJ3J/wY92Xs7XgZ7zklPQ9hOxr8CESX4r2KaNrEcjqwMb1wPFmc/Q75xAVA1iLfXZkDT2Vtd6UcbYJWSbX7fWdia0dBd3YUkWHloSz+oLGK3bnyx1Y2QVvkzUjr3rTBj6HLEOyzVR7mCH28jjD35uBzLwUbZS6Ax1l0UolvID5E344GrosQa+cCCvhXAjSVgDAKjozPHPEn7bOYYBEybc78EXLWxSCOSzs80qM2BOEdF1ZLnj98EhvejaQ4s8QO/VNRrRejpIX4rE/85w+tgC0ruuivhzEtEjCS4w+1/fflzyxaHry0POiXtf7YJEv21BxIzBNjldnVAV+y2I7Q3hzdB2fkH8ts14e7LkcUnD89ixNQKCrVdu5KpMpJcpfcFWXaegrSsYUvBJHFXQnRzj6cx4HFO8lfWPvNAhM5HsV/LMfaGoAv3o9onR0eTXjX7wrpF+1J/V9W90RkqxIyqBL0sccnNvhHRdZK98Sdh/ITUGUYmaq3pVYyGNyyc+flWUvL6GdXtCB1Tov3U2lLaWr9UNzLvbNwrAhuSJjr0XDatPB0bJDQAPUd3cjyXrAR+DkCR1p0riXcJk5UK/JCeLCRs/o84N07urLuxYA+Py/1PZYgyd1ewjhE8EyOrTKDXRcWP0hXdlH/9Y1LVXNpZ9raW8XKQHw0N6S3AhPmEgclp73FqcGxlPGJNaJVC7BBprugwVm7T5bLd6SuMmKFPqoOSRdMJfINXPa6jJyJKiWG5tUzRsXz3WVvNclAV3yPyO5EyIVoaFWEX3e/1W0FGfKy5gHeXEBAUoQVob4W/jSjI327CAR0WpRwNark5pJp2X0Q5GaCM/W/dLB/GwWLHKckp4k4ClV5NPbJvvqwK35Zful6kY1xtLnb3jSAMSz7SqQsCbNnS5tX6OdUDdPhrYArnD2b5jTWmnBaRlZ5kTl0t42/CsuuJiUQJwI9I5o5fHWEDsZ6WGKlQEHVdJOhVfVt9ZTQCa5vADB2FKlK/Ob0LAeuk6WGKT1hywxyuwtTbnZqAFsKixRvXw5/WttLS3QTHz0bO3lKFpaQl0aNzuZHXeaIVKgOuvgcE3CV4rBzMQpWOWy3HkJxbBCHyJfccEcPPmlKvL5ZsRxKh57JKSW7WhLq/367fxiZ0MFFpawvyi9q4iQIZlvQtvi9EG/0m2qyF2eej/e/6sVRFv8HgqhqbbMC6QiADqidPASFTkIRP+f9LFe0+jgMSLj4crTxYmZ6hL2UdrRqo0EhDLLopaMoI9C3zCzPa2oMWpIWVPz+FWdAfY67nOIcNiAvYcoLltHcmJ8/+r6kfzAJrYDJgnyAy0WQbLm5ptTZ0wxXV0197jh212Ia12oH4MCMhwlJS7N0gmrRzCKsje2o+Jn/bLzeY78rJPTdp8McWnDROG1Kqq+1pMQVVmuY5SiF3mLRlu6g5X4iq0qFcK4TxZ8WlnBZJe6hiilrXWyiSTgFEsf/4FJmpDuBHDxfiSDWM3eVELIiQaLUubt1qFAR8Adhphhp4l3vatGlfiG7Uoe6SAPbSuN9C7PTMcTHRWP3tKmznlMfcELZh0bJy4keqc/4zalnUNiyG9+VGC04DS/h0zDeGIGc6GvNoivY85V8jLRIVds5vH9I8IsZCYmVCmJyxFC24y9BUCL00KnAMIdgOHfmfuYUcCYVp4Hr9v0RHLIsovULfTewhLXpf0EUwDNIzYBhMaNhHzaTzdWg4B2X+k9KT3GxpADOLeXIA/GSBEpjc009MaIgVpBqLVGJ1gR/1VRmVOoPs+BypcqikBp/sSneib2PfZV2LIOTIUt64u0MbtbES76texVnIZP100jS2StwKDLPC5puN72MCAEftHfpJfJNnpegsgF2U6ge231Mx4H12YRv2ooOLaUvc/xb7ACoz7GrYyeCTBaVqP5tHvVeFf4oD/SO2XURMHL9ZL10/MNu6qftlIiZSGqkrjCPx1y8zc+NYr9GqeG2MWe97stUHcqfcGI2YH/by7TTQvJF04oZg/+5DXLV7uqc7ltOL6aILkzYVLDk3runJyptjhzODIwa0g9DRVhBaAxD7Z6fZIRqlm6YcUpMBTOuXEadP6nF2/jTvB6Ln1bZ3BKNxbx/ZJxSUnGpV3vQ2IWJzj33WwHwurVth6LvbwzsexQKvk3vCgcYPWeED2aZdFQPiuARu2rkJRb4m17qE0GGhYcOOvJtww0jhyN7cyDKd8aCtyGeH1CTOa49yaXgUK3L/pbMEoiVysnldIUnCWv0wcUyXGjRRb586ASKLwLYaMQya27ee5tN/mxaqn05Yx3RCJM3gCjXWT0DSbFtfvcC8JfiqeBRECMC5keh1FkMuaz37WWp/BN0aT9Bnh8vkyOL2cwK4As7YplKBWLzmfU3HUU313nmsI5nrgPyfwxIDka+01UGreDo9H8ay8im/+EUuaavLMzI39MwUNNKPOAc9nxHOMVK43pq2tYE+glNxNZGV1/GHKjJ72JcYwP5zquZBXj4/HQQkvMRPl4ru/jX2OAXYGOQVX+9yf/m4jisqcWM8meH58M3u5s/fDeK/EgbbNLZU3CqbSuGImmZuBPUqPx8lTe1lJjI6PVYU+9F4OMukTKycIoib7hDZhosM9zmHDhPEtanLKVvmNnnx6HcPmW+8E7r/p7A/TDUCkUWMRJyZwV7dVTWvvFJuyqo9UQ63PZcSsFXvozZbmBt0CArlCSwl4ckVpJTVr1FlAkJHtb6ZzSJiqNHUArhPuSC43x8P3iflJQpy+irCCC3q44h74a32ltczVj7ynP6/dNgsOGxSyxv2WEOjSffzFL/DeVsRjUJbm2uPMWjxepsxIPs+jhugbbTL9B4tWtpLLkldfjBuwzjnvN+XZn0U3t+D9bI6xriwWYgWUh7z3BF5ro4CZ/Rod7/hlwMnJ/lDKBtVCbBZA9wyt/WT7R5CM8fWhWBKIwEKmLz9ztlCVi0QTrtqKFpsxb471c9hetS8OEzT4xMXzNIuFTtSigMEwq2mKssonB1d1KV0aIK/x3euSX9Y3Wn6IrrI0cu+mGC5mCiCjLXS0s0VdptX/II/2A2Fd/yShn0697aMis5Qj3PBb7VmggwG6dnaZQTdVuEGxHF5smN00rln28z4X6rquOx2qDZqr1bD1XISN57alG8DSWy5Vnc3HB5/2RETPwXjPqoTmfbOjcpHM+DsHU0QZXG4CLSbXEKT3sJYf8uWMkM99vSdMCQ03N73rhb+yGlsi2QcMingiX3UNesDoxILdawaT0fI7d+351dafkVmVJJlBLaJtZyo3EYSpOO+0cx4XS5Tv51LGB7RwTPceIe8oN95zGgLAt045toZ/ZcneXJYKoKJmTp1EVUOL/VV9EWTYUOxJYspV13ZmS7z0akIauM9df/vvNuC8mjz7odriJt7xFTVgXNpLvvqurBlIUiH4l/mRe8OASTd8Mm9zLg6ZxpOXaWI1v06BCP4XwmpDH37B9EfF6a1wYtbu09P8LgymxXXuiHVV08YM/n1HRDA1fIPPMgHXQCG/KJJl7hPtIw/ujWTR6nEnja6oMvNmHy7iwUkjcYa5zbrD8Fd4maXGRo1RY+uGthgVH6ZMlhCn4UrH8eWaHLLzHJXbyGbi1FLauBmr2i8S+7frdtsMqbQDPYWib9Y3JoT6LRYSoYxgsfSRWCFdcgn75Qy9K8AVsPrj816qNiLOVUcqsRRoz7W9slSBpFDvpNbjBRKNtREPD53XSs17jK4RN99FhPFFuNOKot66nr1MlWfZGvdTv8OgWlg9aD8Wn+OdQ2RjH/K16YTYhC7+JAvF0qHgbUlaeE6grkay1SXaegQio0zcEUv6/eZJmdnFI5H8iGxtvSJweH17TaWPWfXVbJyvQjEWUQxUj5Laus5nKG+xBb3adEJc479+v5A3NmF4igPvNrCUDVHgAvY6aEasnO5ph2mglCIySktz6L1K+AJ5WefDqjtnLH1pQbfXngTB8FkTJyxmqE9pvP/zsvjzme5U9z8Qx6eUuagYAiw1vHc31tSqTBhUza5ugmuB1T17GgRACQGMFivqwJynU+oOiMWL7dt/pgUyK4PQXmdnlcKei7A4YqiU8XXjlXg1WyHuFHHMCEZXHw0QwCdpIQpDRoF5ODxM85ZxEzyn4ACek/Knt5sXmcgGwGhs0JWgM05F7vXnvGzs4an+/QBzPKvMlIVL1hCU0b8jBB8iRsph6b0su37LM4P3wwY/A5jcfjM0ovdyPb2og0ml5aYVReRVDrRjSUnxmyeFmj+XVXfgfsIJASg/o1GgJUSjzCLv7tLtPfeU+lSctQePgktqbR0Wqf7InrGkvGz+gIXk++52xCpL+euw6+M2vpaSTz5s3rmRj7j++C0scPg9BLFpzw6Imxm96TkVZJWPRnjl1+8uMf1+BbieH7cYpkdHpwdKMd6lCEgFE6j2TX6PdysBZXAoIXbVvVAu244+p1XyHu4phT1+aGusU2/tSLOPLbMFUK9yYU2xurAGaQo8mPTDiivDWETXxhncNxV5w2prfj03UaFUMxLjRu+bqaloN80jcAnOnJ9aabVfmZqQjhbfsLaA6csJ9BI74Rm9Dxocj+FIaa7TA36wWBa/3wCa0D+wrceMSbKAdXKZmiW+o6+UIMnuKWa2ILR1FGgg35rUtf/y3Mjd2nkkDvTFK7VP7CbFaMCLnbtAv85zYIKY7S5xFN1v76itkoQyCN/S2f0/Eix9/coVqTFYJj9plFiTuXpWhWiNd1S1/sxOwVXvtBKX3Ufb5tgEbqXlrrt/PPVe8sbyHy2AHUAjgIcNs19P25xE4fMCrRmpGcuHa6/xiGGPCZEr+fYTp3YQ9nTU+5MQ6dq2xNhvHkIlIlGRlZQeweom7bOzhnoOQugtqMBBen9dxPDbyxRWKL2NOVR/bx3cNnCf2yvP7CZ8150fcGeUmhHCzit6yEonh5DeayiaUIML5S3ls/x+OBJ8pJkdCT49yBf3d2HAnT3hXnSQ27sMIFaxPMSKWCKdWe9ROyTgF2/rE4j88AXFYgxne+GZ2A/ia6E+5juiRTEJ0HyUno5RUYQBmf8IsjPad/kBHSgzJb3SxBGWCn4DwNu8KbSmNJouaBEpm8Au/PSQTkdHTYzpxH2WZBfh5vZ3w987L3C65oK1qwNcd23Csx7hswZny2rnT41cmYPOfeDW6Y3beX5IBZzodYl3ptIMhn9qDZL2ITxdvKe5bj0+y9JbFhdu/UuKTiHi4aOPvlYoZYDxDmo7FKqWMhDGZFKm6aKSRNNhBdT0TaNGq0GnPTiCXH2cy/v/pRbiE+hWPTU1BSEY1xUl59BZWW30vwvDFlowmIRRPrZDbC1snoPiFfIy26ojChTwnJ2YhawzDMe/4INiV1QZpDhg5P5fgdT+Tig4ncZyXJEDgxQ/FWP+0dAyJxg0cWZJnqzK3S78zjvpr5UbJDP3Vd/pDGshEY7wy3ZpcdKNLk1MNS3P+cnthA3GWx+NTRCEWVaC0tQ3jxPFUrs11Mqkr0dUfHFVc2igrzN9McxZSQgAcQObfQX0Grh03ExE6cvBuNdY43xCys3sxWq+uTdDtOgnyUjI8RJyQBkbuH7XAaBBKOzm8TTSSjUW8fuip/oU6EzqkTQ+hukCKGcEpSH8GVyjjZEaWDDbpOw84DUjSWQv6q4TlSaF0pbfVH981brcbVXIlXYIArihmuQ623FyM/psE46xn8Pa6eCUAbio0fQqBRla3J/xiCjmF3itXOxwOxaQoSQmK0N/zSa5rN0orl0P4rLt1XNjT9wsNXc5ZI1Xfkxn0LXllIXdcLMF06iliFMGgjqpsaZvW4Gqvrkpd9sop2S05yrO6KfEPLCOogZAanNs5+GzQjwFnnsrRfxURDikJWPkk7ylQ13k0AkA/fKdObJGxpiZWfw+i/8AcOWMOpzC1G7yeWKrpi3rhTCg2UXvfiuAUeNonaI81qvMz9oa4a9TwoBzzfsE3F+/kA7pJ4as3CB+DN/049sstKWxnKbU6m5eDpAZUU6awhBc54i2X0CmtL1QlELe6tJ/KjTuMQKiDKlFJGo8/+rfeH2FLfFftmJgkbYmqAb7JxJBqK6rt2ysrHL/SiJ/3FpBdeu80F66TbZx9cZuNhfQ3SdKS8mKtKO4ZhQJqGtZhpaF2D8jzi69lW62qzijrPhMEgJepUSOlGnaNfebwjNFvG1pdlynFhc/5VyHAXmGU3976RkjJFQz/K4GPA89j2VONK6EsTX6rP5kSm1l60mDn6qvLG8dC1vT/92qUzqxRmL5bWA58ThbxZIe4t2Azn14qd77gEpzgfzBAUwrE0NedovYLywPK9ZpW0fw4J+CeNAkicbMnRLP2faMMQWVx5WnxTRK0BhJfVs4+syoTANjO+/3Lk0ycQuJtYwse+bSY2Ic5FNeqTRLcQrSw0D5vUqO0q/iIqOV3AMvOe9bYPmQghlTXWQSgNAiI/ps5RXugPVr+ZrA9NPu8ezjZsWzA/Q+xWKkTL7R8UBSJlHzvkHwO7fNz/m9MIhnayPHNV9/l5qcvej4Y62UozTLd/0wxv6B7JLapzQ+QYECakRs0q6qSUVOGn3IrFy1ZNaRdnUAil8TtJD5NRJ5CpgA+oqN228Ddb0HPAz5ianxxPU6FIfplGYDVkef4Xag4fVLBY3dDVprum/UHoraTJoMAeFQQaaTJ7je1ZLZv+JJaNvduR2R4UN0rC9ebUCXPzflh1cLL01nZPOZJZdQxyTCUnm4k5exiG7WTv7QckytwAeJVD3ZSCbfo59sYzTVB66D4UoiN8Hs9ZD5yizLDyaxzXU2NIR/cjYRFOzXesek0C4cHHsTxUmdTJemmyOaYxlQHtvbMSrbNknOgHGxn7T9lyFEGMsby4P+416SEFGpAIKXTebczjN4/782rJepEF9yM8MtDgyoN/h24eeJUlwkgi3gaMeaS8hiY4zkkeXSLfpmOdjo/YLegq6SWNSGq7KDpzrdSB0B6lQhYnuLIFxtOL0OSujxVWnwtiTOYXCAaeoxyAq4nkH3iB/5sQ1L6fEuNmdSnHk4uSsWf5YQZ4Ooq19whz8r5PCZJ6+ePmoCjHsvSazMMmYA8sKuM4YtrPuduBUAy7BOU29B4A8WsTYNe4OKLc145iGIy8E/y6WQ9FtjIkVRd4KZBZfupJeNg3G2+z+cgD7+BAollPunFLLLSkTz52j7D4vlTMrdSLokL29e5TGlUu20ae/9lxsus2bsiACiQzyBNnUAVedUDI58fUzpK62rS2TlxzVNh8R5JsKKUlfY6IF8peflM+LO4Sb6ly1bqYsGcV22q/4N7lMITI+1RfZj59OAvgFkwsSMKS7ClTli6e2bZJVkF8cv++sL05wWHkojrQz60LhjBmjd5yz/X9/9huBdDp4I8orlTp+5a+LS1K/pnhih8+9hpC1DnFJFdpfeUXvgaWFHRaB/JUvd/LQ24SDgy7XsSM4syoVEBCXY/Asg9yTCOArm6I9SBnuQUnOVJUJsbso6iv6GdSdDco+MB8Tw8nSg4QhVVeNnh+UyGUarqxRQ8/5GixcW5IbpRjwCleq5V0CR+E9C8VDtbp2GzgswKVN0ASLgKI0/p+aNhOoqaaBjsrNwbSuQiBCoYRNVZMAzHx9cnhqZ3w/9olK8JAARQZAGy784/8jqUml7W9ZtgaEUR2RBTdXMU9rKRGa+6wRClfQjImzQtn2EoK0yMjLyYrw6HaP0kTQkyPUCcuA4Ixiy2UVXUhsu0sum2GJv3PpNsd9Nc1Rvx4ZnbOe+IxFi6tPOo9Ei1FdLKgbldyOsXzNhXiS/JT8tV5uxJKrnSq1yR+X6/c3c0U+Gdu+MNpUvAaBdow/4qnWiQrGIqY4b2+UD2vvwQffysf02hA5ePcReTFZfdNEe1avMAfObwPfX9Jeg7FmPqXTKP6u3I1nb27UXpJJ8TYNL+/112u6a9l6Omjqhom7KUhgAMpUABdsgWmkV+d6An2Wsqhb7k2VCzmuOMrbU+/Wfn5gEC63HL970CBPGe0gmGOZs5420nlrs488fG3vfQ6qjWOxrq//OBWdkyCwCH66bwvJZcRykJoI3/EFBUL4s1gZnqFBpWaEOjNHJkmyL3qaw9Ffp2E6z+bNtlCwF+bg9tasGrnpw2L9fAyeAptZG4th82FbD/r5nZkGkGisTvjLHT1CX0I+06MD4hxRuZxOPiW5LgtMA2jrd69L3L3dmNEc5q79b9yEIsiiJPd7/s/WtiUsgn9dngnxly4dn+cME2MF8wmilSt5IWftg57A5LrXMimH7DO5KTlgDS/01QEXuchExmYZFTg4tfK6YtD4T4THWy64E0RExyfNojpD9EjtcmuH6EI29htOyE2lHolubv1ztdXBF0zp5ZY3D3yGO81wTMWlHRD7DCLDK6/ltsfLVyaMQUKb+7pWfhfvAd9xuQGIPq+IZCA6vnxwSco20HwlR5Isb2n1zrzBBP7VpA5H6MbcpMDqOUa2+jCvzBJIOuk3fhRoIM4Mvduo+vtgO4jWlrK8QlOwZZD+RXYMdZELdkGta5pUz1BoBlqIso8c19xMakMbYFAeaYN25b3jYHBONHXd4nrORpAwcJ6tdlWNs5fZ4SI7xHYc6a5Q9h2RSV8vdJ8jd9f+LQPmysKr8ok4uUjAjFVKQrHxtX28QFJSbM+fsKrAOts5gwTu3ggntNRAu0zy8tOjXfsKcAfT1pC2X/y/0Ypu3t1P4XSVOJ1xy07tJc9U/G20PzGCvpejUGyxNdlSfMyD8FdTTce8QFKMlOy5znsW1AcO0BY2hVdpug4nTDelcyyqSnGEXChldKTdh0gAQy4911YpA5NSxC93it8o198TmRXawNUV/KzmU/LF9HNjtbLM5wIDPXEy9uvq9+gz8pAVpLYnzwUK0yIiWh1DaIkEaD4rSr8GVknOq/KmQh2l++qxEL0XsG3vMPZRnrMT71aFzo31nPa2EzTh8eVbOnd7so+mKRJmfaAp2eAfzhzFN7jgw4UGHIuVSi4MT1bOJ4Nn7MJZ1tOEhcCLQj6SXU9EpXEqLUiAF+4rmMuh+74UNaOkUNhEw8PX1nyvYhqZBjXlMl+Yl6rCUX1GeebcSvWeIxdJuEDwM0kU1mOg2XeKJk3yhcDAQvk6POSh3QQ4hFNiQqpr2uOM7HNua29U5VRfgwj6HOZSvFFeyOjpRp+MC100yQlaw3EPqRvoHm9FF37o2USRLNo1taZ8LcCzga/8pGUs7gIx/oLvHSnanr1GDIuCTqrYvd0k11j23n18GnHfl7UHIEwUEBxr7568P/DzDfzCsF8umPruXs8q9PbZFiZQ9HUjFyFuYRzU4ilONNdBItj5YmW3E+wAMGJNDcyzn768U5d4IknrNm/Iib2ArzSAHR6S3a1K7sLK0YEKymAFHIy4GSg9yqyhcbuN+iy0elKalJBxabqPvLlCOC2rDki/tLlVpNJUr2hnEFpWgCfebnhmSe6/OKRa6ITMnq4gq2tahegMnyRoK3DYyGOKigMtR+Dmd8VAbHo8W5Fv+xVJm6Bo/uzVOEUX6OvLrSeiEDrcSz3f4k/WmykgGeJKXu5eq7X+45mqPzjpLzWDjgo+xDEroC5L2OszDBFWolH6FJX0Q0kCfrt68DLO6CxF99egLmkd3JENJatS/nDyKSQA9et9CmqvvMzrub5yNT1vBtW+cP35TjKXTm3p5umTIS1KJKxjtIrwnFCQBbbyQj0a23wBgij0xZzFGhrENiKI0aq6wRqLmm0/nljmK5gQtquLmsYhbXc1V6Na7CywMxuuvBKcheJl/ltdlwAt305Ccqiht3HnkLTUYTs3xDWq/jyAXDIX8jj/ftfVR4tyApRF7Dhl44e0Eilb7Cn6YvBQAGtyXYo8TBfdGGJ4W6obAqY3IkEShHLp1vswAeRyWNr2Gv6ckQ4gVB4zz0qfT7jBht+NhDlT+HI2rIssBJGSMIcn87EgUMxu798GuKT7Jv4u2xobIK6Mo1RFVoxVingQddNNvrwJfQ6GKVKQuDjG8bAsGcTvW5oamSgAqZCE+2+ftrkifcpnhYHh+TqaI0whI3ykrqJQMrZTVG730Sy8IlsDtP5R/8p8QZ6do09kncyXW6WLvVH+s9EnV7up6musSqHJs124qC7BLERG4YKf9zOTfixGjAUfNSEiBAr3AE2KbMs+RADiC3KxcwSpQ0LPb4tRoD0iTsUhDqif2I7bxoQcBadVQwyTE+svPYmZI1mAT5msaBaYMYtvXfhxLcMG3xEzQK0iqW9US2fDG/3RdAJDyGuD/QGT2FQcpeAk5ipJIWd7rUZ6BToC85XJdEOFlfGWKDp4xX4ky1zod7wPbkTD/VyA3JfXfuYssD60e1v4Hp+jrz7RDGjRHzpgtJMRfHC11C/1CPQ51CdP7RaqoDMU3foMmGA+hfayEkSt3oCUi/TxBfW9BZ1mLaFhOZ15Gy5RC1MwSPksT6qUAX0ppVUeGELuel8mlCsp4wpr7eFmoMUxtgscmN6HLBep67hxL2EUoK8OZNSQ/n8q5Y1cDuILek5leWIHnshA/lWjpR+ANN4/7ClzOqXcEyYd2Oz7xIdGp1QMoEzKXNim/ZFdRxkvLI52YoNItNet/RfhZPkb1Rh0oYg38xjcxWS33WLfXSxpVBpNneiPontYrFUbOOvmjCZWRYBqHOyuYZkbBb1bNMP5xxqin+kOKCQ7KgDlEZHrSKysT7IboDdmuxd0bIjEGj5YTKcEjsug2YmJ6ETb1tPCS09nk1MQpAgSu1OPrR62VB9+Ds4qApX3quCsILIrwUwLmKF/WBO8ZHZxzfAuvsd7zmu9JYfgsiwbSuGRo62FSGQouPm4CSoQ8POzJ4jPIDSyLVooQIXHneQ88R/Fpr4EyKn0A+3Hy+6GR/R9ypdtIxGXI6Xbc7qrvBRnv70y3COU+6pwGJch4n/oRA59SWaKMKEIIveoJaWNqDJMnoPSCgwhraYN1yd5KVPtuaclCpw90qmfhRZChi9Nllv6hLImLQdPBnned7JtnwD4rd9cCg3Bw7N/ibn3xXLisIVfMhKYdOCvIqphwofRqeL6yM1nDQIMBWAZkq9tRSl+oGvvwV+rsC/OB7iFqVYkab/qqB/84v/jWnTT+b8vT95PAxJihAqih0wH+SWj8Yvmf4MhQLwQHnIF1RSAyOz2qg1576s7xH/IjzOjIcz7dymYQlcymONxpISshbiXahDBFQeY5lpMw0MqkhvKYFaKcMwE8Ivg5THsua5tH2QldhXS6lQvo1zE68q9mxeoPkWrTlqVvIHiAE3E4bYRd0dESH107nCH8VYMg4HGKOhONjhhW4bav0iPEuZvMawXB+eB3Av0yQ7yk6kLHxnkSb8Dz5zJwwn9NDxmvVcKOX1XFwg35euOZdd6RfH62Dr8lXuR8Nol8uXmmEteZmxGGItBOZrk2wxGUWnkxU1TGzXOabHP4E2ImyCm/dzyuCTWX2iJNEnylKjsf0fG5LJSWG58ZDb8Q00UBcxGF58yV/wEyTVQezXFC++Psi9z74x2SLp/FEkACv/G9N7ZCoFF0Ndp2VLuUgbbtb2oHreKx3fgllO5bDr+P8428FTTVKyrRVOzielhSH61WOCg+J7bCAQOg7VsRHYeOoVJ2d5wuXkSdsV0MzR7lTQ+LAhW+sQSFSOUG5UfVT3VfS+ojnzOGPMVwBTKIx8Pi4SvfUbNCQ1dBsTtKW2PKu+YmI27R3XRQk5NUKTqaj6EGbpS5/ZyYsyVBxMEfSYSF1VsnhnbWa1/33Ux5XptWQJHU3eYikmzUyB36wiGUmRRE7Dac1Q7+wh19QSYvw6rfZ82ZoOUzXk7+Xym6CeqtNxc6ByL1haN+HL/Z0g7C5jB+wlbUGvKn4CHGw1t09UAnIo9yJIraS4/bhxNzn1L8jdFpuEN8q/FUcCthq0cjoxyC0vrNq/w79UK5Foyp1P8Aj4cr6igvTOHvJZZ29nSS2L/PZx6D/oAtHWuCbvrLB7sDewTwpHyi581GvY1z6CVornmhaMBaG9W7ePlUD5PnIyauzYl/0oJLIZ+tGWsCH3SwqwyZTo1ioP+hSnsm08NRa5JcgsAxlrm5RkfDNTQggQzd+NS7NAxjowelBlaoTt8Fo0tqM/ZCGNKzdd85B632GtW3dMhxHDYjfdSQTgSvsOBrp89rj/fvy1Qav1wDjp8TaazuZkGvF18Xms15IUmjW1NdUtIlqYupxfc6YVAwZRRlCMGmR5JThdfMT4sDZYvZSj/qG3tU3GO0vVm5Y1EOoi1fPf16ZedZsSpWciNsZIb+OruCi9e0YvigYahnq8wkGJhN22bM8ZnIgP7rGnTSg9rzZ8lhvY0pK6U3M1RVzzmyvbzXS7xACsG44wTTNQOaEljQ6oXZnpVQM7x/0hQeXZs0i1EHnuVfBQ7CYdsWYOl2bzvmPYA5PImpdWcqi/owvJd8gVo6h30sGcsUZkXRhUPIK+eQhNJb9lLD2pK8rK3mgqN6ESv5ykvS9lc3Pn6Htx6gvcMlZnBUEDQKG6JHDlYliQMa5KblUG8ecDD9zpwTJ37DJygtG6T+PSxau7T7sCtLFSujdIgfim+mMvqINGQS2IgwDY92I/QTTmrM5R77TG/vmB0PuVdrkqaBd6qFuP3Hve/kBFw/5HraVQQQuOB+JIX92orESpEruATHGmNHcYZI8EBEDtcvfQOjuhSIDqS1HggXVxxpMuk/fanY6ZU4ZNmI6mAGAugpoUCJXY4Qa4QJNhHtZjZ3TAAsDXyYcRgFvmrIPfzTS/A0+icCb5zx0CigyFS2wRwdTsBHugAPZJKDYVeh7EKymigBw9iRuQD+Odcu0nDGez2wec84AZmNCN9FDwmn7h9FVW1RtVhw50eKYWehqhiz2yEa772uebppdpEoaolh8/yB+ExpRBG762FdTIyyF+ScnjJ3EFgSs3yxv2KbaSF6Px5HLKRMeQ6LpyzofsaX737ZMx1c/vnPXxE4B/S8n2FYpnF8Ia4y40bvckCx054s3M9jf1cZHvNVYPWX8nvvEXvpIksZtZJ86jPWI1c41xdNYneYgTyIUjHVmlzr44sJfXhMiVJoTODMNXEpZnw66NXQgrAN7XIEIXG7ih4thX/cfxQLTOEcerXZnMHIe25JTYPprSvfbXALJMrECtbLye5Np58Xdqx1RbKrHBwIOpPxDIWBBpHz3Q0qjG1CKlZBQ9CQQY3dGU/YdKmw7IWUmF/u8LzweqAl4brGXiGyFozLmrlCCNJw5kwlw2PvfwdW6vRZA/2MyjKKp0u49c4Ui5JEsJtcg17Iz8/hi0eVOHLQ9s3BkS0snlTm1ixcu/p0PI1MZNL3bZSqxFgDHYM4+J4Nx03gf8yWlSDp31kzzuLhnUmU/rwCW4YIu/EeLT49J9IZ4c/kViD1wtJ0XPHnymtZVNxT6feum7r4+JOt2crLoPJF5S32b/dVPH65oOeDl4OiZI/RvOhR1L/clzzpUF8WHEpqKEaLtJjB/H3YH2Nd1sXqKWRnaQdFNlOutLxZAyAyDgdWYO5uiZ+83k+ohEVL5ZlQrwL1EvGKD9p5tF01f//5rhL6BDwqDc7oPUWy6/AKLZLy+F4fz3+UHxiU/etsaVoCwEwqqsEcrNIoiHQgA/NF+zLvuvtWz6G2dRHUDYldPpSMvzQ4di+X1PPiRdVap7dU1YHm/8kYo8JE1XV2vik0zVqFOFN+nlW0Ap/h06riYuYg9NnmEQBkn7GY2hXXiwMweE7kF2K6oK9twJwYNrIOQx04XMLryASlNhHzXJof13ja8O6HuDmhT9BcBs2+QbUvV44n8+Xse/FMl/DYGoJL5kkGmErROL4+eZe4EvG6pEwijI82rCISHuVhPKhXox9rBGFaedWrc9Xo4EYItUq6+8g+Iep3IyEnSXD7amZM8oTUeIdvLhzhQH/Xa8WyWV+JUjprOgy1st05vnGfVK1ld3v8Myed4gPduqjO3C2CEK94GQ3lLutEBouWXG7uhNgdEITvtO3SQNkAz8WLiGmGMFH5f3y59WC1RuNY0BUTPCKUzzxuWhJxgxtPxtmt4DYK9kAQO2vaSE+7Ci+ixCy39hZc9aTmIHss+0GWvCpeRcThD84+4KqxGZdCzPHuDatc+B2S4Kwmqg9U1MRYLs51fOHTRhRdMsQcn4UKZXU4mn7vNiJOYezm70AAk4z1treX4Of4S5XgjS+Fp6juBwP7Ab1I3nJQ63bw6FtYNg25/m9/NRd2I284+wtvIY9LzJjUT3Ee+mkdEevNB3W14Ke/EkxBghxAzvVeZXuqJpZBmo0YYeFw3JiZH7sahAiJdbKHtjxs282INTuq9mlahvXyvtu5eFeIasjYBJjHIJftrNUay/W3JGX2qEMcFUFj6zunoM1w5Xgep9EKU96pygYTsjlTjEmOKYjs2s/U08a2xYTEL9Q8JjDZ6fPvn9xJ6Ew8Xhwukpk+vQWe94TXuTPnUmmQD0KsfoxUN3bOD3jRpjZO+pdAwj+ctzR7BEToCev/lOm6r1ZUiKbH8DjeD2eMHxysNAGSe7F9+2Q7Xv+T/DQ3hsLCOjn3hmyzoUYuSPwL4xAZ/tT5n6hufNvvDy8+d1EC9l9Ipf0xPTsrUGcazvU/lLOhhAJruDJ9rM/qasXbs512pGqKFfsDCbRxHRig/D/ohuYYTA1tKkCVsUMi8w0Bs4n9Ld10Q+00Yb/i7uORthbAJUcsCaTdY+nDuJ8kRKDl7LJXIM5ZurYqoIeW1LKl+RUjr8qIpVstH3JA4Nfm9bBvgDAStMca5C5602ZR2F4ucz17HkAF1d/e2c+A0/JSknKiHlYvZoCL1C1dXDIDPhmyxRMj8QJuCuw4ZggDGcHZ0kwRy0BlWT6zeMxEu+vK0CZJpI0YFBtfRQT4RpsTZpRyW3e5dYqXXX5Xqz8cbKxHgptAipSqI5DgKGb6oXDSsxnV1C0TabbGnLtjSkDhKQb2RAv3qZzBcqB4tW2hDO2pMfO/XfINeMPoLuDRKHSDMN14zClNiCQVyX8PyGWcbcQPwpEFSpawWw2IyORluXwk6RjUhm1oceDfSlXXEPcOcshRj0A1Jgzj9vgflWDtvXFM/Ek00T9EMu3xsvRtAG+WGEFmQMqSARcOoHQoAD/5tekC7JJ4q48waV6vyGbZomSA5eotgR2viNxJ26NooxyCMVTGMdgTxIHDr4KspDjRCGuOFqo0f7xwOIx7ikpik6Ubi48CW4jyUIJ9ZuVfOwa3e1LCMTuG5s3hs4lEP2YzMv+f69TXlA4lpEEschVYoc3+Na4gi7g79XqoiWbqZOrmbTuyIDg7FUcZ5sYxLJot0mOZZm31s6/QeBG0Lu+EqsXsXosCtShIWpsTczGnW+qPAtGZA8sKLLbwmnNKLvGEw9k1F4Givke7j3o/sta3x69vghOWWg/VhPLB5NiKo4mBHuJWFSak8dFzMKll/aQuRldQEsfa+cOkhAy/e6FMWNO4IbXoJl5UILOH6oxSMPmL59waa0fOR7wghAYSVGfUCqd30QMZXcmwePfGejmdTe1H5obtMoUtZSlybzb7mPz0kNJDcSffZA5yl/5XGBSTdyV5tUBkiApw9/UWoAJTccIT6J94uarXGiEIn/f9+HEiwSYHe/01kQ75JxGGntmNttIYTMDH8nE7paZFRpbba1Wqjkk9ho1MJkTynVulpw6gFt1juucE9O3t4VyWTBwNvm65HLYbL4xWmRIhcBaQkL6HQnuNn4WfL91Olw1ODVaa1cVElJ5fXoDCU4DT9sjjz5Yz7jS1ba9P3M3DyG1nbbxLsUXTnfJo9ewc8hoWCY3EOE65OYWVHQtI6KnOCfMxOVtZExOxHccSIdJAqRccSISzDSQBiwmxA1TudpMPbneC67RfjssbE4bKlLlFm9KqOhMUITsA6J5As/rwCKJWQvmEipqOCWzVwXMfFHlfo2isbDRm87nQXtXaMTnS9Ojf6/y8gapp5UcfvaGiM6lKyZy9FaBfBN+F4eldE/Ut/UsMHFjUF33O93EoMM+Ws8S7u/vM5lMnbWcvxYMJe0jWmxAiRGoSS/zY05Upo8DM6CVOr5nZL7nfE8j/0FZpwE1oQQwitGUtCg4qbHQNjx7bt2GzFErqQpV4Huy/8vqQNlLOVD8sNdaDW0FHEWQjIGUpFXM5wlhlwADc5v7MJ1Og6h5FTOTgQmf4C27qR5MJgxIbOUVjyt/Wwx6ugba+5URxB/kInMjieyKeStXhI9ZTOQGvslaHF+MnVXzZryYghkzT/DWw+zEG08JvpAiv1rc5GWr8YRTJPe/9zfXa4ncc+8JVMgscdiEIYpiAm2HKtES1mKwWJL1KNW9TdUGKjT7gLZBlg+CdiMdE48rx78ZENfMHoAGpFz6KQr2B28PgvHDA7Z8dbl9glQVqSzByWHU4/Vyuh10FaBHAnLp3HgHncZiov+afXcT1hiYnxmFCBpav51nkiHpaxSABrQq0kbk9qqzruXZ0RZBookzlV+CplCbtS9uPEloSDHFCjwLso9PiAL+wca0AG5wQCVr9HDEYXLYDU49y4CUaPMC6Je3MObXFSFbyVENeYYuLF72CvlhNLEbjtt/K9TRmIseYSpPaNvSf8sW89ORB2jUry3FbMYtb1fnI7cdmr+4mSnHdJGCqGDwCvtg3FGeJ+DJxXkLe2FXY3MxfEIHNFhsviCn2+cMvERL4XG9BCGncm0nFpEHWTi5nQcVxdpNc7Jm2XeZRmpBDax5is4MK6jtqtKXgILGbLXRfL6IvdRSCQCT+HIfI9w7hRvUVdQesJlT8K8qQYooeC0tMmS7WPDThq96a1rAzjzaGq8knS6BTw+3X6dRUa4jMHP7oRsr+0saMJs2XoAIlzNqNeVtK2VtyKtjvxd+YhUqTVP3Jc/rrzKnakziTMFZwB24r1DTYNHykeRiBL8zP6xOjnqFPb3Ql4UCmHWA8rkmWo0jtt9zmXwMxZoztvSJMNlNPJG3wtaI1xb/E3xmQ5qZy8MPNKm5OSwi05Ip5yf1bF3iMl5KTrkZ4UwYOeRe7Ncz7vf4gl1OTsjhdXw/7vIoy1HHmEUBtzW+49rb5N9vuWv8Yt9/4M0hpMeB2pThzexQQprOiH6ydMxs0lLYGGwF/X6Srvg2ILL96PHEirkj4MRjmEhq1Xj2YZj46uI6b8ocK0p2QKhDVgAT8nhPggDQdXju8dnNPeNpDPLdXHYkRBIuW/6HtrDP2jw7RDldwTYvO6znCInwZQJdTaUUTkcFi8JyE083WKtHUGrhjUyLKeDBL3wTFXxg/AYKtqSGttHiq2xD6vwnrTwqMkF+8KxgnjWt9i3JihvW95GPzsaAed1vzKLCKwf5XoPcx7Oh5BRM8mIi1xQ6k7jcCXpaZBA/fuKHFgPRgjC5dutuSMcbML9g9F3B9HXZBUS+RRkLrjI6PV6Wgj++gLZj+BTGve4tbZjdLQUsqxE+u0iRLZf3O+aW4FAS2F9vfJS88LaAQsUyUySOdSrpKtrB4h2jWpMIvmMRUDLpDirHeVG1VHycpF/RXjyQm+0wVKfEMyHPNgX9Q/+m51OzoUDcGlOllTStlkeGGYu4m4+fv94+NAvTsRsS/DaBPypc5t03uSQWw1hIwbazJAApPjPZv0CWJG2AVwmN9OipCldG10sp/mNEJ46MLdJj7xFdc9K8mhvx92Fn9ngIFC103g5AZbVB+LPqhl2I+fMsWTif6rkY5iuxoRB0tQPiNyQCPmbAcPp3+squ6GiPaNnSIG9kTdLw1i9AikYEtfnyoytZoxV2NaBARcKLH2EOI8dbku6D25mM1Jr9gRGHSePFD1r2foCKhKoWDdcytpc6FuFIG5sWJAZ/E4I6Mvh8/N50uq4+CxKrXVbEkkkZM2iqwJgAFgRC+/aGzIATAxfDurnEacq4g+2Fpbz6fMotdOpy7OK/1bvQDHcjH1LnctT52SqsAkvoQFLpGdZfFifQN1dGHS28gD7HYi6i2hvMWpJbjRrOMYPf/SFCSZuME/izDQYRIxOijfCTtVhDlMYI/AJ6t1TU0XYQl9crsliPnmZsRThWEozgp4gyGCiNw5LJBFTtMEEcm/S/Q8W0GR4mcMXKudvXmcKVECio0+WTzu2bEfgn/XXLFyPi6guQgdW4mhIHWnKIMgIdkyILFvb4U7gmX4itGE9F0ByXE7L0Dy+RVJEx+Hdgt2OuWvKtHwp0wmE8xHMLEoRJ2ReDUWmctcSmMhEGKxeBl9kpMBL8KtlQxEjxOnhyVvqMJSSpiShEGUKzH7FQwYxdBQCFByG+KYu2Co3t2YHU1/T1bqUR/Ygph0UvEuC5ew5zOZZBmvX8doBKaF93u1slbT7axksEailKl/5Qtceva2uDisLpTH9mowBh7lAPUI2ltXl0yjlDefJPjR9PkK0lkNImNZ9zj0EEGJHCzd0JL70KY9tx62mtxuc41/LCwk8GFkVHGqHBWi2MFVtWmfJCtsibBmIS+BJpAlW9brxfx826wvzvpf/XIXPL62aLmDB8+tM+P6BiOqOt1rgRYElQlXnBUtbpYl0hPssK8PDigK61u/k6mfivWPCGcbmslTDonOZXPMKJ5di5jKD/spx0w98oRy3D8IkS9JxtzWF4cRKCK4na2bRtdAqFB+IYaX0aawKDW6kC9+usDuD+5dpBNL6Lr+w8ifZthUVkzhh4qRngr7ujwKxhTiqlY/BvduNrxZ+OZWNMwITBnSna9tJDlJwr4W3XJD0YKIKVIWeVoaN9KW8FmEGv9g3cU51RxgGAtT+VG2yfsG5nBAQsI735BypgDcYGEMR6ch8DrjHEj/ig47e5mzL48JfLk7DB3ftZLm/9km/jTjNNprrbQh55Xuo8VtaHpeFblfncZzcvEZa68tm7LLk9tP6ToS4/PxnJPMNMb3e75+H+xqG6x0HPFV3C2iIarIAdbTe2velhmwCXtAZoI8qyU5gA+e8vF+3tKJm6RqZoQtSi3im9xvls0FN09YlNS3CrGjSIx8iyGAk8m6izVtNY9yFeNB56ujONirnPl31Pvq6i+0MWTlynAwae8wzGf+O1Q/OFcyJp5tnIgWU45+k8Ai3HLFfY9o8LpCm66RceJNpSmSBZu6FaHUdW3UcMXrmhtMi172NX91SNQJJfbSNk7O8n8U6vxck7w4vNY9fwUUxGiyBNKKqxQ9OACiuHmWaUJcNf5SrNzlbL0ZLa3bhSJOR37o28pl8PluYEtmA4UQ+PgGnFx+3D+mpRa0XRh5OS2wmvgVUrCuDI/NWQEb3kbnJsg3+B/yPCj0tJFSYB9iGyd5dVHLI5/YR2j3rkBW0ueV+0zRdn3cNj7FcwfcPASKEwotomW3D6NymFgNh4gliVlJBqBVLDtNQvYRFO+4NO/ADO7sI2OBLYEVrxKOFFNt/tF8FvMhAcEtS79rk+CO2WXGQIid7wmQyIG+mDfw8cW7h9w0YdMzJC79T9hSUenSkBDG0fn1NeAkLFdhezK9xoMsJCiPBxRUXEeTIJHxhAWXcSXvEDq8I/LJh9wr5v93GiNT+qzDQpxbqhuuzO3LM0dwYam52G7R+dSnfmnJrNsKFpI0197pGvRlir8gJ2t7u3KR8O+grj3HEjPHrk8dpjx7iZ+WQ4EQjAVzTEnwlinB3KqpEyty5u2xCpmcMrPizrFKumAxEmMF2W11IO1Wz80ziep/ACMJDR+znaCg9zPfvFO1PD/shMS1lo6Rarmcn1KZs7fqPQfrWdgFwzJnwDqR11NFf5H3QketFZs77DudhZgAJ/FKR6K9YiJSMBm3c+5fpr6P7EHiJZzHNP1xQZhtPGYW02Trzb2Sfrfea5f1bm0e2QY6I6azF5gpaJRE+6y6B9WewllPWQmKu9bzOPJe4vnaxw/jK9+Ktu4RPioEvJ9bpITZ/e09jgZaqAeGggr8ZTrAfLxp8vF9Pqubv9NBdSKV0IKuQyfFJhIw2cgfwReNijOWEMZLhx3D87U8jOWOlsusTaBDulWLPVCBZyFs2Zr6wV/Z/ZRizXi/EDJFP4vJZ5x68ox6G3xdlQQl08mPqjA1CdUZSChLFaeccWpFVLn8q2CVU1PR8Q2dLcvDjFgsvtplY2QtyN7l5ABWfZTH0KnB5bA6DBAI5+gs6c9exGn5AITK/aVuHEw4q24JrVrIJeprilMVjetkt78PZoWmNC99R55IZIs2mWfHBQBJvzx9lGb5aBHcrEO+6yFswbFxoiYa/IOQhBilJnsOqgX8jJPVeSskmQEK/8Zao07gn0vCJsjnBMqOjhDzytPEOhhXvOessAvojShqgiCbh+SfAc438dXA8/dygWjzM4psOjCALbpHF/UTMx9rEhJYk3w+n7TiUoKncXCd75dDV/Pw3nO/nZrfTPF2bDPhRFyg9P/sKmBCkitodd/CMljllGnWgV3kpLAEZWNHJHK8QXHq0vGyttZvIwhjXaeqvoNGuNhbOG6OhnJKdD5+V+/qdrTiSMK5UTJOCK/VxB1vnOONRWyGjvk2f9CHxCO4Oj+ZLuIaox1n4G+ppYFj1rHTFJJHYccY4Eg7PvPX5h0E1tOrInIHXrWxqumXuyNbhKnePUi5N9jhxEO8ExHVieFFcUroK+5PpPRCE1XG6YEBwOqv0skmgBo9CP3U6A2qzC6uBtFgbxj4I/8DXSe3+FLSUzrgtZOT3It2c+9ZrDOr/k8MtAFtdtEuNaY3pH2OGleHmGiuV7GJe+euvYgKXYKaJrN75NgviXH25FkcgrbRmCKRntEruJ99C0EgU1hYZtySUXamYaZgXDueESxvjt/49k87EqkNdffXMCgGlUyyJc0Wq1r0jbx1d9Xet3ZhMYWbbiGW84OePuRrRpXP//InGIcPkXLVuzEa2/wVrUajQUVrPPoD1uwwHGJbzTmHPgFkZS4i2rSg0IRicaVoTGg3F08gjfNsOUFYgc75nDihEn6UWv+lMhSYQI29XVPTzQrI/EtUId6LIGVkmw2VdNrpmZFw9fhp6tIeijWUqEW59kKMu859NlOkneneILeibdzPCrpLzjBl3kJBQhVMD9rbu67Y+1WNMk4y9j/sobOuiogsNf8eE7RzQDHizAVz7ZYuKn1eKoAmdJkoPWdf1mbzrmMedAyZnA/7At/mNz3P+27U2yVg75PCPWJy9kvMqLf6nh9jH8eUCFwJOe0i611dKhqjxWfdjVxDIV6ThJa4qtq94nYbZxWRUNKxgNURNFHnLI/dELS7VSO8v9AuN0HU6sodMU+I/yEs0Siyk0tS9wqDE+aX41jIJvOgvJwW45Rt5tcZZdvLadNQxuo1B2Z6g4dkhKJDB8LmFE2SHbGGAE55wL0OGyyo6w5M0v+OIkYbWBqZcpCagp43FlI+JQLFYrWFH5+fwyH/H1mEuCAyhCu8OblQTz130zqZQCft/QM4McfYzzqINznuo0jPoh/lo7dbGNedbdsNEMoiHXqAiRSgCX8wEsRpz9XjmRYRtRKIkiZ2/amiL9YK8uCoI5i+4cH89ap3KN3m3lsXfcWXn9XA6duS46jdKh4DPkXhaepy5Z0XCCHEue+SniKe+6at9ZuOoJ5pMYfDHfkxzYWXihKsz0gcrFau7gSuGdVNxjclmQLTr8Zu8Dn/meIlg1wCLM/t1wXxhlkoimHVNco58IxYsHmYtNO1BEP8dOSjqJNCmvcEXrzhsvtzPV/5SAd5whC1VLBBGRldXGAtO2B5MUQ08g1WOM93ngUHK5DXEZhYbHSsggqqdS5IINCVMnGZyFN8dTHqgFkLcUz5g0TSCfBtsrEd7l/+Jl3LfTpcxcfJ6AlbfV0+snn50VUR/VDMVSu+pPVJy55Bd96T30wEh1bkyRrLTOkL10xMftOKYsJ73x3N4cizKmf1U3BWZlm/6+UMi+W78hsDf5Jy9wABXnunWQ+9O+d+Q2QvwPEiejmk/XEY4iaWBAELxwhbbA0rJXrFy6G8NOddix87IMjW6YBE34J65kETGbCYHmG29UyV9WxrE8GAPwur6/chJUnFmW3EoiSGWMzKVtQ0RCdYy+wnsj2oKbSeWM6aikTDOhsaBzCxLkNrLDbD1pcymChZAYb8UeXc0zUhvpk1T8eDxdx7dZjr/P35RJK+FfZWPtDMtBZjoK2EWBnUfipjMIJPjMGPPgN/Fb0DUDArt224LssKG/nYmx1j7+p935Iv6pV9GRQi9rd2JY+ag5zYwWoJXIgXFzoHatJtq6voiFA4DlP9u3FCq5CV3njo6devuuBCSsVOM8zUDPWfyl9jJIqPE6IkTF0FzBVZQgREr/qSwoCEA7ZOjeRzCXvAU07gHH87il1sVomC72Hjyey6cwBwk9yEKTydVwawHQkOnS+3e+yX9XzV52Jous+S5Aj+nQHC0lZdfWKfLnltrNIQcHK8ais2102uOGQQQR/keqXqCkxqaIz0TaykqocxLgXIo6SBWP9gP7OYBhjisrg6mYPWwYNV+25wLL72mGwi6CmZeaDBBrUje/REFlUcS4ViGnB0ZkveG0sMWg+POetT10sfTUuE4w2oNvvxMj1lnpRB1otV2E+IXk7qxCO6khlnQcPKFIwIq61kLMvVsHGP9XCTqq8WjxLMiUPDjv7LSUKioOmhrrJqeL4RflRZMg+lowzrVdojW+gYy7pF7+lwdRzRpPZGrRKR815rATsFrbkHVHUZTK9jbtDULj75Kx9vO6/wkWy4bPjw69gurMoo7bPc5U4MugHtQ80iQwg8LIb7t/1eDAr/PzecrjakWfVExLocfBLBAmEtmi2ib32QFdU9kYk/hVtB0ojbQuuGSwktJExnVzYG7DWPQ7wCcmno1teF4cCe1FNZUMEjkeRYfTpuiIb60M1TXhSvcePT0D4DQ6q94IDYzxLXrE2AlW9dNLCV8Gg4jOQXdAt3foy/F7jxq5zjBaJS67ktn8+se77KUSUGqAJzD/qlimNUxtt0Txv+HjN+QjsMIabv7nwaBcLPNogPf3qVYGsSFWSBpd+Gh74+EvWkU+oC82oKKwMVqukUXCY0mclufJKLr///XYg8M2s62DgGXjPguoH5+zaI889LQq4P8uzZZrCu/8rBRk+UVkdjy/tPaFLbVcBddVEAflOtlmVwoT/b5e3TG4MDNjIWEWv/CUTRV4SOsCJOOCpIcCWYE/U6mQjFpuul2dEBKqtYCbKVAmcrNLQzDUhEasLaW0hNglw72sWjonyt4mUzf1TTtZS3bwcgsSPT6IGuUXwau42Bbz9ZRy8vYQ/yuRRNdREzHaf9iZKK91LDEjNwMHgGMqm36dggjn5KZKZtLIIdAfUw1aIMolchMKA9wdArd4ZSruKrK++nmeo8V2suZN2V2QM7ku/SKaR40l3tEw1GIByT6fBiTcU82bzH+V/zDipLmpxJnCHyI+ZGWBAhFDZopORLDxdYRJ4syTCu682wA0OWUtF9+XwRSyxWxW8POzeRv/RLf7K+00qen9+9+s1U3e9hxXf/7bsO5qu8yJhDFF1yHF8NUtKgtxbveuUXtNNtrfzks7sR1KEiyCd2YFWnJfMTRdrCCwAS1Gkkp3sKfmFQgWe9kBRtY0ojMWNPSQu4tTuKqZfsEzEqg5lKdB+o+P5ZPaxqcJ8QTpWUbZaCx9rYOlxW88z1f5GBJxVrDUj7HIhHDTXY698744uJQPrgZoVLhQ/zUnDD01JQd0ds0HryGM70sMES5/cMonQA3IT/bXqIOKcUmTBXDGU+J0ize9kFTKdGgk4272sepMCxqTV8kb4chlED44pjVlOd0YIXGUTPPfmHfZ6XRaenfFEkpoIM6TtsScgi0qOD648FSGShfPguBkjXb5VGZzlUiVmeXGyYhm3XxlqLtsmWk1aDbk/QkDY4JqLQVWgaxHsDmXaW3WXZXz7YisssfQTRAmngNtpz2sLX4O1wmTDSXUsCWxyXMuHYq6W7Bhzhr1Nem7g2dD4tK1I771gJKfeDDsp25ITVGJfuTddh8te9hdEli+haIqvMwQGc8N4H2/8nmqiIofnEWFDGaJDJpKmImHoVK99PuPqR51SfZ/+5zr6quhCfuhmLIO6xWeLPO4Bp1YkoE7zOgy0OkNp6UdF63FnV37So7mtF5OdNe0HGVsM34vPr0o7j9lFElcYUDuwJWqoH0Og+ofMZjuxNYzu2oxFzsUmm3YheKczwMvLpvUAqyzYEC+v6949XIxWDCEkBW/fSrx0X2zQYqkc3sgecr+XjlZCK+dIqTnS+tvpYeJZk+TAOkZp/BNKxXODgq8rX1MpFyvoKz37DUXrlJFvdt8HbxUY2DifznKYz4hru/mO0G3cV3/PRty/WuxGzhItQlyYnNHY86wzuG69+yIcoeo7H6Mq9mYd80oeu+WZV7dYDuwXC9kiaYq8UhraCRlccMEmcez988vYkPr6TJxn6gj3ouRzqxtuMNuh0OakPVlSARQzem2wjDHZKsXgiuhZco1CgKEdR2TJdHX/g7ZY7frrNlHCVCdN+OjaaM9tKGDIPzz5ND4kl1Fg+Ig5IHX+m/AG4JaiuIUawxKh95Yrr3c1zQndJTbOIH5P6t6BeTOyU5rxqUnMX9qfiZaicD22rntvMCxknBIDYvcBb8A8uOht7PyNPxCEGJs3cs+6MzmD+80uaIvkPXbzzQk0Vk33K+pF+w3ivTPB11B65buOlKZAcJEZiPVCtkc2ORh/0NPppRA7fyK9EBSK4YFVetkveuNZM6ztvynSUPndylBGm+Pr7QJeOewR57z4Cwn5Tq5+83CgsdTlW3oy0QoAKtK+UMl8Uuj6PxVd8SaoIv93KyPVtulBh4UOMZ/dwNvt1l508B1h2omuvtuPWTzp4tmAysUAkxFcCJS3ZWJF95tH+Nc/ByuD+XOwSnXIwDht5CJ4Vgm8Dgse9MpncXd9zWlXzLpJPkyxtVwKJlbSdaQAdMJthURijoDeQd63MIfLlEaSyvq+2RRpb9rsnRaT98Os59/JvOimxA3oadQNM6uLNhYUcWkZlsWkPCfNcTsnSVHPcKEXFIC6w6LMoMv+HYQ5Z54uIa7Z39EpbCyJ2pYWmthmvUc01ozqe8swHuZFOBKAFOwuZptELudL8/HF+Ko9U8kvDfwvFinfnwAm8sL41msbCU9kTfTvU2MsiRVSkXnzfaLC3twEDb/x5neeTWk7PZgS5UF/XSMhIq7ytetajie8hePp/a+LfbNe4Qv7mMhIwgaMsPYAh8WDVOxi5C4fHyYOepH0ubeZttc/BMr9dglSo61RwhQTnbBcmkFBwHRQbxuqRSDnn6JuwqguktjKfoPxVdE2/ZgyzegYG7hKupiHPxPPrYpdt/eS/fggoldk7tdpmApI9idf8PdWTg0uv1uFbH0IIIJIXfCwuK+yxCc92zlwEXSMM1k4uKvnbQje0DcHs12KtC/E+pnfQw+R5P3AUZJtlqqCJgb+jm3Lq/anofS89qO3qC3m1E0gauB0bbtxTNXEWWBlTCMVz8C/PmJZ+FBIFVzrDa7zyTILuUePI/jtyTuarRfg3OR6N0TL1nxJ2nHZkk2lFTZ53EGSjGBmFPJaw1YKjWQk/Sa//b3x8pBi+his8g8BVG5muYClzpo58hN3uIN+urH1NSVHLiv4Ac1g7i0E/qBKUHF5SEQa7ybuFI/SwQJymDHbhHuK7vv8pv1Cdl4xUEYO0yhgIvTV5Axskrow/P9OMWQSoY9UKRrtsoszdSWuz1Sgi8HiUN+HMfqrm/265vjQLz87wfnSSt3XMQVCpvy5G0RXy5ttHcuuYYDVkZCnZ9W7RU1/o3QIV+CFudaLVGxazl11VpxQGwuSWrNO5eKbdLNvZ4NgW/RbRmqlLEA+miBJeSMz8JwXFBLudRAU6SVDUMUxrL+RHMWbiZDZdtYTuxTKKtpLtbeZ0WcfcaqDpwn9dhCXXvtJvGA5HoxAmG7dS7a5r0koKTT612vv4K3EVsF5vxw1ZaCnGSvqLmWqpnY/1kRvpVoLxM8QUpFL8By+b6EYOIf7DK6gqJeIdbtpWM+74WK/Hqy/6nne9Gg9DkrIEpSzPvJUcuTY12Xgk0gf7c9uRn+koseOFV6w5AXQ/LG8mhKbD/h1Ld0DFNchRf4V+i5e+BQYl2UgEcDhs8RBVwZ2XgakZ3LrjcWvE4PUklgjJjjZy0QGgAks15P8vyzQiNtIB8o+LHrcRP0h723hII6sODEN7K9y00my5JnGSe8fRuMKnDfHRI0flrhypx6GkAlyI8spoM/zBaHvg5ngX3xX1YWA3t7YrsySdZuj8MD00wyjYr0BW1WItu0xgZoX3aGB/t28jEZkXrB7H0tmFlZUD8ckj2COKbcPFHK2mtJ4XC+TpqLdxqI7o+40Xl6WwF+dHJ71pbu2CLsTPmTFA+4DDyGkHLtJq3irHcEo6K0jkls+jHv+8IlqYKaxeYoofB6pDllvOd0oDuO96y+jxn4Jy258TsuqRv87zsHCEkulV7P5/CJ8fPYK7lVvMla9ourSURhyqVA757pbK0hsC2cq1XijF5hAwG+Dj/PKINDhYKvV1+7/GKCkJ+T/dPIwRLOnwbCsrl7A/gdWtRPIZZkQM433zUwYtKL0iHvSD4kcV4qH810b3ZRvx3oU+uo+qsHrjPuIfVwvXzvxqtFgpjQ0K4cELESSS79Adyayn8+dqXMNdqrWrhVuYWjzzHNdWNBkLj0cxNsSSG7WHdqLddkM9+mAtGHn2SQhbx8VL5CzFHi9YVl27qIW9Ejn6uemQ5lnHjVDj1nGOZhHeNVovV0yB/u9L7fA5Lt9yWfrm/eVbIbM/s5WAGXUOLock1JU+LHHv4tSHaAeL0GWACBOnvPNneiz0Cfu33NEHs4NdYv5NP3ek9RbTu7Sv2lgXXo7TdI4rvRWCYQWewFAxzPQgopwpKn5jZ/jQgh9FxfDLhUiD+4KRRbddil8nV8Je5wD4nQESwtJwvk9wqLG5R9QK+/5RBx6EvundUVbzCgmKMg5XGgIh3Bw2FP/uDb6+Vcb0ue5b/yjRiPbTDT7C7R6tsweaoxDJaivk2/ilIQsLl/NIRe+dEMtX57UZTLerkFl2zGXLufmE+gIkjfx4zzu5zaMhADy4dB3RXWS+oESJmSd/vs2MMQvJn6svJjmLZmbMEEteuqCCWCVN5Ux3NbNy4pz7R1fMiDhsKiTS0Zrb53MO5DpasGpWAO/i7raTtnmKYqleXVWwkskIv433IaTsfvcLC0U5Fd92ZCdVCN4Bn53LvPHxYGRmv25/dHUhtD75rNfWTgq0N4ibZMhAwJHP7L7fTd+QjCjzrbcSheWm5klDUUIcbzwQDNKjz4YE4HxBWc3Eg9oVlF3woEvkDYCsHCbhI+vJIycZK5fwGoFo1iSlKSo+gMVt3InbasdBPGGPMg71nV8eHpQnGaCnbb/ncq2vX8Iwgx6MXmfRwT/p1+0gfulK5vRmzOXRHYZEfinuR8KpFudz/gxZPvJ/i3KyxFwR/nMtBaBdUpB16QBvuqZfZjF3Lt+1GGTXXaAuLmAKCH1fL4WJBsXD0nQYUZmhw+cxDdh4lXJnASRE/b9b4iLthjv/n7dLDLk/OVmzZtTmtHUkm2gRL6IBIDTR7WRIpBx9sQobziHCNA27VD4nsgNek1Y6cRRryjqkk6yMdZzbUQW9kZ2AefoGKAtndFKmGPO1siRnZ/NXyQMYrjhhdG3is0Cjb1Xl3HuQnggQSjkW/eOrNLakW90BemhEWG7NeNkMWt8juxxwl5ZsxqK0zMJHcNwZsQ47vgtPFEPtFnilUIBm2VtvuL3lv+ecbLEx2Etc7bSfBRSnR5RATmLQHa7D9pZzvOHD8VRBS0/nyR0pk7AjiHhWOvZes3I91s42o0H0MYH6wpACBcULZaaRJiR3+CeptSgJct2K7Jiog/JWKu6ammzmapjvX2TKZ3u6I+oF7B/e38CbQoz6LJTfwjgrrIOAEyCQ8bHIrkv3/km6l167U9TDUFcAl6mH7Oq26s5xsnf2+jUeesaWxOo24viSM6zqsEvwz91LopybHL4Y7Xip8SZOdZDRkCv3Hr4jc8IocbghzUkH4/gkP749Xm1aTxwg6O5SYvKxEuO4iIdNQ1zWGs3oTb+ww+qyWb7yHjhBEwAHXJt+oBENPxoJ6sd+if5Y4eTK9zowVVHF47lX+bB+Duqy+339cUzOtQPtYmpLGo5DRgSJuak/H5tbtD1FJX5gCdPuEpVjeiy/Kf4ZGlrMZYkdx6891LxXDfGmLnuXJGnhdFtkm9OJspLLdCY8NPXlAE/zujIXZvRr8Q15nV7kgv9rYVoGMABJzyFhjpNaFFfqpCqgICO0mrf5/q9tNf7z/hsSAMXETDcaHRmR1FK4XP8J0cilp+1+gVM/CX4TvGMdQUM9uYZHAaxI3lIOSviw4PUDOqRaa0f+WxZd1MsqOMM/wUSqWnC8a4mGt+L7FqBkofaEAD7+7pH2KGyX1Sz3/V+C9FYwtpAXKba2auhBUafChHm2dO+/RTs8tjex5BvyEWHjaGI7IOkdiQQLZs2WqRLtWMI0j/cG/dxcoCyz1yhUUlZVOS974h+WB/yAWQI8+HCjQBQeKN73n27glNZuf1r4xZ+nz5ra5/WzPoQs6PH661K2GGR26ecM7B2YULZMoGMmG/RmvIk22YZ/+YSNYN6G1IyvhDLYqSKlmwaO0Y8zVIetxKrh+mUT1bSlNOex/0xNEs3Sh3GMTfmtpSoL1r9vqfzt3g8M/Sg8OzQmd9u6UetbLATzw5ejnar1IHWuB9b8GiBR7c5+Ha6owA73yAoGXF/Qy905J3OXGFzht/SRpCIqrLnOV5UCAZJxRX6tcbbdamwgCfmxivTEEwWqg0g14UgLrGcCUfGz9FuZdlkquzYezHZR5IbwcXO8RViQYE/0Vl4RHwSYEGhnvdYA3+WAjRHT8tL/soLnXFIdS4y4ox3SnZjJmw1aliBtXQUiArukNuoqLEwYSQ7+e2QxfXxopFvReMWEtZ3YA1m/PQoG+j72O01s3CCY67eUdxVkl1gVU2AOUrru2A4sgvcw1wxbyfFkw5s/KaMqzogff/Q7s2wcio2yd4BuvaIJC9mZD0RprhWzzvbRqc0TUw5ekz+ppw6pTTqvREHMq4WldJw4rNiwvVKHKoR1lefG2yxRDjeklMlSoR9hValchNMw9bcGBFFoaE2YkD3xUzp3tZPYRn5S5RM8YZl1ap7/xqb72sxPshooamKzuzkG1jB5Im0vj1aXdUX9+hD1fVWqncXaQ5FTu2GusnL2h69hALwhWZt2XI/0UqXecPB6xG203VycM5/UCEzdkoacrtt2/L3IjRCfJ0nuPaKW89Mx3YSUXmRJPXeN3DtTiSY1EX/Cwu73V5bPJ6MvuXzqQBveJIv5CPvd+cBJ7WDV1VqwjV2Ct2vq3wan2zliRwdRzJUzxDAL13IYBOYFNOSFz754zhcQcEH/kDRFT4qzmLeA7SV0ZLln3Ce7GddA/UIUow7BD70BEOnJkV59tcKk6nLxgYjvwDZMsjm3OSP/BrkAWW2Nrm8EltbJWP4+RC2Naf/k2FmMutiPaFARw6u6d8IYmjCtKzsjwGRLS26dRGUCmQAJiS7G61iS9MzL8kLJrRu2duSi4teciwhqIiupYdw5X4pQKIWUr1CieIEBPXQooX2X9YWtsnkEtmFa8l5CghqHsBztxukgmbArA3uqT325ghPOVG8HSDApuyOsqES6K75BhCwuYoKKRz6CGPLbHMMfgX3mQdHMEfjewYuoObnwkb7mMrLPUSthPQOTaeH2whEl9/ZC7/54aRDfPaVt57dcRbTz6n2mmTWxoTIHjxrm5KHhZ+M+1ebtH/2WTMtbPPzeXeBzxKeRdBempaypnNZBrByL6M4uJqMBnXPjg7FrKednjjsE/KCPENyRgkpjIA/71zoYPE69zKvJcrl9WlBgqaLJWNXX3ZAdVKO5tgXItBWVeFPftz0SyDs6sNiApIHqTOgNiDiUPF0juEWI/6PYJ4ROEfy86Jej/Ew3e+muFqeHytz5Au04VyqHrZiQ7ahW00lrlR3XNZOhfCvML4hH9dLwRxWc1cvqSCuST0TgP6BTugJYpdmrPxZX7L1tj7PbyGFdm0BU66T9Yh1rDbGqabDJxFL9azjzqf3vA30x1HqHYNvz8mhAvSBV6Jv/mTaKZx4czICQkT1ulRPrAkjwrlBPWLPy1Q8JStYufoNaR6HZ7hC1CtKjky8wP38H3qfEFCYSy4l8ADXCmOcEdKLwml1/L4pD3WYjr+Uxy5ZBL9dqmDx2XTgU8ixKYITDhNCfJzqAFwp4ImCpTFsVLTLy8zAYCqfgLTNvnuGckfPvwEvWiAgpH+qtHjc2gUNJbkf9JJHtPjs4ncRsc8ipthy8q2ZCRBJlQ+VZ3akzL7fUNQVNX9w9C478b3RdlTSdzixEgXhMaDq5YJo6VY1d6tPbjKQ31RGeR6gOmyekRR/1huE6V6e5BNEFcZXwFKBGqY7QQcLkYei2MLOZdZQQSItBvKg2PvBqwSK+EWzRAAPDEMqvn2V/Y2fK+5DW2TKsTZt0W1chV/+PiQfAzkfpeWKKvlHXgmk/m/Lw1zDBTl7on4E2k4C5ClENncOGc87o1Lh2HCfyCu9sofImrWsv4j74pGzsGO/75WqwFfOTuMafPypSAQvT6o0ZeqOpfjYryPmuCs5TULyQDk3L5qn3uRSay9Gm5W0VzALtF1jcfZ7Y9+toA1ywBBaFi7p2HdTRkqbSM596y23NEnp1TlgonoYS06wGobAJOYdH/Uv6RAFiSCQCqrK673DCS6NopSQdDwI1xo0Qi/L/D9RXLZSY8GZWLFzk5WlRBVvabgFIMEa4oN1jrkDgfIVx7d0umjA/s4oHOcbUGk8iQea1TuW3gjXeE36c/IfHtCqHl1iamJ9gMwY4uVBiAUNAPlGl4A5u7iarqR9z8TMXBokbbarfRUcjisB7dfCNNpcGZKPmKnfyL4/md0TZwmfwQhAxzbwq0riBmNjPX22udw+FQBV2gw6Ri/aVn6bCXNKIQa4/76DSmDB64owMS480PggJP8YNKbfIzj+cPx6rcjED7jrwUIx3KyCI6mzXQxyAmvlnYKafoi+fUu1Cjv08AkAplRoytIp7M4GCVjjrWVADN4jlCxXt2rsxaZrgSPCicMBxJcDwgZKttHVTjc/v/Icz3FkN+biDXZN2p+RPPOO0mapIYQTBYuMX2nYpaQW+TDUj0NB5U8WjuzpWXhd3+9EMNzl2RchJPXZ/4a7R9IgjiXhoRBl+gqDDh4GkfE3MHc64LUVHjp3kZ8wkEL0U2kY7KcxH6Zi/amZm0n5bApZ2OhGS1vqpxIpo3LR+Gf5i5njOnBZfzx+QoKwYbJTYwTn8vD52VBQoLqPvOSy/xfnMWvtul/fR4fQxPgPSGbDdiTjl3celWYbPyO/dqJTJPMQ2Am19aEuqAwXNYjNSu6HJNIkB4/B2zEgiAgjxt8pTPqu7JU59xYZrgzWrQBiHsj1ISav8eraIEx8yB7Omm/VZ21irEM+x2XadKUdYjMoSlfCb0/UDrT5VJU0nuuW6MnPKdamnpd38Q1PmLpAOmms8XWu0LtK2tBLNQKgEosZLo4fVU+PoSCIm/eiQcqLOvmvC23DIi321jqD5rEQVCTYGE0nmPEu+VDL1oVsJE3ZCRjmDy+OzydJeUJfWgPJHbeX3ZHgvbGrX/oExHK9SJNWA6nQtSw1nhKLsTcbYsr5L+yAzPuGduD+fwaQvqDy+VKMrHqRlNZFquz6nvu8+OBTUrF8wiHX1U4H3IK1rgVy8sp3l2z4QKNuVLsm0he/oy8E+K0b+xBQSBb3dgpF2knq/3Dxu2lX9odb5ah/lzF3hQk7TDajFr6n9c1DoYInWro1utHe+/4Uzfh990zv+eFjLfhe1Apa9c62g5IsRBxTm7Pl2dMtLrXKKriin+uu+Xt704P6CGoJcp/gkqBhJb0zZoMhGfctWQuLozFEv4Q9hv4X69OkJQluQcplSmJTkXlffTpGRdu7tAwa1Ueru+++83rdKIxv+mwTr3RlMagthG7sEN3IUCt6obBEdRfYtDbBeFxsblMsm4Wcn1QTyC89dZGtQUle5fB60GSSvk9wGGEUVNhTyAY3dj64uxuLPPH77P8/+KNQQIxSFTeCiTDHOTnWrF8HPgw5csIixWJDe+ZIuZPQte/txgTszu89qo6DUUwanhOTlZ53muKD6uS/y+CTwekdRPiwkdq8ZTcfTT1ZfI4hXeX5M5vnv1H6r+JhVd1Kf8Cckf8OBXd60LBypZ4PGMIKrt/FNvx3eYf+AhaFVjqvZI8BgvFFz4LkDyebBKLGAKlzWyKTgYwNALV8W4KqfGAuVM9WUUO8Vnn86GbympCsjdTL+ZwCqf8qScAbfflQcfRVxWLKRv8DKDEgU/UYZlIyrOT340mZdK7h7TclGw+HB9xcYMSBVokryx1vVspSBBSfRlfGAKEMM8E8mjs3X+znezv8GuSvFI8H3ZNii45fKulD1JQaV+wlMaDrjbetQiA/DCqNk427KcVcq5AXBKPrKyzJYE4rZFr5H7d6c+uLWFO2BMncpBaDLKwF6cXSQFytVmdKzk3AjPifex+8xgvXRrCZ1qEfMX+pXpHsDMr808tFNFJH7yoX58WpPAubsJs102kDRwt21en9oaH2MX1zGsDmrQAP6A9LCUjvj0gX0/1588gRlqU06s8gzi1TQFDXvrCl0FR3Y4Iy25aOzxwXs8AIsDQwvr3DReB1de0o4IygCSboMufpqZdZtKPd08hj1np+Ub2LD9uScgfjXbPWcFwJyjy2ck8DybAdLWde+sTGj/rwoBPifh3IoQ6J0aBudRq9QmOsqLYgZEd5/BHKRyNKXbhJ7xB8zFBLQgTDPbSRiiEOf5HL+Awb/wewPEtDVeKrqySyd6xwYAGhzzWtxZ26A8zRxl3bZocUZm7/8CYE4G9/aXIf11R+Yp6RUThyDK62i6CFbIdF8unzk+RYqGsN+pQN08OtO99fm4qagPERE+AKJ1N6zlB6f1D2HSCdsW2va7wVR22RAPRtaE6vvDppqjKoR3DXf2xVdDTLrP2QAKazckTrTnvQrp9QKrx5Kfkq8sJ0VlymtsOP3DLHUJKqimdZznct4WDGBdPqXtBJAbtmkLo6UbEQ2Uk6Io+SaSormiJYdZej/xhi5PwQ9ZmzH86tgcikv5PrdM6NnN7oijMyG8N0lyRrAzl+0SVf1cJBo4D4JfxNz9AypYJW5nU31y7saSIgy59fqVbMQYF1q4lnTrfFww0o3H0GXPtLbZzSXpa5ctiAewsjK938d5jTSPlMAzGS2Xnjruj4y5O8BGj0d2K4UI2RpnOcPcsH0N/LaRHWV0On/AIACuNEsRdJk4MayF1cKBmnjsy8tlkhn2O+3GWIGyrm5WZAF7LCEuqUOVyYgNb6VkONfPKZmepDoLye71UOCWudflvhud25XlfkarjlIAr5tmrhKZ8wWO+T33a2dp3m0gGYyj5/nrN7VtAimHOlvrqLJqR8YODNjXIA4ZkKPmzG4rVy8Ut3Xll9dJw8J0oHGW6+6w5yNfol87QCDm6WPvA4DtGx/JFpK0alX+hli+de6Mui7vyvydpzkwhQlm6BJWwnxXysSMGy853tC2fNfueUSJPX4EwWJqVMIjHm34z/8I3jCFBQVZep6kGGQzX9c2sUkHS0bst/RtDhafaXX9aP5yo/iCY3OZigjDYUQ8cTThIHUmVdRl37Mb6XfoJt8L8uadI8tz/QFl5aSWTuSMto8H8Iy/5F5rzI1EMn4gnZbsGUDFk5JCWcx1gBjCI057CqTz7fkkuQO/XZ1+EsgkObiwDqU7Uj7Lfk7YkaHEtWfpyO/sTKIYA6bJIh6MpCcE1KsjB+F+R7OSGnYeunG6LcWj5CIKxFoGW0fRxkpylJBOIDLpNn1TOUschyRTEPRNO1jGiaatxTH4M8IL8HmXcTipWWwweF/zGmZsBEYqpkSF1a0Ob41h5I5ncgZEX6XW0klV9EiFzybRXUqzJyuj4UoFcgIqjmj9SxPYmzgtInEnXqJgl5mO6cDmJ6EyJ9gguYt650z/5mHJSy7o2ZGOJDO1M2q5M/okEGmQHaH4X+s4UdD3xxoqqpRzGD5oBGS25bIpw8dUnxr6v0mjgy1U8tD3H0hWkgL/JPjSTFB28edZF1oFD3LvTUcqbw5fkE+Ao+l9Cn++kNRpDnUGDV4D4GtThz6z+W2m6BPRwD1cX8o5T1G+3593fJhxzBPJpi99if0SFFnrA7sVsRmfPjLUX1rAmZM5ysR+oGvJijyJwEilUV4s0PQ1mB79NGgSHBpFXtZu7C9YyZezty+32m9Oa2e6d75b8H0OZBg5Fv92M4ZKx8q2OlG1BEGvBfJ8fHqsveX7cp7oGO0gy5iovmlay2DyfHftdB30+Gyko1LaqvsqLz7obuSvDz4wsBzEFJgirN408g94lMAHpQC6Cjb54hbu7MQN2iA6Lr23Q0FORWJRa0iT1C9p47joPRV011gEKFaJVhDTlrbdPPYGqyHXupzElJCgBSsyhVBPIEnVkaP1DTn3PN7WcUCp5iNywhBOZ/y3FUpdnrqSZ4HV3ipUl5RdGvRqPkZCqx8liihIHhjeIU23PlhscK55QoHtTGpPIhumgEoyqVwYxQBR9yHn5isq+JCS8LnODduI8crRv+GkJP5RfpTcVNVsq4x9SrYAWuv6bHnh3NA5JhrEz5KuwrumvjcvXpKtPNy+Eb7iMQIpQHl4AvCpoOk8rIMBjT9KZTLmw0BoGml9zkTquZY/g0dNDFn9aA9hYoufEAQUQegLOQD1T0aXCa94P51kZQJMHn829yOhhvnUuY+Ek5hPklIbFDgXl2E/GiA0DMZIKhLrl1pKzo2ACKy7ugxIbsi0bgb2t1m9iukr7L1Db2JGwJ8m4RQ9YmHZb3sd0dolEDhZa+Jo90RVC9JSsupwarTw1iv3Jku+Dg6t4zCI9PpL8SG9D2DqWZVH1skY87mP4DEuohX0f0XJObak0dJOkrWDRn6RuVlRx6w3qalV+C5M2qrebQfIaZ3b+2RVcwEiEbFm3oP+Z4LBqlb9Xjr/7W7aYeJlXjQqdrXZ5H21tFMo0/Hal9KOoZmx7aL2bIm5QYfxnnCqreNvCxf7NE1a/kO/Rc+qiqBXTIZHJRRkvgmJyV8e2DG2tqkRhIcuYeEVyHvCH/5D28md6lJbxI9S5Sdl1xnpok3628cOhVntQyE+xfY7bZ5jQo8ZJRHFbLAwkZJ2PXqugxEWR2wqDpJNMDa4qq7xltcvPQ9vIAvLxikFDpMN7+lBrZhAJTtvGMtYxGfX4xMvuF+ikLPdY6lOHtaP7y/O8PWEXW1dRjTbiw0G2Xf48zMS5JPiohmo5cV3s9H6Wr7zErnmrVGaUzCID1z8xNQBpmNPL03AUhhbZEoHYmKZCrqZs1Pj5GTnZKcz+NuZ9dT9pDZV0P7u1kJYgPISkohNPir7Cp3GKPBf/+OYhL/P7YjmlAGb4Avmusvqi7I3c01yVRQE6roYKMq1mlteAHy2FmNfXgsm4lfLfEpb2OsEZf5HNTDSGnZykR8fC/q3AAlqtliIEjAGPusZFP8e06QkhXXZbwqvxwbfwLQIRhhZ8cKvwvqxuU0uT3PFpCJZQzkQMeYkzB/N4ZO/1DDcg22jJcIjbc1z56yA2+ZXyxe+5dUmmBPvuAC+8ECmgGkpvyO49ntmE3FAs0vyHGY0MBGdncz7GKODgFD4em1IvLbNRPCOklSqfyrfQuB2ZMXye7HlnAOTPNPCHcsp9JW/EF12S0WkxdxORjfAraxvudBTSWkefbUQ+4HR9ZVV+3AQgrwKBI61to2GA9KosJK05IwIJfhFofxaKthXICULj/BaYiPZxt5f5OLZUMhXZui0stw+QqM2P7OiSbMeJv8JPaWWzaBYsuDLBq+2oNEhQAhrDof4gKAaXmDVq5hotMhIdxoKM9jA/HOSKYnpULCb0PRaXhw0HRlEtpq5eVmXbpD+E0IrtVlgcjfIvXwm1oJinu3yGvUoDzRd4pGkvCP2nUfemSzrYG4JlL3uD0bhUCCRnlf4brzeDKzoqTeX0mY8xW4jIDDsgtaIN/36XJ1c/iOyAYdDffw9GqfLEbtuFsTPs/6ARGvgd9z3kl0/J7sT3zRwVaeTqj6/4BdgOFRtjHIbPvzIw7TM/oldU4242j3J6T5DwFm+q6mmpUgmJRD+fLJJh4gjIjtxi03Z3ffOnM5yYIR6H8ghojzrad7Ycr69o0QwlFUvX7XeXSmlUGmpeA4CXiqIDO5Gy3dvUxCt2DAN6q5axbhmmXzfz9wPqVa0+TrCyyQD9dNuvs4OEkS11Fsquypuv2kFUZIXvaWSHvSm4DM9mC+L9dOzLaOY6/W55NRYyv2AqBq/nv1VcK/iPsZC20BxY8hi0KeZRJmzMeIMLuhdhxCwSFN45NcXKk02Iz3HLWWcQUrYHQc/hkuDXGQhSMee2dsXX/ndm0Q1E5/K/1SI1PnULQLPzp+k9GCe/gt8KP8rg58R1ouOoA/DPc//tS0VlKCwPqLrU2igpQjubq3JDshTTnz31wwDnxnmaDbOGd2BATbl1EPIn33ddsnGqIz69MTJTmldM8j48x3d3rcU7zHxgNvWMynk3NF4CIasUAF6xRGpDzQAJfZY7SdW4LjWglImtB3cDhCb7jq3mcUS+rk8VnHCusBixdtNK6oGE6+xFDA4+42e2K39C7RHpaDZqQ71uziP2teXF7wZDPhxO2w4qtNgrj7vfvVSB7dZGlMWPUe3Md6UODiO0R/yqgPTUPJQzPx6zty/3LUTiGmYsLLDbBbXlQ1dKiGk0QvfE7mMJ+ewaZIJwBfW05/A4zzS/x7wXrGaMKeVvWAn7owCfZNbjvFgxEkiFFGMA8tuFh5TqAAuFJRDYVIKaaSoqg8VFEDxz6xBWB3im0DVLGn8YOCtG5wLrGRsofkSLqL49Q4LzgOZGanjT8OnDxu5gvM9JWZHURbGm451UvyCktG7tLAzp4M8OhNKIeukG+kFQEWe1aEalts3c75ZaOmXPHrxvoHuY7UBYCTYvcBpEo0IH3jNQQYK9eAJt5kKcThYWPYQkZo2+cn82/sLzhckfipURk58gX/Dc1VtHbcmpyxeSwsNiEGfQYUGGbtdXGGiLqdkBS+o7lx5AM4PcSHZFGmjN2rHuJNayZgRzfCP7KsFhfKr7eiCyfhB0Di+igxH2NTRkdnjzzKvnxMXA9RyAgV2RUYTz8dmcmvYt02xrtlgICbbYcv31X3M0RaALiScIYP/kfNfKCJgEx1e2heSWlHKcNnPi8JiIbNbS+J7vJWJ8koeE0/XymIJDS51iXb/2GP36hcd/AaeQLPd4EmW3dWzh2BXEtbwmuK0rnB2xGDiOY5DaxDDXSPlxdypIAG4TlFP6PUtFaNX1v1AdKuRNDcdxOcR+ELt6/nwSmVlhzYwGU4eqU6fvtkULFHxInK3KrtxEb9vQ9dHOvsEPUvrLhHPa/bKJ6xKj6Z20fdqMMEoUuNnjRogb5cU7i/92hr3Uguxuc5jjVEBmpRWP3X9XrqpgZntEfalUNcLyJoYl9xToOAkIcEGUnsodmE430p875BcJ0FWw2WcDtr5edGAz4FY8RHxOpp19GIcuSixfq/0TAm0J6IA3iFIU3xTus2VUW8utIda8GEdjWdfv/F80apKolFmEGdlDA6yowbvJ8WJppXWV2+QHddVZ2FGvzVGLpzyen8mVbZA4hS+Z4Nz5YZ2wineJrqfH5AGqlcYUCxSwG3di0fHjY6dwyQiD/2TJRSgg5UVoqOjSdq0ACSayjPjfEdYnDYIJYH9UlbNqrxOIv+T9oadnjcNa+xdpaKbJml2dfz6EzXDj0W8we4+SrmW6YAjA6ZkWk8cHfSU++YJXrzLB/h/u8yCj7/aGvDhDUplrl96yoShM0ocEyUVeIwmnLSEIQc+GTQ62q8INnK/VkcdA57q4LBKGf07G59mRunucmhr9SVrIYtDuuq8SWGXm7nbXsC27DsU8fI8vs/rJCmMmI63rhRUXydJyTtWaAaG23cUhBn5nx/DZRG/py+o0erPZ0RQrmw5+aBPRfkeOrDoZAd706M+kMDADMKfGioz8plpFleUKlNLLPzl1NYKadkMmZ/Ghm/vsJDhv8GiqF4Zvhfidnh/kcZneegn9tnI3vZzVKABoyjyrgZ4gMZ+8lPHApCx4QVJJ27dXHqisqf7ZytMpX8CK0R0HE3zlw9VkgEs4WCIzADlRYOwok9vKE1bZ92beygiX5iX/2i476R32sdoNRVT+k9A+ioKeZai1zj+c2HzmAaSARHMhqTZdCvkGLrRpFpn0E0qAGWqcMjK0+9CXCzpaN+tPdk22LtY8TWRndWxO8Fb7Wah8pcM6TWjKDSLayJYYaVrM1PvsTFComUAdOxsgc+dYlh4aBZv6s6+AgwaPYJ1IqCyzyzUWXuxymZEZlmHxB07B5aAOfUXs8kEX/k2Kt0xuwlmYu2BlgTK+6aVji08SFPfBJgBRggh1kAx5FVa0RPgd6dFvAK7QxF5QN6ohL42ZymuErV486DdZxGlcMCJPCD1xfz9BGUjMN5yNR/nQrRXVrk4heb3PgOF8ZaKlWgoaiVeIXwRGWSaWLRjpjUuPzZn/uIaZt7g3xGPPTcm7NZ8sWE72HHHMzXTSeDLECQTLOkhLZJB5t/nrEis8niYiBdPWBOs2F66QacC2iOHkOjcWXz1kEIweO1X47tbOH3zZTxrVZqnG9AtyN0YQpok2RJQPKSeF98sz0BrjIjP9X1P3GtV7yiUcziJP02lF1pJqMVeLj8Hx6m0StqKbLeehGi8xvVdJ8unUGcYdAiGB0xTH1AIkwKJvJ/wr+aA1Wz5ehimHCFZmpletA3w6Atk/cTHjgQfPO0DM5Cs8s3EmEb09g4JUDR54qKu/eN8Eok/FhKnzZHlIvn86Ug7TwmzDB88MYV0UnRc+VFU1/KZkOnqXLVI58dGGSA8eVN2/EVe6vkgHmLNtqBb2vzM5/2Pe+eXXlua20Fe3OISFsWsoiTGrbFlFYukyaB4nTmXOi5ZgsW4Gnh6cS1W2dxiJ1ZiutyxgBskbR8ldog3JoWoahmVC2jsjqYbLC9D+t11+4XzqD8TrAtXlvAG7VyPGf5o7zpA5m9EYpuegv7rhDhRjqLU/IRhmM+3wtDoJOYHHGXCVFN87nU7g9SZZGJQyIOSotGjawZA7q9tdlpcjwqq0Pmya88VUj6OF85JB4i/A+7U7vDg4XWisbHqZngMq8cm1Qeq8i7XE/L8yn82ogc5DFGqTUUqBD7E+Vq6SEtzJ2AxMFjd+2oHrcADD+rHLQd/CeJN3FY4cEJnuWe6Z6C22RCoQEm6tF7/La4AGjuYPDuYkCOnWtveIG/Gekx6M6V7+4Rzog4M6z7+JAyr7vsl7S1vBndiD6n4n7wbF3PFiZjQGH/QpWKQ3g2O5Yak4vMhwy+niTX5n3/aX9Noda6UaWfMXfXaJg6HurCvBxqwE1qyXboYwaBpZybKEIoVZAPGLQcYVkbFei3AzSNtGVexnx4Z0MXQdnSBuifJVFUGyPPoeZoL4Wt0+zyZ0mMcd5pK/hatxnTZRk0tYN1HONrTwTXux/GO+ugbqNtv4jq6iG/JKCoXWCJxd/Wkr0m0VBYju3UntwcGj1J2cUF1Jqrqu0r8U5Af5vyaxOc4ad67RKE1TQqh28RtbYneSaKpweGmc9JR9pWKjvC4HfUf1BwXC7F6Bk7jIPTUYx0vvmHvXdIDZMduNV0A4pfdK0DX421JZs6RXFr7f3Dd/lEnwioQvAVUrDHsVMoAFnpGzKvgb44dXY5sAmrcFCHS4MIGr/eDSTQlbKKrA9fWi72gljiGgFZPXwmoPzUXtjGhLosL0BG4PdsIuqOPAeHKmmPSMYb5lHaYuEk4YAoSTNyAKYXEg+SD3HsYIt43Eo1r8HAJSI5yV6t50gkmud+tYt3iq3Y3Yr3PlPEl43DqhhbfSgdQWyse7Qt50hOUd/hCG1FjbcgWNOkhEX0baunTX42nb2kkWVhyvceSHvVQwTmR+utR3j1Oh1nToCNnJkT6GNJP9ZOklf5LLg2+cxsUPOCNlPjnjSi25A/+h/ZIyEWSSjODAgmp8W4QGh9Fdu9XWez7cyCP5tdBPf/NDjAbqTerrHm5Cy/4SOVFCP5tVDb4LpifkL4AzH9sin+ed8na4ngQsG/9lJiKn8T+ikeF99Xg5MoFkP9efV6rrXOfi2NwdPkAqB+UzjbveEtKWb/kaeZNT5wfgycJkfHtsf4jIsyxbE4OFof25ZsxoF/OdmQ+DXmgN0BCBRtUfkfhws0204xQPOHIBwaYMQAMTwmlE2qKAg9PrOQRT+598g4Yxhqw6H4kufIDZqewBg2sFm8Rnk5OZksSu/rt5p06f0bxUIb1Z+yg0bBPxn4iEDGUl4rndTO8V3Q0n1zBn/LeWhlxs9v16pi2U1pmLw5OqW+sBekqVcVEQzbTfQUNd16ZKdowJTHnFmmfzHkBLWE/DtJhw8+rmpCVquZsddQ4Hu1zkwos9gfw2VhZmPv6qMU+uJpj52t9JLtxmImn/mepOaXrI7WcW6u+Vig+wozd1pJd+Qagi+xNumf6q2BWUZERDSFayTLdw+KOUeQ8mFg7+txpfscRQ//fBdI1ikNln0wO0H+8pyNglxxCbmrsBRu8FusxhSraiMYu5oSHnkOeMARjNQQE0bLD7wIMsRxnrjGlZ76hodbzrDGxFwKJyB+ei4JJHFNdzt55hTM4Rv0pUSSyqB6AsW3xjR51F7oZ4fb242KJ2akFu3LHaI9hA6Df7iar0Vg6vGBZqMunjSF/fRhWf0ozVmNn86K2iTZVR5iJza+1Ey0ZMn1dYsLqci3HMJq21mFTrwqJrxkfvpVKAYPVJts4In0Oudc8kr2nAEvpwon7b9iez1kAnFckcjyVEw6e9pFNBPm9fdYPODLWzWgRp7eDtCQQ5k9hHwRfjhQMWkDFlmbBMdiyQEmM/l6DS3JGuRVWIbGLJSrx1DI/NutsxwiU9GUGnmOfjsh+Is7koMrxpjI4FrECIjsLk6zipkYvAhaqON4vVENuQ3BupwG3FGBp9S/gAw1t8TWW3qm9IZvn6WS0efW7NT9wnuf9//Xw2nL7CwkxXnphyv7w6E5IjsVyYI4RQrj1FmQThXDuNG4i/HkNN2/PmuFkiqePxCLYm+y3t/bf2yqmuiqk05OOE0VdmhPXBJzVhJbFGT49CI3ukQZa413vxZGxrX3TKtvOyOSp6bjMQd0/Tn+K/UqG8zJ3VKUxgWOdIXypdu//mc9FK8/JJTNzLMHN6fk960jEV1edBaSBpQ1Ko7KICwqguBsxvpPhXUPZQrghZLW3yas5ompjdhLYYBGTTSUnIAIrwsx0eVPU4hE8691OhzSP5cGidXsf7bjhiHmREIulSgEBN4ZEMCj/JyZzgjCPRLJZ53pXuNZH+qGd0ww7ca+/OmKiqi4n+VFbrUWefWAJdRQN/E+AgbkO7z8H9+DdSNO/CIwcvMLPIxSwz/uLtETsVFkDZCgHcQvJoRbCSXWPzc5JJUy2tKnNaT3YHA72Hytn5IXY/1nhKSp/orLbdEh2XjqQiTSARP8OHX64hQvKGybG8UvIWwil3BIwR0lQ47sVthMgcvZBf1lnAPmUwG3/k1iyMPlEo8og30XQAiXtCuHXtSWLm0Aq3/+nGs0+yzybVcA+sRDfWbwLQ97pxGgfYFL8asHkYaSBRUYk3lnXWzYt2wciwJudX55A/wXDuQ5wMmOWnK8sta8w9suooCVahCK+LglkC/H7FG/aBoaDlC4m9YKuZd6B5I9j4XcVRyGruWDMwvQk7FM2VsOT6ag/SNlFMstIRnxJD8sU7Rp/s9CKp55xoKUr7NHHy6Kv0yBgpDxZZryTFOWSfuzNsCTTjcP0+CAe8yvYXFPRymK3yR8MbvLuzwxpYgw69Z+360MJIadj8YApaCjRQ+HArwMnSmDBuAyzw4vcGtOY0KYp2HYZE9o182Stpk/r9fhIxSayX31upn9QkQGobP4MMiBnTvc59uuQOsThUK7gFXWECIbj7FWFloDsIWgOZOEyoJt1w66+dEcflsHBElUKXKOGEJ37/w4f9/OS6bmC+BpMM98uKO8mBPWtaqKpHELvwV+n8BKPK1ObQM8AsDXR9FSNHjWK4tT8wWopC49Et82XuVHqhbPh4oVxS0QpyiJ+8KnAW6n71N0cPvEmpEXjAqle/KsSzCE11j61IS2RhO/XQuXgE/7ULS9eHZrmgFpQoiZ4ZSvBllc37+oLMkcrrigcFWhFchfKVRhQ64sPmywYWaA52w/vlpvpwNWIViC7t73NkffqUWnTp7Pcu5k4u98HyKNE1l/mieNAF6pphwtt+YUHGwHNybRVA4pWvLHMM+TO864bB5+J0n0YVeieDu4ztiYFyZ7PIDfFjZb+bXUkafapnL4xrh0gUV4PSLtKzfyUaBVGiqpTrwBqcfrlcassRQOc+cN+Gxi2RtRqyQ6IyvtDfADzxsTm6IW25YNJoNyD4sw8cve4ghBBOTnzxAro0NL6IANrRURU7eY7lW1mYFkDDE+YB765sdNWSr53+btKsbPC/BHzel8zx9OeuISjbuNNhNcBszgcNgnPAIcnVbDAVn7v2w38gSwAXo8bOD0ek+HgH4h3EnIOIlI8+FheumE5hrEpVPyrknsY322VX4K/xacU+4AeOTPkXylXHpKCEkJ0zdb609diPLj3kqNjqzBWLuhW6xE3nLx+OpvieD7Tbp+AMAuCV8vdBFMuTMuqwSzJBe3rY27TQNtKjfwW8sL9uzVGwDVOnXK81jgBKCIT+fCEJmL3IHt3wVcCAKVn9OBMHzNnVcpOqKwYXnotb6Ixo8zjVq2Kx991shJOUIsVfUhPoVKS8DlrOE5CtOnKZxhGXxHCYhx0jOVEVupX4zXjRoxhhC4lpV8B6XGxBUvFPLiO72Y31zITXF46Xa5aq+sSqIuaf8qkB92L7ErhGLriAg2Zx21tyEwtQSugGvsmDpHh4BD13VJtIFttYtceMJVUyjjeVSbR2x1K0InhbBIEUUjXWVqiSpK5RIVb8iiHZEPj5DfvOY+SDF3xFMnt8pkL+oa5j93qEOFGpeM5cHnNmI9oOqyK11p3KgbEa5T/Wm4Ne/in49VX62jmwtJMYWN9uGRIujOx8swqQeBSRz/CyBZFzID5qeiChdKM2RY+f5lXkfPRxjcD90hyRDRkZfQIF46BsLpV9NH9wiOz4q0wEXuSpczppLiv8GnWJF8DkEb8FOGO9X0yGtDRGSsOUY4n5prsaCiym2S+npQwL5cxk185Q9AcZ0uAKv36MEJ6hULvp9Tbl5ZGJOGchB9as9sh2kxrsaEGvi8hiralsLaOXNimuu8BeWZ4+CXDPVnCEVPeGfxWrcymWDf3tzHi2Jqb5iJpPzOTvYK5h99JqFprBpLdBQk21jdvBWVkCASUmAjcQ3YiF7ElnfdvAdXiF9F5395NBIlIQ7WgREOfMT7ZkbLwhww4HGTojyV11AJ2Z4aWsRgmrZD1bGmRCTdy+DvFAl7EX8W3lgqhuEbJKLAtYOeewq9isofKYVj+N/Zlpj0XRpWI+PTnWSrdUki5xdECP/smGRTAjw1sedc5XdmW/WbsDrcItRKx6mzoMMXLN9JaI+tY6y5FeAvryt0vaiCOadLZeDEw51ekYE3HIRfRdu7gw5tvMFfxGYzHsjOhgru7n5z4p8WLn1XqqQ91kXfZC+fWtkS+HgS6LiF8ZTT1epfije3xquhS3eIIMPp/2A5DP/y9+LVi3RTWaGCoJlFdtm6h+lqTW4N1kBI92x7P24C/BUQThVP/PEaHphDKIVvHcJ/J7YVoDviEcgKRrRschzIsKMRj1jm1mc4tnn6NJKIsnvgMw7ySRyhsItelNJn22gNY5yn3nO/se/ZdcHt+s1RmRPrB2Af54WLPmuz2npz1dAZFTbhhz2SCHvgKOQVBlLrOfQnqx4dafqG7xkz8mIl50dvIT3xBCrTTz3Ldv90IIa18JhPusiVyxMHubWL1izCMNhs32KfElc7WKELOBjzFCCejAESi/8uY+QJz4hzkKiWuWQxZudiGNK40/yYReo5IGjC9D3rHHRIMjvxK8MbjDKfO9GYXqMPKMn8dCE0G7I3GhhLb9SdlLO4qXuUYoSPEBlLTEZOZ/kWn1FZtbj58wr2AeiF5uwS8vgdO6IfUxid3aMd0RJdqiv2+nbQXR58fVMGxNqP48kR6AXHlp4Ur8O6C6xKmrYtFeFmuhTclBvDKARJjF1alJQpuuju7a7JRDm+ZgaUB4vrldUUKCR7siYAAFjUAHlxo9EVZM6TDmYrbraBiajytmGHxuYuNnfHfBvGSvIHkQzPjBaz1gLkahNjk33MEGVBUbbL3vyirv7mSHRMaT0GXWx4hpcS6eiROjKBnCh4+VJ559fx/MZS9TPJFVV8P/fRIzs8oafW2MTMmcfKbAL42NXE4pST+MTVHvrzH8vvVRqEcyWGESaTGmGB/me9Dugd18Tm3JgSjsMk0nUEgiD/edwu9ouUBPPFTGQ1iVz/Jxv5N1lbmGQk9dSjH/HBdWjlz1W2vRmAWNN11Ra6xaGfqDNOJA1Pz7l6H/uuFAcCj7MWqunD0G0IHtKC0FmmcvaJA/vnaBUDFw3f+V3J5tQpJs8YBZsgt5o0gD1q4kTxNNJq/bMQws3bKLNnyM9wj9S66a5OnLDtQCEcf7fe+nFk5Y8ETQYE2lw0Qnjj+AyxU8SG8XyMZHxnYHKMkHnX+UdIeoYPl3LhkphiFPIfbdz+O6uicKWlX9SkeMVoLvzLzhHofib/mu9I4GA0sp+o2N/fEbIOSSXjEfMSIiGFV8vfnlvET5HlxmN0A9OnxMLEHIyTjsVGOLPACOxIjiKX3uHVxNxtl9yi9fhkfDLevLRu5qeIv+HirK1o16hvKU7H+oQbyaiJPzEllqKMa2Yposyt+vcwCc1dFOyYtFdt9qodpn/I3oklivCldeTP5pgnERm685v7oVOpcRwET0EZ0szUw/NsQXTcXf7Kxo4U9fKmYE0VLH+YLPZoVGpCQvbeny3dNDugcp4M3ut+PXt/iCODFSgPHvpxCjb53eD9zYE3vK0ehXv877icYl+Hqex4NoOsN1e719jK4IuS6smgAjuHfh9mBCTIiZns+jZ7TYtbCpe4Kmzq7JhpGgQKfAyA4L1o5p6fNv5XxnGefSEtew6yGcvSzHYAL4MBWm/9t3XYlRxfc9sypsEyyCdmxFAoJ9/J+nWcRC2C1pT8e9HMQvr8jBTVjV2ZwJ9A246I35AWVAhKwWk1K8LnAVdBYbQYIceSHphffZ11dulnK+IiL7nwq7SQxkz/4+nxjzCZNkBHD35NeLB0Nq9Gt+SiBGOWccgRxliz/jGYJ505MrHCFMcY/uD6gQRkYuKqGQ8PIx282/9g+83CWQDlGihYyvA8ON1im0yJia1Gw4734sjzSgD2IT9XeYcy+I+Y62lcsLzjfN7j48tBhOVi7AGBVVc279nfOMQtFadHwIyVlUORz3Ha7Cd6IGQtN2noyIhv7Veq6V3kAHmq1kPDZXmJsxn29m9/WaeVhl6QWroKX4frv4jCrqzw3CselZieG2vJVpH112OGU1zVKy1fPN4f7+P+q4Rux0v0C11kEZOmsZVBnv/5ag/RLN0J4wLEsUbVIw0OQacofFNAW4nardxEwaigjoD1p9BMy0j8weDemqmaVHbhrIP4x/8YyFEJ5eiGTQFjrtAwcjTbkLE2nHT9iGR3QfZMQdC8899IUI4zpIXiiX2ors/29aMdM6UOklQO+8lCVURghLwHhVvG5vJpTVPjdL0H/9H+neu/SgO4togbvd0aVxNOHW04op7Aik5ErX7Rx3GiXKopGtuOyS4W+/Y5NdVCyFEzpaUUxDxzBJo9S5SVIOvgK3Fqnito5NPAMkxsSPwXJxhG7XApbxL46vycEk6almJ4Nx8JlDRsap8AJw9C9e0LGgukPmeRvk2EF9beiJGbveqVO5uGuZ0Dif+KOfOpgrulOSLC6Cx5PNf7ag+2O3BDefSwnEKjtuPW/1heSBI1sXJAC/B2mt89hMofoq4FsjUX1vfYxXmePHIpLlZd94fKEq/w4B+SASQWXuSprkaxIwAREemAobuyIdFZsOCazSJAlpsjklekpbQV/x/PATWFf6hiRjpIaemWzHDqkWv9wQ54hM+1VRYeD63zxC2GC/g5kjnN0GXQQL15d7gNOaUURg8bfOpKOBCsFRPdf7krsYdtPT2AQgU+3U9BLEPBW+EYBVROVDutnR0VIWz6OaAHSfN7b4LKi+29GJqNjtUufrp0gZ5n6m4Sc8uWeZE0LuEWEarIE7XJVv2Y4c7BpJBYtwZo3EXlMEfK+/KYd6ldA5G+4z7SqdJ5vCuCbCYXlwou0BHeJDcCc74qFaSBefnTfXaBwxhZOIC3R2jBRv1okofaX678WdRVJewhsbJAtMd25p0/mJqjmm0XIqH6y92ka8fUJ4ikCEHb9ZXoklJCPZjejjlq0+RCvkwoIgbAKLnjO+WRmuTZMaCtq/5SS06sJwpC2rVIxcxFkikvZ9UgYDvBjmcy6mgOxSf2aj4oiUBH2KNOVhYbAEoxJGf0UXVC6yJI3lwylTUHZuVrk8TCeqE7IldqLYzZnIv6FU35w6BHEaTtVHr0DhinSPsWlyPcJRBj7Mg9YrL973EZ4T5KN3+krgVGx9OB2C+Hq7cO3DZwv7BcQcmfvJD660VstTenSNS+61IVaoWTFcoJKa3WI6i068Wp5SXFzw5XaxLysn99AxbAitK3mQ2K4wK2nwS2K9I57UUJJL2Eix5XCjfLBMugtoz+yDaJX8ejpJvKIUHnA6xuuXSs1TxBtiCLfaWvEGTUl61DNW6RaO5XPeHJWicx89fDA16VHafinFpN8Q1FcCmYAq9qBmV7D9LLlshp4q01m/D0sYf4te7gIhkg1lDprKLebEWZxSRvPx+dDxlYP8jA5+Zf4+FKWUQFyOU2S74HCdlKwUb5kElt0txEh6/fR+Nmf/XCMGhkBXj6gFQlz1ED6IYJOBSbpe4a+RKGCRzdIMIf46auQ/+rofMMuIpCZKXaTWtDBZ6Hw0zyEyE5OqSVtH5zxGyF+D/1WMtyyDXgLW9urFU5wYMrdM7Bniajq/AIkTISfP8vuKffWFcX/bctyP5AJCF4AbPbsugclr2tedRidBSrzn7JQmUZ6/+05jlYv6X6yvoMIvgOADzxSjcMBkxKIdxyy7ge18UZRhE/jWWhNRc83LslsDZtwMI/qwp0ft+uLyspiZTxbpem/JbMxzR2hbFXBusP2J4aaGg8+ILr+BAzJXct0npzlPWc/5M8nUFBm/Rt2rjY3Y4TUABZoWcH0SZ5XdaY7EE8HMkjO5gkk8TFyHSjfx8VPxlqIWPwY87/TRI3CmA9TEbzBHfjb+kfRpxm8f2GV+/vSt9H/VWyqBqeq0KpCFYMz3Yn4WIeNK9VURLGss/DPQQ4QEUD9/mDkeA7JdBGej8bsWN22atEJOCKw3BUCKs8tfcz3Tzw+zMIfDl+/xYaqhVOtYVzFVa74su1LQ7vJY998t8UMZ5eOAKb23cxtd53qMuFAt7qASEquuLk0t6BlRDTAMkUCbXBisdlsEe6ziRoviBWiz/Si/dZ7PArfKV9y4WLIF868anmhfh1ynasHG6B35c12YhKUDJTfvHvkW1V/0lgGgRel1lQiiiRaw1qVAidC8Td5j31mNss+4vy6DZKY+wO4Tb37iLvrV10s93tWnmCfOmrBg+QCmOJ28K7LHjAv691Nw4BHqD5HUizDwF8Jr1+/g9mh9VtDbFlqWWHIhzC3GdIUwYJ1TFZ635aeXRixwR+sWL+lRWhYB4OF/9QWaNpIS23JEFCce1M28hiY2Yje6+CZqZnaMUEunyZTQ2vj4Wjhvuq6aNMt3SF2NDALpfA7GwtXpM6oEraGGCaDusEzIZhA1SBJMcALCuaYWH+up274VIaS+Z20j7uZ+8+XJKeuuxuB03YqShRgJp8OOWbHPj2DcrwZMd0LhLtvLUdtLjK1XjRWe3+SCJ155qMKjSGoJT7OXazd1QEQv8g7ceWm9vH3jsNEiCpDrlR2S8UNu5QY6OrSRLt+xgoE892iZn4F6ogPWn7GIc6/wacHC9+SAaCrXGMq8u22PPAmKW6qusy/Cvb1sURWs91AoDH1/47I6m5weBDym9ZL6vv6snGKzEkmx/mizzxO2/d9jhsj5JGq0VesRNUojS8vIsjpEQc6f7aNtim8pJkUjBJcFkECaD+/ZNVZMlhe2/xyF49N6mnrrvJaqC3KIgpeX+PI46ds/sgzqV1lacXtfl3++/eQ0JSH1xdr9ynSkzSFkohK3RS6Wvk2HkFdFvi/RPP22fyN2/KRStMytaGQnDvNWwvm3HBNzCQqrJMeuLfgcdxXx2PkRH2kMgdgdxOZaJl1+pMWceGaffGx4/aTaNUjRuXSQw1HW5LaEGsWS5o0pBX3Q2qn024AsMZ4WgSoMclBqZw7RjwgLFaapSfWzvrscJvJT7lT1nnWGpLusvzuRX4Ur7Mb3987PUOoKemGZbAtbQdONc6zR8puWUPuJNQN0VWlLXLzToH6FeLbIncJF6wkU1WknAw8NhLoGK423X81jSqFSYoYAU4LBemEkFE5wmmtIOG2hqo6Ysk1civJHvb410Z0jFeL6b2r/iio6LYJGWLRmNYApmBwkjrxGedpinKH2PMlsFT8R4IvSO1Q/TrJapSn7Uimn5WJd2ZwMn+xy/isCpGIwLHo5eOF8e2qtsUtJn1lpu/q0KlbzcJVKkINwox2uj0c8eVlTCb9q2wFQWGo2li+mNd4excPreVVGDgCx7rvNKDoKmd+kSbx5bl4cKNEKso7NwwugCsI7YUbbfQrTIz95DGqr8YGmlVAFY7ReRFBxeQEwCAS8Pck+EoWchG3NQoXGPRRC6dWjvTfuzgIX6TAWosFX1j5ARvnMz7uHb8sc/hdGe53Y1GhBLHsm33hLcRojcOAe/U+vxo5mvHtOL+G5tZYWFQ/olLtBEvt2JMhf4abUhlikoPx1HagsQb6w6dcQB+1LJojdxAqUNvofDLYNmyxkAWjHGKfAtu9OMmFi/+ls6BuYKFiMwH2JksklBuyYn2Y/EQDUXMohqS1fzhhOaZQN7tbpe/iSG60PW6ExRAwifxd0ahNeDPTLxJzsqdAi0Bf12H+YHQ14zQ/2eL+w1QOMqyidPu+YxLVlcV9KKjFLxcs264E6McuP64dJ13bqH5qEkrOb8uD/NHMzwLZSM9aIdnEIZJqoY2m4mtSs5Dr9z+ECoAvwPKJnNFDSu9mnK1M370sem04oxi/2DbtuaNTGSIcdTF1nMlEIhToTGdb3D8u35a0FczsDSkIeglW8kskolwMeQGk42jDzm5cGN4k0a9hUMWFVBZNw4kkecYZ7cXD28HVEnx2NP4ltZ9yu+KhKdyoQoqyu4Y1AK4W9zKMlcGxNCBkWWOouv/a8c7z82X8QayvtHQXZpk/vvXJX2PN0zybb5RaDvGTqzH828kPUp0gd/6p43AJTRld4Eq1TAUigIQ0xe0Ly8BDTFMxiHRIutgmCFs9G3Y14YCg2eJDDSeINSKBWWGxo8ee4JLZ1IJOZ/gzCHfe3LBRtcmrZmbaIcgmSk3WzuoAsrdXvzWfL35pOtM5OCgsh+0VILDZ0B76BfCPc6hEUIXO2yFPjOktgnaAlZX8RsvTBRhXkWZREI8zXzCLY/gq/aLTRjML1P/Cg27Y/GqwzIsk5/NzftF1eznMwmzHfKTyQbtJnlVAdjYTZboODZ19spEqtko8XHTXG+bJkw0SJ5ldbPwF5mnB8Ft77+gOckX/ZXtnJUKddpnG8WdD7wF7ts6FylfXs28zpUzW9MhRdUR33uf+Vyj4369xOqTCCfMG3sZDQpNbb2imA1yOVtCcHPmiMpsVJEBnj9GU9Ug0XFsZcLW+lNt9SGVZaQmGF9DUYX6YXN2HELisrVjI6P/EcuXckkybPkccrdd13YCcdGCsG+zBHBVR6XnFQxMM+xBRkd3vNsyGSPet5UhTQz4f9n/DLTp5jyqEpzutU+uz4MNmFR4OPkX34l0aleUV7hncIe9Tt+741bMbwMEOgFSrxHpmpF/eD/gv1x0O3X8czfGHt9RvhxA7mzopgopqMudK91DN62ZRzNirIM6sbVcaLDpDZJO71uSquz5UZ2ovyog72wOfe6MtoYB8eCHVWG14gKLoNAWhK6oGvCXPRiDCLeknVfukEPGcicGjGSHFxhFuv8SzU/ae4c5GL66I+O1ktySGld0wT1w+pPoFwnCJbBqPqsc2cOmMbJXSXJ4MMiA9gzL7m1vXJ0qAFe7L8hUZNJDEzD1FE9f0QZ/ls5bCwWzqPmW7/FHu8vpM1/05pUCMA7gOExZa4YsLbLhuUNVZRPTPEi8MsavBrDPWgRnTj/dHMLsGCstknF86wK7L7Fq3jQn58VQ8eOzcwRBmelC2fQf0QxGCZH3+tZjnkUbwOrWftRNjN6hPYRwc5zYcTrPcPgVBD0TYJUUQylyaxngybuSHm+vuMFTuWcoQ671G52W0Vdbwver1xBdyFebJ+4e9gXccxg1vb4KIqzJolIppG+nwF+0BJivHjvUWf+GATZSPDWM3tRFIh2EakgFuFdm8VTQDMD84WZmwz9tYz3ZyXytVqJ/Ryh4bSPB6ogL+DTri87bbTg1MtMY52uWk/ZNdH5DM5nqd5NdByc082SdFft0FVt7uh2w5X8LG8q57HJ6LiDzQdpH5UMbfiD5X9+AgmUkrX1M94qbabrmRXSF8pEUoJRYjdvK5QbFQSMvzx5h5AGd3oYdMiF0v0TiwQi/pzR5wQT8txfn3p+JgontWV8Z86cTDd+q3WXUFMxF5m9EDO++TMwIrvzVH/7iw8Mb9NwSfXjPdtKZgrw3mBVWuSHRlxUk/T5RreKxjYEg/KRQjkMg61V9EiecqoKbPd/UCrHuY6pFnAZMCRzSHWZIlt1H/E7DYbHYDVQgGGTfVtHeOcSBfx8ONFjgBKQZBhOMwAoK5Sa68515p/0auOdyEdrldfu2prrXnjUjW608sysJ7Z/ECdrauzB1Lj2Nc/7PzYmkh3Ys54xPKEoVRFNvgJjDgeyenJYvU8Lkibpv86yUYPAt8Nnq5vYoMNXFIMhprFOfwnWnk3Rnieit0Fm5u8nKSbUkU8Eun4hzRVfC2C2a66neQoJGqrb9nnDSUyqXJ6eR8gTvdcpYxKuE4Xn9BE8ub2hPB+ggGCibKYT60F9Udxcg2CsJDcj2Kp8QO4G09Rq+Q5cgGZVOeyqJ0F79ABkGQbQo/tp3vkKZeZ32ghtlhLapErShqC7nCYFt4V0KAf6Ph4B8l4HSmo1Qks0N/4RnH5nglcJ9sakruUA3NSXyKUycBNiZQ1EaSuJDVKNJHfWNt0TiLBiKvmrVk2AJWct2yLnJfivwskZ7kN9oXWOcrllihZ0bg7LkZNytIe62YkX+oM6J15S6TYJs95uWkVJcUqo13c9Y/6KhFF3LKWs911B63I8b5Yn5X7+rfx6GjHauCB1teO1vmwNhaSU2TNihuUJeVE7fEhSI/S2OZ/N7c3g+4SFznS4gYxnzfrWm9/kKlVC5QNcR4MafekbiT1c0nZnfzMMDJjFiRLnmVbdHZhaIXAzZRkeGEt0sgyKmBeOLRV5sEAsPta6eZWEsveZ2yYiWjtThA23mr+veyptUo3J84eaYQHd6exTPOx5IF01KvvcoaGBJRvYPTxh9kUU/9Y8BRRFn5F9lrxDyptkVUoks2QN2HeDonNy4ao0j5w9o8xPutmckUmzthJtRolcbvwk6+osY8nv9jCMDKu0/YB+MkUYhKtt+gPzfaoJGmBWMkd076g/714/AYpA9TDgL6AIemfMkZfloLvhPSMf6r7hwokthU8rrgl6r+YG33dW5azXf9nP2zS+v3f76xdPgWEt1uDORCSAZTxXHbEcGpWvK1wzik4Nx5VLVgWMcdYK+qLgHlkjQ+PyRiVJ4Z8JriR681m9F6Y9PaAwOHr/sEfmBMEhi+GlBiMnTQmiRXvLCYvuNMxnANp4GpGh938Z3aHJjJnZZ3wtiiSb3iI51cZO6/LGAu1iYcw3zBwKyQvXZfyozt6IAWoO+2ev6a1jeltlO0kf1ingdYjuIT9hEgEWDrIy/43KD0casGKtsmkYRtzv6I6LXR4h9nqUlUKv2eiMBKNyYjBH98dhhJlJeAb/msHPt6OB/RtuqffNFfsY4n3DgnOAH/q+U9b4/hNTaHvtfcuzmRhmVj8NOjvPk+lMC8jKsfX9lFzXD9piOX1ATxPOPoLx/m+zTmL3fxWz5TKxZZVLkXmNs2jRCtyguSxLmHtnQ/6cN6qP5fdpKb4IxOzY4sZXGzv6WobGfQ8y0iJcRI0Vzllb/DD+ELl8qF0ZRnGyJ62IBObgDA/DrJRgLip8MU94tUQjUykKhxF8z01SfEUu2+YOtjXVZBWqoOH7all1GU8yBDB4s+NiANzYgW/UGUAmaQHpRxChfAXMIIHBCZD2XjVtdxmXz/MICfXm+i7ayRfnPDTLMz0ZC3qDUfM0MWKng/O1qQG8K+0z7YdAQf15HbBh94lJgDJBjp09oE5YoBXuclQuOUOZsbQsUN+amETyqaQWY5mTFqz1Y/1Ebq14AjLNwQYRNLbIe6v+tmY8WhcC3u+phKJg2urZH+ibIk40fDl3l3bSXhF66kPxX0RVXZPoohDPToOrj14hqnABfw4RMEott0KI32MGN6VUOSpLF2WndWx+TOuhYwfe9fO9YthxFwvzDOeZJiOW1I7Zf7/X404UuY0CP20hSFqdgQjp2/jMHfIb8TBWMPLmevZnQW6gbizp/HqxikhxWJfRifkTSyEhnKR2U+4fSr7lB3IQVYg5tnM6fsagLlIcwff2sqwv57GaT3doVm+zruemKZnomaOm9Elv4AsNPYLnZQzDAUN0EOrltxBdQ2D2ojdUY42aj96CLxZZQiAd7IJkWFQl3X6wOWL2vyfiibwvrvqB/ZgPhWBoLhnL/cAwpFs0jvnT32S+Og6IBME35N+ni26tEyK3Wo5MavgUfVkYMNbMYmniYXqgDXclzVAOsAjsXm2UIwUf+wuEXmc0mxIFtT7yXWpXtEp/mJahrl0bwLc7eM/PCgCPpJBErt5j2jvDlXJ10o3YQJbDL+2k8ieaz3vOEyRmdVle0CI9Hhq638YR9+DresAEP3/HPMOdin4jT8rBAUReGpZ8jRsViHUnem2UnRET9GWRg96acrHP7ntVHrFC5+QKJxS8vCaN6veatMZuqXnzQdKyb6Psu22vgSZ8UssfTef4lvs/mmeE+EX4uVNSwZM3Ottopm6PFfhSveOclWh9c9JlcPVa9h+kEt+07J48nHroTh+7T4DuTv4//g7znH/Cs32uGGxxoUpm2UkRIPwnZ7aD24jV88RagbHZRfl3LUNJenXmj3v+JBXhw6TmyN+N2s7vyPR+MIWBDL4UzeYPDMn5zXR/V5ETAbrfzL4qo9yKnqc4JZMoniSy13zqWtvNA8MtZl4MqJFP4827yTtkrY36+6q6NBlXnZt+D7GWeK3QWf6HIPXJaRugc0eca2OXhtrJyroiUDGVnvJwsxJj/LViVFyN8U/Vj8X0DDZq35pTVMtEYvjgUBmjFZq67db8vtgpGMscG3PvVeoNBHMYc5MTeG2Yq34igfhxz2ETDEevYYnVqwgXAVCUVaej7hV5rfgiDV2DkZe/I4veRaeTMnOoTJbVEzEP7HUUBQl2dVNlPdJcb3pc9DU2HuGnkTvh0c2arKI4u4Q4x0l2VRoVGibRO4CLis2pg/9H4Sq4RSfcXxbaK/j/VoYuCChV/6NeWZgZffaZozKqIKMobiVa/EgioHUTp44gRxOn+iiqOkbQIq9psryUFaKK1D4hA4j2MItpwDGjktTk3NETC+xwGNcykWK7EKSXVY5JRJMlgvYIJjhzvA5HTFX2r83e6QEAt/0HOHygiQ4bjW0bABKSdNPdaK7dbWvcfKLNj8Cv/OTiNcI+W9715iuW0GTiFJYdm85cuSTHJ1dCnl5fHchS8YJSad5xwGAeWi/FINZnVFCxcjyTOoSMnud5o5PwThzl3qBCCZo3Td4s01lIsN+igs3heHpjbofE8eSB93D3CF+F+guSYN6PTkC6Te4874kaXrhBDnZuWNKqqBhvuvCKuzzAYEpCmFlcK1VgwxBEF+uZXSKEy6OswC6HBN4SqjtuxZcOM78WqLAdCS1dn+5bfO2bHEZeCb+1G+TvmlzML/zeo0feiBD52/l0juScqZCFJYBCvCRXRmb3EAOpXkVtv53J3WntLgkKs0ekag+dRtVzonCSbpoeYOAhH7wMb5dPGqV+VRMpBt2Qykpu2hN39GhKmBDLta0m5RMhax1yGgLWGMQNAmPOTGvZbyAlFaFmtL++wS2Nnat5S/T+4gEIQT9njcxMcr0PfY1EcPxLsdJkwrkYntmEj4KlW7tg8Kzt+AUysP+uBDdOym1rAcOZRZKny0aT4vMxpsYOW38nhWT/qth1+upGd8gR2KDi60irpKajHgwtbhbpggqI3I0P7/PulwMobChE60eT1CpAkve5gqxauS73LkMfx35YxKuoaIWzoF9quOwjhjdbM9dlw3oOJE9bjSEsw39wWxAMgqnFlFgDAkOprM0fB90NV7Plc7E0LKC8aDGzhMj+gLOykadommERWj2QGxGtANKMt7f1N3bRuY5BF1LoM8QiEKHA8eaM4SlSdcgPuF2KektJ2QjQGZW/kw5ITMPR8XfvOPE3iAejDxNRMNn2Wb9tNgFmd1w5/nkn5Zpaf2eZMqABKtGrXfbwuxM1SCr+Pe+TQcN/6seeI8854ilNdSHna47m3XXT9+bp82yA6AlnCvxe0p1x3ckYdylRImippk+jvJ1Ni5KYD8KtcMeKmBY0eFeKniD6FLaE8DfpmQkIotEDduhvv7LGGSMX23fgWPGYsc55XyM6btr5FAeVd18B3yka+rcrI4U9+eRsnpIJ82x+hRzZ2HhM+zMiuHVPJR/0fcDT8kWOtAjj64UKJMT06DBGxOWjteQ4lZIcMmH7eC/LjrsXmZlJ25VrefaVvm+7zl5wcidUc1sAtXjhNVrM9kn0FQt/X2Kcp5PUn84ct/Wl/j8lFGNtci7cBS3qvlzGKAG2gmC5i3NtxBQPMRfQ3hyyycNHvtg6AgmNP3i9dIm9x/n85zR/J/8WWxf4lcbbcK4A6yvbY9FRpCEYsY/fAfZFSFT3J5lMlVZn9lx/9MfGMOpMDln6+MN/dQB7p6hMoGBxGNhIhicDB7R2HNNJbCdVsGTJrPvti3eAVBAOZ/pr08YY/Omf5eJYD7IEc68lIfis5QapQX4vtkeiy446FI9uRntYagZYNQNAaW7YtUb6nAfXcIt3FqrqyduMdpLriajGeAsWWEaWj4jEpQdSoiYszEh6A8GUny85gfS60x3A6UA8hHyc3yfAxFNqfoi1stqfTuEHSG0EEXPlCqKVUHk46Z6gi13/tL5mrzmQ+Jf+uM7swL3T4YjfWTbjE+4R+DtXUwu4uu0uVDF/cxp60JIG4waOhyCLe33tnoLrzSpt/uIVikChPuFNTs3JH4CwGkJP5RyFx22KS6o+P9/Twkt1ZuDsjab2Isp6skLYGWB8EIgbDou9UOe+ab6fTV02gIgVhwKwZej19TdzM4MRzGlEAxpNek9qTezgUBMKdlDxLY2zA6LsGvqY/DRhWVHfIS8xXVQg4mdzxDxKyi+hnEKPclE5BeX0jXDFdWU4u3Qh23eJ69xkX5SbPIurnPApQ7tLdMnHXduVewnzjMy5VhFtxXTBrLgOF+BWbdIn0uDPYMNUB8k4lt+PBrecjMVl/X7p3y10tGN0+r5+P9PB+73U2IIHTictnl5lh+uaJvWhQJoy5SIexc11boiOkureGdeWnB5wU/5BEjluutsnCx5VBRUnMqh6G9BFm5G/NqfkNfMytAa+ecVHWesF+zC+K7L9ZK8DRx2YId6dpt2PBrJ0z+4Pya1++/iaBruqYkG3JNvzlpZy8eHnwuKp4FV2AXVxrlsm4KSwrxVirtKBbKXL83dPRhQb4IZTEobwu/f7RJu7XKKb6wnQu2DcpFeIayMgE7hpAP+V4RpBViU1UWkR+GZFA2+3P1QQiQfpiyIVseDLtcHB1eh5S0dCuhdAHf4VVRgHmWBE8jl759VuQIlIf1o5rOG07cz8wZ7SbWqVsXcQNRpp4Ot4Frp9RGuIpoaj2WVihQQXXWhMipidHhgTt+UEBR3/+6kZu3uvRxtargQHnOtRB/ubQq+cJxXCo1Ldp/AW4pWMvxLxfGA5yQ+MhYLvbsuYucK2MNeMYGFJnBgjd3p5R7umCpzJaSUCO0/iJRXMpS/34FmtHMfz049QZ42Xn9OzxXPW29xap3Sreaj3GzDQH5xfSkDBSfCCiBz5Gn3Z85Bp7l+V2r2cF3La7+je8D2vjNv8OAT/yIajlhKNui19zZ2nqVHPWQC7fFrJBFABg6ZPikzel2GUMdiSkZf/VstCHzj4U3veorPP5DWzfvlklJo1nHXE95iotzzJOVye0dKcvq7uBta55Ce1L9BKCUHGw5C3Km1RLoo8cxUzqlnhYuQjtOaE5v8EZtUxTVvi/n2IhCAyo2XFLxzT55f71EBBpGbhyHK2dxGLu7TbzzuJsk1l3xuzR+UOHlLC0mtMPIGoS0H89tE1THiPVpF+4bQKq+LUMYitiQ1JJTqqg9V72ZAJ+VM4tCTVkoLP/7Vc6e9OCYK154xvQng4zC6wsfIN3pkFvz4Vvo3VLLQKhe9NFPLhLDwU65s94OMx/Y3wZZnhiz5L2YH7m4UhmuzccQXT+d8GnWFy2GYAdiqRhZOSyOg0ufza+WTNWifs8v3Eh2uMaP5/wWl3P7PI6lTEUcZljm6p9cx/gF9iWkZN5RK/yAw6nlp1HsAsqG9SHgvyrfv1l1CMMs74XUBUIkqkgzT4V37HP6nrZHvXMlPzxgD4FxP+zkZAOT73JN3zMLiQ2oN3uVex7F10T4dX+TTlwzGRstTHC846TvfZYlZMwDcibAZHD1gejFjGO4xrQgRXc5M21ISicxYiu60AW8oAXcVuw0bnoz3GJP1orm7SzVYk9DSUSj/oK6qs9aVPnzpYn2knSWO2Yl2u74zccjGArlCcSAK5DBFRtFtsCUGcB1OZLinmTbg/k5M9CEkgbEBQu0GQW2ndf2zLuAVIBNowfbZONRQdEL7Gc0I5rPxqfAK4QlBfKsaqqQDiUf6QinC89IPMvFQeOzXbvFpXasXkMtcR1GNJxzpsgxBBtQwUi6hiUfI0h353spkwlNW+ouNE/eqhkaqM90aE/8HRTvliH83uISv6XVFmvbKcVfRFYdjJxEDJh9ZeE+dhF+vQCgrTZ3uDGSArkf07y+SwhANHnCDKh+AuUUt1q1xaX9nJGyVTN4NmBKdHNrsCIOTz+a0PwvpCASUTQPpz0dNlKG2o6iteVXyMaq6QlfDQa76KAcerUdmIX86cZY5jx0Fy7NwtBwjQj0YG8g5gjaaf3OPMAKtiROkdZHh+BfET6BYFLp3y8DEGMwF1nOHojzQyW4wpNKZQ+UoQ6FlxSgUiuy+v2cUfw16piQYs09I7EpAD7SZvBOqy9EoSwFy+gUeXAKGheaWiGgcn8b+Gg1Wfc7pCGCd2A/GK9jg1RQliVZO9hQBfvVKWAnSZRJauhc4EPPuhjKBjkNOWCoTP7M0JG4lLB0TwuOYTuB96IV9dRkZ+T5WfhTG186BHhemfro+iZLIL6rj5uQqGB5OYX5ocFlzJgVXdtozrLzjInKtWkHS2zbyz99UO4Zz3nHPl1zLHT7GgyyL/8AFvg8EncTYEyaES+dwdLQ5UYVkyMNYS8wNpDP62PFquvyqEGNAOtyDTRiQx3y+bc4srwXtW7/+B24ex4pM8YT6g4/bXB4QdHNJULYgYNkOi8WsfA/VjQGeKwOZEqaxwdRSTvrPa39lKtugmaE6RJg9keOCil3H+lY13uYLK5Tf+vLAwfC7leJdPyPp48Cy+QjxFwhC/KZJkgNRDwJfynIqs3mJ4KLtdYIO9p0mlXJH6wv00cjvGcX0oOtZqgtM8lwJYrx+91oUDNGaKpIj3uXCudC/tT929mmJpIAi7Rqqk6jBCWmx2/g8d3zdAhUfj7wMphDqCyr+I4OHqsXCDcuA3FFo/3diVoqnqg7BCFLk5e6wXpj2lhqiwTN/1UpXVTd0w67kWBTNu+yyPKKwX1abCPvEDAl4sx5MQ+IemcwmjqX0AdqWvvMoKxodx0dNChvj8cKhVlhkn/6Vx2Av/oVSMuK2M5BA+yV1YL9E6yu3QkUU4r32WvMvL0igZOK9jZK9zzcJXV+IXnIb/3BiEI0DlzhoPvp4x+TtViS70Dw5iv/D4QcqlRFatzJVSvYzRUljBF3Iv9y8igAIUKi4ZOQ+qkRpaOop+yL8SUji4EiLnNlsLSbfDyH7PCNr22JNR95TU4Vao3KKAL9uvM2MfTHQo3aQH5LP6QlOKBCYg1IFdwLj3lA7yFFzVUIIs79zB6hpnx758iQGk7maQ6QC5ag6x/GUa1SdU6tWd4s16RCmrP/9VjtcsJVVX9EqONPgZ1AoiijQo5+Sg4Js3n0O1A+KIf5F8jRUbLOIBkGewR1Vy/9fWSWdBjexyToqLWk/F2/3FY8u9T57xkXqK7gDuye8OSndProPUgqIy3EjxYsn/yu6qcuLsa21WmutwmuAzoBYduvgPkAF+37RMP2c0BrEUt2uMOKAivQZie4bj3o67V6dbfPxOe2A+4Mk8ya7GpTL1haMaDiMQiAXOFlZRrwjXGxnE0p2XtR+k5Nv4jWw2ld/4deU/FDt1fIUzMJbdzEYqn7kk+Q4yoH8IbG5YPvaep2+5psHGWUfER6eN0vpWE9e+1XnzCStsUQBKgHZxVgkpsospIi/yJfEnpm6E/YBEMkOthh4MdWxej5Aed1L94U0OoScUVvmm6GmgcZ9Wi7RQFDKUHcrx/RljQtRf7b2tZ6J2ScErR4yiuaIerPcDSX7x4IQhMPFUOpayprMXwcE7dG2OHfAdKQZ0aDmTx9nrkTpMwjAIij8GmXYkHE9fiOmgxiZq6eJ9xUsQFr/0/w+8sy8a8Ga1Bnpf4rpwwrtq1nwSB3yO9KgiYElQiydrB6L/qiv8Wbs30qOS9t4HBJAGr7bcB5vjTV5qnJROyo7vZZv/Dp1W6Mwl2+JfHd9ebGuME/ZNcAASB9KvuhLKJEXObGpPNEjZlPPA7CEXQ1d+NNVe7u5N5ZsNs9vdLkaR0kKt3ohqs4CgCKFtWoqDp862IYUyC5HNZlTdoGX1zL/7j8cdb+qqGsaT/GzXRCdf4vcCZYTit3jcnFXre/juX589K7hSxYfQPCDJcAnXKCnI/rkmzX91Fvo0CXQrK4SJRo9NjzqRp4kahfeEa18zuJM2oJ930xB+3hTWKVLx9Vvnw/MCAROrGirnKE2dXN7iVPuawPSCTP6frwDwb68E35aRmtori4i0oLsyH/5DPCDRIk4LRuIidIiO/UDBqpMT48191RQcroOEw86+gQXiifs1PqdWCoG7Ei7OdRxzbXuNZ9zBrqzM1G3yqXWcrT1wvNLZZgTNmN/kS/66CvB4OndF0aPIV5RT8W71LQCG/l3ygMtkzmyDWpIbzG974GPvecuzSO/jaqkKqeEafiNpsdjkK4IPnd2qPTrMoCOfsLGiFWWaVudlwKx5b0/u05GobkS0VfcXnbHYi+9IXie9wI5ZF10Ni/1W/1rsfPZMKANXgeLjH1q6jBHkEkG9zmXHAsHv+fsAgE+BfGd+Kq6LWUB/ul7Y1F0ah3QxcKeXKil/v2V1bMsjT4GKN9lqKFCLu3bQY6LSHz9X5spsta3sPOZJUs1M1mikOz09eAUsFJj0hbOVs9+fXmonlbbDdQ4wx20VyfGe9UgUJ5JVwRxVl7BN8yv8Mq/vYuI8Qr3EN4QhblfYNlGefYJ9Ka4yQiv1/2LFNIPwPZF1OyHEURnh/a0T9vJN577Z1ApwKz+QpNsItJdrv8y44yO78j5pfc6TZlPVuqU1oRMRhTrwzojkkBhvGMv80AOXgTuuT0mpDYFtKFhKqviNRNQ69tPL2G/PCaC0oLLT1tYljqtV8fqfdnuyGwls1OG3iw8sFEIEvkUYEnOPNURng7OP4TVQZoEwkTCuww2u+u9bxDX+a0s1MJ940gHEqMvhLdlIAtUgdK5WUBY11lZ2+i/5UXYM73rqsS38Xp/2gbneq9pbGiXPwOJ/RFUqKAKbyAhXpkA3V0FaTMUTwIRr/iok0s8HvaoDVr5uOb67seLr81C9bvgtmPwnutyIlStsI2VjRj/m4g7oj5Xoo1YOaJoATvGmJMuPr7xA7/x05DBLeDg0jt8QsvrPiYqSC2+eIHTaR/utRzg0KgGMbCEE+z79FRMgR+J+6QCXeRmzcdKTKCUd5xilHLFUJZcPNthYOA7fsE6VVc18tyiaUfrCIab/i9oNFLshwxixcfUFR88I/PFh1/pfPmiuluCDp0MSlIvLtFGT0t6O4F247slZZazfdJk+vVwCxcAjcLhs6RDs4/ll38J6XMPNCF6btngRdspNgMUwryyuTzqPelrdBy1aZMCAurK+4HkTe/VmH1STXJ5lPillcDm8fisfEh4MYXblA6afL/n+ZGBbpDz+QPd7Uqi+lJLrc/EH2E6iIVPhUAywhaNdvWNL1+Pxd7Y+gygLgq/S7JIf5jhXK+Ai9cpKRTsbli7X2CzQ+YdDwxb/a6ZcrMD2K3Nqj0ASk1zqb3836kT+jNRQ27AgQI/b3QHoeR+DeNPETlBlcxk9Z7e/bocNDB5uJbkviq6/SaOM8I1IixuITyyuf+P2FUcIko071GAnFFPyKNVHsrYR+bjO7z+kJl/ujTD7isFr7+WQmovcfFpAKN1PxrGuAc0bx91UKodhLKV3O1w9mYOD28dT/iHFAOVxSQMIACE/C/HTeMaCvZtb9ERITRHXlP5JobKY3jsWGLdgW5gJElFiX/yjpk+L1gLzGwBy4b01nWkyWvceg9xqv0qXZkjeXFpLWM4AgVq0Dm/Gpoy1T0dMIAFAfMUIkXrQMF0IkIDyVqSZB4NgCa3G6kC2a42WJqHL5VPHonItvnE8uwUe4QTFnylMcdJUSsirtl7SVa44uxH7A+IOYlJrMrL58SKJlciepaN0u6GMBFNdEgDLkruYUowowjjkQX/A9Q5cKoNH27W+sPeAMGZvNhqMdn4i/r/QSTPz+dF35ZfnIaWltAzMcVAue5le/uPK20sNs4vN+lCGL8D/6U2Yz2c75N6XoyhHyCdF0XuEFbcZdjg0Z3SZJ5NZjFXlZgwnxZI68uh8toADnAX4muqgMCAil9CUCbit/dby2Qkdf9GFZPZ4rbZnLCayvwEk9gkixhTWLo+DXFryeb74t1uVnPRoEKKgUbFf/Mfx2ZUcsIKsOXJ32m/yHCrElp9uQwy5HIpnD0RE3HZi99Z/9atv8ysrVvfpaO95W0GcgiJQOBTdF6uWuRJvLpWgTV0BBY2UZbiy0Bc+q9+3bfD9X5dZlxDS981Es+xjcRsYOZmzi521MvQtAYJV7jWLSbI7+xoqm0io/yogpDZO1DKSv8HZkRw6mXBqo0IJGnZUPEV97LAtOi5QfmvGVdzG0kKLv/5MXvur1nQYND+KrdPWIrottKhTqw8f5JC2eC0NQCrs7tOLbA2KpFTXx9ZXkZG1fHiJCf+LV4AICOdN8jW0j2Chs/OapIIbUx8ga0qkBjelQImUjonVIpvzqhLwvZKI/NDCq3XBDES5dWowU/L6pR+8fTEV6+osb+9+T4TN8isDYBVzXSUmA1Fory904rkq6AOWzXcY0XXEtyEkQPa+JIJ/B8l5sZKVqaZNcOV1S/6uyD2iy2/ftksawIrDUBxQy5J94TwDAOWwIxS9+Ju779U+WPN1bCr320GAj8RFtWnEGBqQtL1L2c8aAtGhh5+5n/VNblg6NiwSB2DnzINLO+DqdsSEPDqA7exvfRaM6DEtt+l9weRWPt8Cb0H6f9M179bZogehVPSu2xcET9XBFqnJ8mk4y9fAGRBfLDMdUGjhhSkpTTETL1kKpay3JPMybUqtctqVnGPAZtTzG1kax8fOKbHbcUGe3S9KIikGeFd/wYj1u6CVynH6YXuxQAoVDvCJInCQ/HWWhb4CQ0FJ3dKfLrmH2quGpSL6eb9TdR1kVMPIG3TUbs9JPnfzhZCy+3PFrLbsmKKzXqJa5G8zRPGoK9tE1ZXiCJ6qa6cB1ku050FLdeNoeCN5fc5oqwRXkjXGIZlZzhVC7MF4H3x8UgJCxIb4F/UxBwDJPIi8B+cvNXhGfZy7lNVYqlzKRGj7DFDNiOVyn5iMS3jZX7jbiMqdO2k395JPGFHTxB6kHx7JrXU8ofXf19bnhy9NRrT2Eeg6SPU9TM9uocixMkg8eeBhItMZyT82HE6hdasNfwdBm79Ds5Mdxat05JTcvUfMYfbmcV3ANF5mI19oIdsu472SKTfQvEBYqZd74l14Mvp/cBAeqVC7VK+0771gkSMjoVd33FGkE0JL8QKDX0lwSkhR5YNfaXPm3MkRir6t9mxd2x7d2/3vj6AwstY7T2++eqps295Wvyy6lsFhP+vIdwPtBJbbakWBDacTwnvUm2GteYng8hKNr0XwOEbkhI5mQH88mteQrzNj9Xfsdb5Cx1RkKGZiqUqy8bka0ICePwnPwna3ig4iEd+tWisbflTBcBsQ8c8/6P24QrVx8jF6+h0GwmxY0qXdS46k3+WV0zVevNeMzzwoarUYBg703h7idYReQKsFQDhw22S7RXJc/gF1q9eLuSlyzTc6/6poCkuJDEm42/P/LZkZMFkJ/e1mOTmH67LOpdbwCfuDsF6NoJC55qjP06Vo3IrGsQnyui1m39rTMoAsri1mozjp7rQnyOK8sqbtXOQYqsLfsUMI0NeqDqRGbXZ2A77nHSiV7ojXe2P9/+i3qNyD2gsJl4+J05HY3Gq1c8H0dtz4K6YEKKRMz3DkQNsaE+dC73Ui4KdVCmJHvpRib5YUKdBBYtQsPLM5XHGv3YQh3NCFk9HAOkbQKwhF4517ze9aZjb4p0DznLgvilbcsP4lHZb71C7EtRx/LTsGQutk6w6t+fyjX2OGc3Nvxk/valVeJwHKkNAINcYLqTRc03SKjk235vH5/U9dKPM+khjDHuC1EjEUW/2UxNetQwBi8EaFHbRQUGvwFhKFGv5KNRrTmf0VE8Vu0M/JLxVRGaMQbsvArCU4DioNb7XYRl36YU1TXHschoIQ32VAenOejkJ5meZmUMugFdUp7xesXd8FE/tQ8htzrpeyJsBbSfjaOVYNzKuztejHtyDUJaPx4QJm6dYanX/9DoohYciewQiLpZiGk0kHbShKw0bPeVTk8k4AT/1gMfR23aOTg/7JHvRZlN2Ue9OfqqJxE7eTcJ1cbh+poLoTLqbsuhtadKDabGP3EaE3YDw8vQcgu9aETDpKtCwZuyBODis2t7Upb6fL9Oi8Alxpbnh0OvjntRF0qeCUHOUIz9G3KZQcIQqSzwDVCoGkTm7/BmE7sjz56pACo29qecbdxuyut9Jzj2dusHVREwp/nCT9C2uRZfrlWz9BEZHJ1leXlpm1egNfMZKV3xThQXqcTZlBX4lgXbuNVfQXs3f/2N5ara1Z7EnCtH8xA65Bn8GV5NOzGbUVog1vwuYqqx3eyT9CRQalQtmrgIgrF6/aXsW1aiVFJUXkv1FZvbsxnDEp5lUHu7fHlXW6BzI6z+ok9FCPHocrTrX36HtiHCqDTuAKKSY8WEi/rVPQrlJ7wxn4d5teXZYpbLodiFND5fRC8Z/xQKx9p30YWyNTnopO6bULLZJj9P6NxrRnMwBLrY6QBg7cCvBRHsTmf+QweQSSnMkt1WQ9M1e1xbW1NIVVRPdrcfVz4pzypgI/FNJ3xlPXbQ2N+3eedTeW2F1MQ5RKTC1ycdfveiTmoSge/p0W2UhLn8iU3mIiPEcurE8n905B2QpzwNo0CJ25ruX35L1AP0fkFqPMmqmU8kZIYyHyeivGB+c5xk1zU5racYcJg0JjTBnJG1WMMNVD9mFICbiSjiNFD0KOc0EZ4Jh4c9PLuU3QuaX/jWVDEWOgQ7s2/6Sd2GDTVnVsbPpnJ7b0wPDH9V7LHYJKAHFNGs3orFvBNSJyCWhIaZ+WIXCTI2qaH6C0MMIV7o8BUYsVTKviy+jMA2Kw0F99edu1OM582Pq6NJrwlvP95jCTAN3z0CFa02Izk6Duo5zz+4G+mRDCu3JltwS+yWADJ36fAXPUTvJno3znsTVXREFO8ihnuvraHZAAcyDWRQ6RHdQfX2fEu686m1lPAi3sJxBqvFMIJ2TZRaUEayDZp/U9DBqVedJXE5UPnBZpeQbQG8NV+vYqbNHPpAAMdFSaO/Ta28ORkAvG5BwjZcxKo/rN5NKRiUDNFslex6srcBPEd3hy0PVp74p31FNAu/n7BcCp85JBMcm/8idve0pzA3hrr2xfje6lSJzeqkubfGPT2Gw3AygmB94X815zH5wWRLgObxFViMKHp108aXiQ/X9IOE2nn7+rghkeGjSCGuD3fWrwtJ6B5DHmrVlDjWh/qGMB4jXeX6aC1STCgcgwYiThFfJSkN6NSQUgPaSshtmO1/7vc8QtbJdtTVdRQtb+358IcHqBde+btMaT2D1Nph2OKHM4gwlDESRgbHZmPVQ8EIIB9QcjyJGNva199xm1uQEqMrWIOursNl03e90hMCtj+ONm9lLZ+gMt1F8RQhHjuR5i6JZj+E7ROhQu7yBnECLsiIEik7GzCvgLyB28Vs5IXzMH5Qj7ZnSd3rQXHpzXm8xsMi8VtCSvedRAV4Ug48FxXYOK0IFfwIdbWkaq6csKPKXOXz97rAez1G0SINfuViClxlKQeWWtHCnxvN1rYZ8OChz+s88qv7ZGp2JWKT4tAsKvhwr6fD6TMLbJlPCjMLD+qvKWRXKEW9s/2pTOse5aGvBDrfUdJehY06BwbBaY6JC7ytEVjqDVARwmn+Ym8PRjSTYIHK0e3SMp7QDWwcy36ACwbAzDCw8GLgXkgP90yaY3YJJWbGAHWp5b+eUMwdLIVXvo5PlRKCS9dyYdPqv1bsJTHKsuxW5kVKoAi1dmKtoRnyjoJZpsKWsX7nkn7fbRTaR3+tl1UAtJyq9Se+3+bZ2CP70NH+7svtWDUNwzSbgP74ekiRlEyGov+g4u4ZSdH9nvgQ+qJgXAvBrfaDchqm8akZBGOj457UFB9wtOu75MqPwDRaYu+Mp3y/8wOQvnQX/ySrZb/vn+wa/8xHXRH758rl+MOp97/bFZ5dMZ3EXrDdMHtPhbt+9pYPAkMRz+GJrmBq71EcY1tQ4D7j8YRceYS2QDoCeI43Wzf4vIg/WtUF/2z8H6X9rl1tdKV5u11QdOgO4tdjvk9wLx+T59+NbAiaGQO2z5oDNmuCKJ8VoLG4/Eu1LZWXVsQlD3ngNnUtsf6WQEs68g94G0KQjRqjb1chjGhR5vrHYmQ5xomvwmudQlk9mPIKeM3/NgP45IdHtzD56G6E9KUsHfmvcbYwOdfrQF+OYRqnQ4hahIUHzJB+zPoyaIEXnS5Pk0OZlAqcAyVEDatzhPtbUrrbAmtKBOK+w0GUDh3leKbCUnUphreP4LICvEF/EoPuIxX1srLVUXZGZeaGUrc3dWMlUWyZKx6MGHDnd0xGZAiz3iQsqn2gSzQzMkluMsMlq+zKXb4PjLsdLNsUR/dfPUHrJlVHq/od8s/9EJdj8NTgHXZnoqw9XDcvS8qiQ4et7N/CHUI5d5f8vI2HSnl0ewPMORNZAzWiiaBviAGGFz0NiQu4R6nfDkvBDraK4S2fCHE3RCx5OpvpPvnmGZfEBlbIj6KzYsF+/Vdzt0tYYVZkeLB2/dBII1KanLCHo59aoqk4I9VwTvpv2AlAv7wRcXfQ5xrLIlzPGu37uw4X2fa89j7rFHxJPFgotNYhI41wlob4SaYvIp6HBj4IWm91flnCyFZfVpKxQK+vfurMhLnctz+zhIwQx0j1PV6dlhrGNq+SDQE/7CElCO8GVpOv3AUkyY2xn6cYFf4QcjCzidCGuHmVNMaeK0ZRaPvIUIi3tikoy8ThbuHX87cRElxsD+Et5Lu2bg7PNiXCWvi5XZYLJrgwziMsPETUFLrZbLHEI6N8X1AFFuX7hS8Y4esc+HugxZclJ217qoVF4vPeROl84UeN5uevVVQlVVi1WXezxsAWQF3WETAXcoTkEveLQxO0uft2ifU2xl/sAzpaDP2rJCh6AqN+3Y1uVqPsUf1mJ5udaflTtdXJofVx5j3cktzhBy0q4YxzWMCpde44PL9acL5W09ff+s5+LOMAFeViHM9Q1mU6E+zPusKDv8J0uN1CJ80vQ5p5YMFVLN2fxYqhoFtgoPEtX98l4rqC5J33fHlfB2baH5N3z6yBaJNb4JGxIR8xsxKk3ZMUTOox9sR3l06a8qSTk3ma0Binqub2Z+CqnWEG3BvHhvyRkMlsF/CzVreOY44zsCXOhMFV2oOlaTChHQa3OLIHnthj9i+m94oAsLl+P5HPKJrkSm/ep3Hj5ndQgTCejR5rrwKjk6zgkIHt6CXTQ8xArJAqHVV4twRbWudh7pwfSOjzxVhxY3/GSph9aBln1+EM2M4kBAhhJORxt8UFpjHGhMGCE43Num/1eR2IryTcIlGrCZjwD5xoUIqP130TEi0/4/KyBqWh0EXzpTpNodqv7yuKqXMOWElqwkA6XdbLjVL78W8q9NrqoM6pUSDqy0NbdZ5ol5RuVtt/wkLq48miux1SmSytrRN93/yDeHPi5MW38YOHbbARPrgnuD+l0zBfWpwXIw5dEWdRoXZSRcpqnMnLBCG/asMZnqomEFQJwoDa1WKlZ1quh7DP+clEboI0htUJTLpxdi/1ZY79Y8CLgXA36l/HxF+EOLEDdtWCRzGm0iTXaFW6KBxZFGucOz2jAUqT+1nHFyAz8OXnQzKRDx5Fwskn/SrPy3QTssZtjJdTt55A3s8LnS7G6cw1wbXZnOf4Ef5NqXd1iPA24OecLpuC47lj7/WVWCuj7tEWEAPyIy0O5tLd7ktiertsm9gOABRIlVpuW4WWY+awVY23ZX84mCdSwvIxnSl62Rh8Kl85Ox0wRyvqYssPrwiyUjkZuNerPtgWb0ju74Hf/fIT8Q7tvYKOzn6GUcUvz7Jdd/9LCNH908zrhHKsngJ7hKALJtJ8HvO7gSxTHefynr9NxQf58ttavs5TQWyyCTnT1ZGOFwJsEczl//641aaoGOsYHB4NYka5HSeXYrAhEmnn+xF1KFCwQR0qFtM3F6kBUqbXMPOWqP+zj2k7rzcb+5IcncNY9lByoVUfmGENF3OQhtSNtI1qW/bi4ebQbOM+Z8FrK5QgBkaYMspDzxxrVqIy5azjzb1I4/ZLzKLnmA8S97ZyGO2BBxr+82JGC4D6AQ3uMWqiGUMu+saNsybyV2t/mc86Tw4Y31CO5dXYnL0kP5zQKzax1/9psSM9UTnsSZnm9PuIsZZhqkowSeoKxf1SpWafmeBEdWhk3+ojvPQKrxy1nor1c71cFIx9QREaZFF7b2MwUhACVtbwj6IZMqrKQsYstvJxp6mDN1DuOKxZlCfX5ZVKAHlTVt1EjrSZD+kP3AKH6m0zAS3lWvDmKFMmv7ozXBvtBu7c0D3TmJV5ZyqJ28PGulQFLkr1mls4Df1L4Dc5ciGkLjme98GOwZag22+8uKiLebg2KrSnXO7/zUJfn8ffga23qWhcZmsgqB5oHc8pbFI0hNUhcz4JS+Xz24vUkOGQsPyey3rf29njZFpbnZsRfg9Pzl04wYIO/s+T/sULPSLBp4gk4cK+IP6ZFQqFnHM13o3dmDCd3KiUI8w85Sl/MHOBad+9r1SP3E34rpA/KzFXg4SFZMVpnOuhMpdM2V39lZKMC1FGmWpLKT424DPpWLnN+diymyogtGPuhLs4hGUO27KwHywoq0eTnQLm2BNuEG1puTDjkMO/J+Vwvym/It5CUMIH/gt3ff79Xpl7NjvYV5RW4vhiy+y0HtIEzDFrlefCxqDo8TJII4G1/npuD1R98cyA49+VRzKY/4hyV/Qr+x98NhnSInVMsg2GwOqFfvZOU6aVPTj/6cBizm8TuiLJfgEQMfuiOzVjopO93YqB3UlwBQCbr5AcNJCWUr7A7TLxx1Bbaa0fxk4ndOTeDokuWzwyVNuTo2ycQiSQqtlPMku7xq2S+lHAQi14L5N0eiPPt0yflekPvXzn5ZMBuTLCs9Q6GapYNF1RlRGMzlajx/lUTF6v0DW5mIH8ASpykxWX3R3CO3vnL7kBP3EBbXvDZH6uLzeICG6zqFCXyUUOYGOqykB92DFVlUm987Z9cFv1JLnTH9FhPVeJ4KJm+JakGMSzng0UHklKmoPp+alMvVYVnKs+r5yIMgj816jyRjhJnJVDaD6ZwiGUyBPOh3OWX2EJWLCBcYz2mCH35QGT8Fd8vHNdwchrSpBAEqHXHzS63kAu76rdzgA0iy7QiTyH9kjplOq3Kdz5gH2B+gOCEp9mKHxbCzgIUMHHVclRn8uaZDWKH6ZU3uGjCYqyBvY2gBGpT1dkkRKs2Q3sxfCGdJiCKO03m5TKziEaBRjSfFHnZPxDL5r+gggqPvft58dz6K3IT9k7ztSUcn8J73EHxGSheLmZXHVyqCbZPP04u0XyvteJIWWSqionqVMl/vsiQ6Fm2monNt6d1k60Jf+aIR6iw5PU3/wXxU9IstxujCgzKUIZSJtnHdNFqEuWKHmZYwYUUlhxYnq3TwceL59g34vxJKsGfDYE5JVXJQhfWXeMHo6g+argd2O5I2/OzgY0EbWLX/y7R9wrThTUSWkNS5ynhx+dKgvMnuakh0Lvq4IcXelqrjhrR5/nRRVJwOZMLjzswXU0lbV7+B8ulivvDLaapNWXFHGYaicok0zDZaws3uyM8Nxmth7SEOze36U+wK/fzgH00VlJRSsWzvdhfiTtoLG9GGNgTlBDBI3Wt9CPYlDakO3f9Wh6pVhjWf4mdqvUzcgBKE/3JszW/QALF5TcVdPO41JqcvDqYTVtMPz2D3Os4XzCTV0Rik4LHs+1N2Yrjv9cES9hQN5iU+sBnFHV+RlmTxx3hnavfrqxuk5hoDNk9zJAaGzuSixUj28JLLmg+e0SKVbfDE6O6DQm1zEk/WpDmjXGW68hTWgzPN/hQoSHAiYUZXKe2znUdbmtFdpvBneiAtz6cDFoPAz6oBo7T86OlboHmKEXc8EEJar3SWEPdzkMDWKx0h6x2nz/9FLd6njzKyN/dBuIcWPWSaI/+gQ3wz2MSsz8KvdkL91s0fcRWdioS4ytqdT6TYzqeMISv1l2mlPbtKOD2gM/0fzMPR8POhnkjCWBFRjgWwK/uoMEeIAAEku/PPPTFmFeuwdRq5M+/b/kufpmYi8v+gID+/r+2jYCAAGNq2gTjmgt3yNGNhlSbiMdF0jEDKO71UH66CS7HPAMpTwzIQQsM6/9IENygmPRBE3k/CF4nCDhpvqUwu1zgNhYTAUEymORQ/o6DS9WoJWEeV+DCS4lEk6cVyBE51JXYgVz85ix04iDM1l0/U8iLMde7Eo7W2/wACUkNUKgcreumyRDJ89SQS8bPxUtVf0Hv582ynSJRC42IbbcRgNEF/rG9Iu0nceGzNQlTfMj84lhH7xf7L2vnyHQ457SIYAjdyQjSdU1aBn1zPZMJcJNx4vzTQAg8E2Pa5runeUC0psREn6q+/iJQgnpd7eNlFPSNa8ElhkKmvzuq62R71eTvoB5iYn3IATUvZUTEjFzdUZIAzXnq4x236ZnhoiDql2K8roea7XPM/3PvCaLcpPkl/1ErT7XqIZ0FnBLPgsu6Zt4RZJ4s5d6MXcKaGLTzt1vcbiXDV274l897FXXNRuY2sTGXoTqs54dB52ROqvwHZMJcM/szzcGChbU7s89i0B9AzFWHdTgT2OnF3l1EbrQYtVefs4OGYPQCsqlgydNLxWNcvKLvM+5IJv+GxGrk8rxUXlxVkW7ixUruDvI6u7Pzsw1QqlWaehBXdAwETDMhA/oj7Otd4Ack0u8dPAb7pJtOb1yUvF5717qGNLPKzV7mjbR+nTFlHeXf/vTA+hbq8GzqmLbYoIa1xOz0s8V2481UpFnCU4zfLF3mSIjxwScALIaQIvtrno1Idvhsi7yRWfUSFnJODRi6W1Qgr89NHaBq5Ghmjnd7iaks2uChmXsho0SeLG2pO92p8hcwA3CtkwS/DB2G30HVgmplse7iTcUiGj0mPgqsB4wFAkh1TmusEljME+vzmTW7dWYRPL1pO0HK5jZZY3GrWmoe6BSkK2G4WGJJ4gQt8qJ/T8sJKMaWbRyAgAzI32ry4MFc96euFu4F7tzyVNnKyhp8sJJmZc+zHlKxa7HxxT+vJ3XOg2HWB5bnwWi4bMDyCK+C+kNUSSE86vbKnPZgXZ9tjNyv5HKCov/J5lyE4uA+goazXj0pjmNTxbKwVTIR1koKSkiyRl7iD0G0WoYoihHZ8Foz+QuDyon1ddoDng7/xQm6jBV/7koscRQnNA5dOF1X3T7AW3FjX9jFcVEnubyI4SzWrNvWgRt5h9PJ3KF/boXTYS6UH4M9q1jOiCv1DTWFDcCIk/L3rsM0PeJ/Rh+zwO0xvvfSSk2qv4ODFyi1XFYK97xdZLMNHzYifVUGXpogSibGm4WoPLf0ucS+AoYxMIDAa+hU53jUDkdWVvHWW7hhxZ/q+cI7ZJZCj/Y+XZIpou8CwWtSP3VEC7O6l3fyuUEC5vbEJz9rX4ccCrOOXflg/+lFLFGwi5GOCSrYofR0iXItV51Dv18hgDkf/8gLtgvyUVyR4yZBaV8iQjAAMjMJ07P30JF6YBWfqndty3h9cjRJ+ReQDCQGKlX1aynjfGSq42FBFkgHQ5YeiEaaQC/RjW4M9gzJeVUI795FdEknjMcd+/M6ExU3wgTTDREGg9+m+OFmMYKNuvmT+DDxQQzBzAegyJMKDYB1kKSAwI825aR6bq+hYTFKsp+j7jgJUgfd/FfEe6CDxjusmQKnAkk5AStV+N+IC+jnqNP9k5CsDHu0yToBBUGgDOU7VbJvVIXtawlzwqvVt2xCW2uZMqAy7J3iPlR8+rYIt+gpCndFmias+j58p9MwDGwKu9ylw2SRCKIzi8kfB1nbHzwkIS79SX/igXp/z5f8Sw/NYD0UxkIB0xtCdZXYX5yg3eG52sxkTWNfvsCyVfobWukHdRlgkmIwvqzZDoXwGesU8UJBcwOPFDSBXFy+Vd4+kYcsNqIxOLS6QtWnQJhsEzoZxHZW0a4mmsRxZ/RS2O6NNVmbhyRGJy5/66DotmgyswIJMpCap4aCSCTqUDI/phmmoDebDmPU6T7hvz4ox+bZvQXoBxqaw1JJ2GVJU9CzubRhtJe/02ixlQ5dTOh4201SEHYCaZyk4rzenUB+Nyyb+/GyV5Mb6kd2+94sd8kEoL+mUHz1tYv6wvMXQFzXZn+0C+t1dwIFE5ghRTXQyDn26Nk/iyYxicYxSZyLDHkYU5n44yDYnNsmPzkB5ufY2tf3l8BZepYL0CssKONcVJs36rGsWqPFhOrrJB7RS+GPesOr5Hvy1cSHBX1z7p2YlW2xL14dDWaK1hOnqkQ34HSwz4BmhkJKlpcx1T76hKUOP3UvfRwknknCmB8zUMtO4A1hPn14tQqV0taoJvQdYN6/r4dU8BcJCiDYWXV/8WSzVmzGQ5WIr0rKRfJ55NVMuZS6oJKdUohhtv2h5jomH4hmFYhg1ULWUBFmd66piKqwks62sL9RRuYWx29wf9p+SWj+hRmf5WWzoKy17G/lfjDMjasyEZaZfXQV8EFXzx7wDpDxet0XrPvB6iUWyI6xcwzA/Knk1GzNzhYlQ8kpibUAsDpv0pM98ZpRifP4f+uzVbCjbIhakxJ1VEwXwlC8vj1Q7iTGTE+ZCwnB02tgdwQrQBp+2/eEh0zxOus54m5/4Y9YvqDBXndQbxKgimES3jdp6ZO3uW8OSlCHM073qaWRy5s/jjDaUcJvQ6oMd8PsYBwxqqDimSzBXNYAY34XbC4ohJWNWajP4vYBBG8Y6r/bJdgMiYRmDgVmRBi4ARHnmrYOMEtxI9ih/cKUOJ4HFgpNTtq7AxJQEKnlJDEsEsi9SAnW5TywZS2zDBp5Y5gSFqxS7SlXzVQwL7JiXcB2u7KEOZwRZDag4eilChy5qW8v4OUDTcSmN8lwpyy8Xxc05wirWnFePcAGxDybp9HXAA1PG/NwvuRlUftbLcHFzaAtnaWWICK5s4rgoUSEQN2ST6fIN5L2GSTglgav1WcGDAKB3Vu9EJXOfaCm8+yi4qK7Lfxe6MLiXPEtx05vx+vW6nZ//WUpw1ahsPfHAs988Ciir5bcVWgIWDez7DWmlcp1iVW0s2+NQtdHI6ZX+VtZ/sC127mSOYJKd+qG1OXcbzWHLL8SfdF77Awt0fcNhhFN4KeRdAlIHgrNGqoN4Lg5uGERFR9BR5PJuKTZMzpJ477ginIajzro+1+gsJvZZCQSwB/P684TALHpOJ2kY5sJIUc8VlWEfvGDOb+gzFT/sNkfZVO8aJ39PPSNV7Ed4mF7qYSzLjf3fOQDRgHNIQtN81EHkcY0+2dKqEhZn3jtkhl63nyPfGKxfzyT5D4rnu5RFxpKDhHLFogXYlIVMJX1axlOvfcUAZ2HuC4zEYNC4DfiX4+d3w/w+aGpkne417FGQ9nZ3el7JiuXgVwIL3PbfsLDO1PHRB2zy2UU1Xcp2dKnOwB5ILvEHa7JsAXC+ZjI8dE9yrw3li9d2Rn2d7jxphsXK25R5OoJJxGsCuNc5VcaZLHiFenm0G8RkJYM5w/z9eJcpGNcpIuq5BdLJvcZ075gLa2QDKIQEuaZiKeTPsVtnBCUb7XcViEYDTQkGydnZpF2kQliDlXauaOnbtky3ZClMnLjzW/Jo6FC8KpTN4FaDX8/8ufUQNKNTSb22L2vQx6f/m47O9r0/1VDj2R1Z9fyVY34gzydISZgxiM+YIUHB0KK2VKzQxzTV5TjB8fnNKQPfta2jSDcYrQqLcFiUKQ5VSYaI7Hpx5JNu0PFZfWjdWOtkqQUfq7vSiQyiRTE9+2vcww5OuLxxpeca23fg8TCRwRBjQUecpINHjj0dtbVEYJZaoXIbU0A7ialxskmJkjw3CGj0huBuD4nQ5qM7s1R2li7+zbIqwNsUogDqYBkUyLQgZNEdcqEIJFFn5GDBgfWiB3YLpTf09wtl3IUhxSoxsouN4djmbvRZFTTGTewq0HUYqRm95unGBmuMWnEgP1CfXWtnWx6+RRvs5DGxMetKDR40gKYNJ6Dw2/zfZ2kfPDXXKPwvb+EN7R3yQrP/EjMz/g4NhfPEs104u9dpx0kJ1qbsztkZMpakWnXaZI2XwSLx+vF7xLqW96Zi1N9JsTitbM9speP97AG0wYXKJKcNdrBjWQLH52T2tGl23nS/tYe7V41hBGt1ogqA3LA/zMRbDb+yMqhM7hMSdF/B7lYQQJDZthYiZrljr+b2jQxDf9b0dJeXYEc4+FUGS/eGacCCtqrC0d8vn6LR6/ji29UH5zqYD+QjppwYCz8tIbGxENEdhi0+hHJviIt5XfI0U/szrU9tMfcuHgfsppCetLTzVNkl/rHH9jXLZP9m8+LBeSLBlA5kFfXvWq8H6fqUzQEo8BvrFqARPKpFMeJWzZ503JcUyZe/TZI9UYKRgWfTyxIt2o7zpZw4mRiG90RI/yQuqyvGjTpdtH4vVtI2zEXLSfy2VBkJEkydiSIiXwN7P2qGUq1uHIsPHZAsTKUyp+550IWokWZmXq/CT6MP8y3CNxCDRtzOA1RML99qMUfV9Ul/aPZtFB8cg2W02FK/rDd6jZxGI8n43nJ0nUcqCzeY6OW4FO11k4xsX8QfsGPHm9f32gBHIjSH5m2Tvjky/E2iWsJEdn9Zj1aLRQvudRIzEYJHjlZEiTtN3mb9a5VVitTp0V7hORtvASY/6am5x5NleZXcncjM6MTnBbrORdGzKsPObnPQkXq9N9KTNvlpxtVEJZJseZCdHrZ97Z/nPYpnuIEr6KqB1HCuCr6B+gbgyJrOlLqGWtK6isMLkSV+o/frqYkozhDOFZC+oaeXNOcknCV8ZuDC/cnmthIVvSoGLI/CU4nZdIlrahL/eyzkZOgkKoM2aOvQX1CcRTGz9ewmZYPPFbfRImeHfPtbGXFf15NA6HXlHdWTWV0KcxW9fOd7WL8Y9ejeKK0BsaIJNXJ1ufqpPYu0eeIFrAf1uUxaIyk4++9NbbWnSbihBWJ4Pz0SpcUa1LWSZl10aQiekV24aVzm+1zOiFONelXsaHgLy2ZsmVbKNVEWyqYW0Zwtl5Wu7iBddI1g8j9tYIBSXvevOQQJN2I/aTou4RSExO0gCpUx5TZ16XceNecFQRFOiGnR942+WnBOGB6fp0p5WaBsnbQf+AQbR9G4YCglYGQGCg/0X1WeaXhneZbPHfexbOBSGW5uBygTrs0tyMIoaCtmFPrABLYoXj0drmXQgGJrBAmFT+DokQtD97HnWv+zkbRlAacw6mbLs9+EDQyTzM/LcvWlZ/qnAcdabe4NB4Hu7OCQtYCcihJhtoDdKlJDiumK5b3+bVuu+02qccXPmhDuis6KPZ7YbVxne+BXv8PtX1Y6Gqg7oAUOTSFKqrPUPdm9WlC+mMtnYJWnadFVZYhQx8ng7YbZdmy4FhnEODBw80j9RawkkdDCB5CbgPPT0xd/2Q7yIG0Kn4h8/7dJkyMVExE/HMErMc//nrwUJCXUx16ZgSP8UVAhGasS2F+G4ReMij+v/KNVnNkjX5xh/dws8jxUQpTX+8Sur6VE4CVTfp3h5YeUlNP6zWGhMqe4EHfI69f36l5mTu37wqBW9tgcDs5ThundBTEQIUjJV+0y55X3jGRZuXjf5ulhcKVnUPduc7xWNEXfXm88soLXHtf7Dl+0xwNIZcEp6h0iBx5FJG3ZfNzYbQNNKfTOttj5h6C2zfRDY7xwtS/f4gd7tuOnEuV+2Txx36cSseoPixlND+Hwy9BERBx3vLkjyIOYhCM35Hw69fdyL85dTFZ9j1Ow1TSh9F8LUDcS7uAwtF7ksDlD+SEs5k7qa8ZovZXzIlzuSWjJxdHIuw/N3gXFVzzWCBLFHEH7xgC4nI0OZ7cwkXBEufInc1GKYHY7FvQtVwQLpZjbFux0UKjFQ0jSngZJtxI5C+xL6rJS8hLF0QUYzxJnr32dyv5oTq2U9yczA/8+IlIs3WINlm2LvX4IRwUFS5mS6sKv1Z7lDbQ+/7S959JV819CQOKhXIg6w5Gm5/xLwEONuHnqJhZaFwA0sHrYZJzw2JxPdL0v+iC8Fe8kI/0W01MUsI7h91mz+BAKIck4gw/1JckbMWRmFFHYQbpthQm/UCDw+l7bwCly+7IrlVSZWwsFPhc11mKGpn69EYYQaLc2WOOxHprgw7RUUZ/mAMnUCdkYsTPrwVVsR+DHJY6tUhBOgybdj6NwkOhBgevmQZW9NGBTbk0yytUqlhCRA2ubzkluwkksAhhbR7vcfLR+z6r42zdm4gG26bvMUMwKO7uWWa86X17Auon41wR6mimp0MA2qiqJkxnbOSvL/txg2m6au+qwcjcr2nsu05uLXMYBizsnPeEFrZ2est114A6a76Zpz4K6qGhUBqNIfDWl2EkJ4l+8s4+d9vreSExTjA1s54YFVHrCUFrIINm5NXMFiEssLwrGUxQjsXtZCZV/Bm5CHdjzGkjX9GFWUcjxtxo0NbNDlNv6Qpd4gOAM36VfEE/6RQwlFGRL+X/6uAfXkSb1xBlKU9bmTcWoTRZksfDKWSyh7V9E7DjYPaqUjwSvb3uIwrKiRoVQ4iVNMxTN8o82KCo1VzmONxgl6uiw3kI4KBgpthvizN3q2b+2bCLvOS8Ynyosc05CeMNcTP9YQ+jYxFnJcy554klkN7TbTskIRKi+3mV9tu/OY+b8homk96Vp10u/DWYVr3iyNxlGHWddEQscDS5G8I1FbAkoP61F/gw9cY80fNFUAvxzaMipUrCHr+ng+AqWlKvgNttB+NAr9nWQTpPQUoKnyQ2+3uTsuDJs704sJKVB68mwqkgxz5igN3bSs7em0ZCJx1yrsGggNUYMKJsiZIGY6OKjDnLLxGLbEF0xRYxKpS35bpMIwFOS89jGfNo2rKOSttoAtl/vRlAbZwhDwPe+pzf22IF5JOurILBwdQ1G319ESv/WM1E3wVcM7U6L/HaU3KwsuQoNmfXUQ9JgSEpqyjey/LOvgN6kIvyvCwM6mLLjAnXvv4z8joYyJI+HZDdQPxo4utMuQVdpr9tPZPnpup0u3LGQGig1mPZVqgILWO19kDw3IowuX/gPrL5aXkkWyYgOZO5FtQ+2h7WHEeTzUhlbKsjFiqcGL7wvlDHPEbbd03wtBgQTgJW4q2iaalr5VwPDs4DhAi+B0RPhatGAVM5fUH32KsuVFrmvLAec7cjR1vhdsohVpMqGdgaSHhFBBbKOUAOT0sfREDGRhhqZb5GEqxo2Cm4Giz/BaQqpKUSikB7pn5x1eivzABl06nb7ys/wezxJJK7YHNdp7LkX6pPuqrLWEtbTEVBEXbh07sJGhk0/by/Bas8gd/A7TlAVttisIr8UyD0Hfa4dIOy7y1bgLcFtFmXag0a4O4JNdY5ko/QxjXUp+m5GffQbGKRp6cZYM42LAQQWTlSvFTNVJersFnG8W7GukuV7S2kvhKiOsgIuJcnHc2Gp8mBL0kAWGlarvTUQr2cZVnTzvFz7//MlTc87kZ8Q0G2rM9DBQL/L50s7G1z4Zd8wv25ajOWgCbCvqotwx2I2HdV6cIyjpXXs6mhNW3tCkHppdVog+1MppvcWgG2xS/zy53ieXOyWifNglywEvHFThfzgs9waMSIvLFUdRarbe1W1nZ7csVI57G60/PTcEhZr4ZMWOsRDo1siYxKaKPdKShsQNYZomPaD09W7JN5FHeyFtBBZ7r7+igFaT2hC9AqmW0V9u+BUriUMnzaxPsblirq1i3m9hb/T7879NrG5aTHSdEgpa2S5Jk/rpRNJgvIaKdBcxEIgvkII+NOEVvOPp0wgKn05Axpo06LxQ7fxXNG5L2MOWCjFx2SwU/pzhPQ0GjKQ2ix+ppIDQX20PJyM/B36hRkf7hBoH7iTcjJG6UzOSjp3YJMz7NDOnMvzUfWrOk+JSp797Sj2B/9fv4pZFw3s8CnPznowPAqNI/u+IyBIKLvpFoL38xyliVdAQAC+K4dppTCcjP7C9EAsQ4UixOjOlWk+JcvA80Pxb1Pk2DsrbtBgSrk8uYC1W+IGGM1XRnCjKRtkHrimMMc5DUQMgg30ucm+pOCyyZ4ZmbBnQ+sD3Qb13rRXUmsTugtTpOqlqcfiZDSAnyKOifjGfob5fWpy1E3frKeNa/hZbYGd+t+kqFAnJBlPNLGLgVwM2arf8+Vf9VMJW41yx1oWSR66dDv1I+B8Rjy0IIL1TQOY+z6cqpuYPQ+O2Z89q52PInIvgQPiuGsczSl0zE7OaeWHXQUWJSsiebDZpXOZ6wM6xzaUF10jvf1oPsB0bPfJWaf4nTHb5mmMY3m3z45WU32yCuSkg8tHFsUAodPb4UsmrSgBVaN6EIk5N/dHRlh3XXgdaS0jU/pLirvHIbnV09jb+FNILkc1dA/bWiU0eF3SkMAVKgTL3tgF3dQchwRJc5EZRLWGHvo0v4V18uquhg5hy9Vo+OVrnSQ+YkgoTnRiqC5JOPCnFFHDATWp1micDrNgAaVI0FgTsJzjSsotaz3R/8/I68tNTBQ7Sz6vk4r9dTOGiAYGEecps3wyf/dwvDI9JyCQBCovCEeU1ZyboGppdQvdoagTTCN1gse0nCuAnHLWCkYtM5wUCBW2sjgTbqsswe0nyRsCLzzP+wLi/xLlowc/vg+Yszgb95CK+yfzN4s4ihyn6lLlascs42Yq1aA2diiYijjI7vcED6ZOC5U5Lg0UJHS3ptfMZ7twVT7aq6RkvGwvXHvPJPvp5421scAI4QAqJh10cvkr0cYm7wpgNtKNM7XX9jBARSiDFaguYCOKrpi712SOamcuV7msFe5gEnMYZcDx8xIqEf7Dldl3Tim/+LScatB/A+DMampwyHp6LxttaF1TexzQTz3L4m2jzO5EYBMaTw5OMugVTtyLMjr5f9SrmtlYXu2CZ5XUeaWq66G9z6FSed1aKRQwYaj7G+L58WPNBdjKeEERXhdPhNyysUzi0QQYFQYaJfB3tZABNHW99Lo12fuCjOJpywLS3dNXVGg5UbdAbA2p7snHyxBI6qKqYMRAaj1AqSdXEOW9/KBAsXV+vIn8JoMXvdtQg/9OuwrYzlBbp6A9qO9dURLeLTnlxSbjYaniMNhTCwhZkw9sUu5BHDIKqvV3XFuafRAypInCNaHz9H4Rv5k/rrpWNMtozmD+lnjMTYz2aoBh9xIxTSo7cF9LSIN2ymHATOaSRj3ciCTID4pPAGwE6/37mBbtBSGuCKQGFTYAjsCdfHzIzuedqcVhhVR5e5xf2OuA5v5T8qcbAzF3Nc7UyrR+F1DIcDPko1mQUxwtZCKnDkslQyo6LJ7vAzn+v9R22TV6H+lN6UNFgTqE4eqnQXtDjpgJz3d1nyhLdwyvmNYrS8Qfx+HZJG4NuhLMT9dvUxusL8Nykcm3oEpIc+p5/bUai+5mrSTc+PlLCWuMgcYkN54rHJLB9CI5vDQIXyTpMCXZ1cuPSgZvIGokRkFL4+a/1Fx8DtjQKgFs5W7iS1zW8NiYloIZ5Br8J8x/GdjM5NmMjuoz0xPVqxAxNOB4jn14DHbRWSpktK4YTkZe6DJVihqHCPDO1WZ5Z9Zti6tm6urg27bFqCbHKThgZscMfQBETlUwEWcxe5aEP6akeo8v/rs2y4/V+GCdrWAlT3Yukqie13d3GJm+ZSNjiAoieOzxIYbgzydGlEvbtNX6VEhK754SPX1C5crzaDunMdZfKmi37EZMvvvIjhbaV0DVTMvBMnKKXG4wxvUQZcSyhob7o1cPi96OZmka/XLdnCX226TlnorsU/5ECw0wrCpIuilCeuny02QucD4N6FeMOYaqueSiU1I+4nSztjwxFxlk/n7ifvtvlwFIJikOWqXJPZEq/HITOS8wj+Z9U0UJ/LlWVndI5bJjJ4gF+WYDalqf4TQVZ0HpflRMP5oJ1XooLTLUqYdbdlU0Er5IjyuEgsf3BEsdeHXXYjoqlHC3HBLTzA46s8randtjT0GPBtTTZ/NNA13oFQoql/bi8+Fb7QcOZtiXmTJnSOwrz8eQsuzxxGzEq4M4vjLP8X2XoDNEMRTdR2Yu51taF2YN5QPk+QseMH1b3VSX/qm7bp0s3voTdZ1lrbR7KCcfe4BOy1XkQK0O373VAI04fVlvDiCbUq8z0khAbrU+2PmAyqddSuBVEf7Gn5f4T1otTrOKbKI/G8si6/r4R/ZZi/5EdBMF8WQfhR3Qb9YVTYm0xBFywouVlQGfW7/f06FRx4oErosWQIt0Bns5UaMA/fV884oHKdEGn6x+HjguPfmdnplhH07FklhxsznZX8aV9sQ9gJL/kz7AFu4qggM40nsvNvWvLLAPYkDVmw0OO5YBBsiE7MM2FVVXwyjcMA70vuLjL7+QGhgKQayD+KLeiDF6bmfJDc6Y9lldlBt11tkmr0m4F5secfdV63dm2f1/1FJNPEYKAdjvbuA+5AGI9d7M2BR1CXuWtBy75SMCojg4f+B464y+QAtFll3qLj6gLVpPg+4AnK/gifLdzwdkad1prtYV72vVOiW2Lgdgzoq3ao6WlnS2f0UgEaD7aKIxCoDSD60wC0Rp64yh82URQIiaVm2l3+ohUOUvpkzfjzyZGu6oP20/47itrP0G73xwqGwD6ndPnZ5urcCcS2SIUxLuh3w0NVTf52/cI+ItSPbR7YlNcMkqZkNMG90PqoP5TegZ1vOBF2pLPA8dRCGLWc3eGaqrBYeWSbJPHrcsMSI68QM0fDkG6whH+NeyyZZO/yWBtF69AGvqaAFLr72SFeC1z6NqxISVBDtte2uZ7ShRSGn9G+9rCd5Fsg6P/HhHTnsJTHFxuwfSw6VdccKCCfc/Tv7eorHnY7RndidE6OnXxHjNCo12CkgbbuBe28c+g7Ua9Gr04Bfr217WPyaZCo1ruHNu9zt+An8f118ogN5LJXe1CoMJ+oTqBycXdk3CeEtM5rZOKdI0MHtMDsFmfM9k1+7nFG1DOLd6SZHYUWXZsPzm+hTtPds2AzlmA6iUmw1Bj4HnRgXURtX5VFOYQKdEs2+pQ4OOTMJKq9boqBImRjSJf3Jn9Qz9M4gv3+uSQi9i7szkQch3XwTCegEjYUX2pIUfI9MM8reIU2Hf2XiKPZRvXei2fhsHiWbtdSRvCSAk2ZSHz60s/RKyYWsL/kJ64ve6RlhCZ50jT2BrnVKUarRMiHSmt8lYyCGGTK6XdURReOrOTD7ihk6Qh5tFgSr4A6chgFClKBer3xe8y0pmQGjT/e0NfhYaXb4X3uOVj8S81AAXdJv05WeSrF1sCe08ULhecNHSJOkfIEbZfaqmGMe67GyGIsfPsawgThdKTHIm3pUPOFVaxpdj/MBL7iqh7dpM/CIHJBS+bY+pLczntWasGLSVbKZxxgpt+QZYRRatSaHavIwxzfsuI9nqIfXsnfaBSNVg6xumO9C0Z4eZxX8HBYMkq8WxTTRZPjO51kkjH1oG/fEhjYOcGF82MhOib4BuA2dNT/0/iTUFrB/mIUAralbwemRcwUjercQFisvAz9o8sLDXlyI7/w5YxTKs+Za2kI8ISH7Jp0Lzh5RQ3gWXaZquLiPHkY+4GVs9eYOpYlkKUlmyfRkhpBaH+vWAbT6lA6m5CFZINfiMYdve9rM4vyZxTm+ihFzdt4bcwBcA/tj1Vwqw6Mk1s9m3sS54z5vNlD1Fmj5tIrpaC/idqn2RAwMwgEqjsDuW2yVdidg7UvpGwtVDAp0ODzI24IPit4ISqUPxfH4fUyQR4ecOhQ0aiTIfeSVKDCdu54bc21fc2lphMQNVXoSSfhdaUQJNYgdSpQLsHlDSCWa8BaFrYpQxcXO0t9vpYsmCb8sacDV2mhZN8szlsjyX93sNX2wrICjbZzmq9KZyM7t8QpNGrwGBkoG1NI8S7UIqPLlSlIf1ZeCRnhDJTjDC6unwWItEeFxJ31D9AOQ6BT8k757biE7l06UlDCqEmkzwZLlj3HQt38L/nT9/mL682YFWFIMsJ3LXtSho5Nu2XgtTSl0bVXpE1aU95Htuvk6VJkYWsSam0dCjXrPvQT2YO69GFfLqhshswP8PSIMXTBxdqbpT8E6gqyjTK/89QYXo7aOO3tcKuhKfxaRb+CBYsYKLfXxl2/3h99fB6afrDH/Cwx0jBZn3cIauWPcn59kZf/oDKYd4KCfAERDcaMnk19sGkcvA6aP5134hcvCKhIXwrI5WsP3LxbRC58OCI7c1XEgNO5ukYgKfLwQ6S87RojYmI/x9uCxMPgxd+Xq9EM+zcp7uLbEBNnEEdT7mRX2waQkTHFtFoWDLuj0kebx8IGGIn/2FUTvY53af9szh+bLIX0D01iVmPVgrNUoQGaAvzbrqW5NLUlyKWc2/uWugTuYh0W4YlrwfQOtACL5RpIN10qzusU2Q4KjHgfcBdFFIqnLZFERac1Z5rYNw2Zh9k4KixFwOXYTio2uw1xUpaH/y84tWJDwUdkshq/NC5+mcmgClfyGd8ELOf9xjI4CcBMjjPqTD9WFjlrv3bRPL8LXH6l2WkP6YbgH2SZbDmvzIuyhoUM8q48Rs6IwLSWxgi+a31oAh9fF+NQMpapzIcXZeMG9fP14IeTznJ8WkLglDNaWVeCuxrGOMCQtS8dCeu6SE8OhhXvr3dKMDjGIv4TvoFza5lC464E5Uh50L92J1LKaKif29xIM2/+BTBbH6Mom7IN1swFv8R4wLTnLVY/AGJXq0bnPGoauJkCp+CHUpStHTLTln9Ps28MfMJ1Y+8h4+xG10p+6d+66sHzcTknkHz2gdGPsQoWptu0ePzc9Of4oBc9otXvkotjq9IWvZftBmhLdZ/G7rGplAMi6wSGsAMj1dcEE1qu8YQvLgX1UMWMP4GUO4R0TzYAkdx1mRU9r/mQtmiMlAFK/UrSHHvT0DDzmyHb31rjQ0i9PsaiY9kaCnQeafPKZHpYNbaHxy+KR0/k2CZM6mVytxIBmECIL0K2m/FXYEbYsZVmHMq42z+6HPCu1h+b3wgmieSCiBkpJrVfeQQdTc9gg9VyK5GHcdupM2dnYUCEoArABzOXVLFctNVXWZY8zo9TOAOeTIXuECxMSqDWf9RuoBzBj1S43jhF4xNZF7PQ5mkwEtZUI/BTSuCo9j8NqTDgy23wH9g9jjgYLd137XgKcVrZqThsOgJgo1hhkBWjiL4HfQ28+C2mqTIy8zlLKQ8KHPOyIfRTA05p1/WaLGrFnZ+tmiTsWiSwlV9tc7lSt7SGqk2XE3HOUAb31s69Fkc/Tp3EC4EYRqbZM8rFzAzBeKmgOFHZNQzUqGXH19FQWZC76K81mus/kjgZ783/j5H/dit+CJzVjP+9SayZ1KFHeQiMwfiFw6BQYr7peAcSiSMepgsvYHMa/3YhHhBXXZabUbJSiTaYMO/J/pV3UbOhIl/4RBCnq4ZMggCutNUz3Iz448MvB6PdApIwLWdLOt/yO3KBBZch59LSQplp6Efhyhe3ARR6uLOTsgRTD5Xuf5pI24W29uJF+9kKa0jNs3xouhDc1zWP36qcjFh4PN2RWGA1RqhfWuqES9dRpCma9OFPJ0ShcJX8OSj69C/c6V0qm1yIoEs2281PgvYP5QT6TdsuJAhdsPqY42Ps1EbDcQTPa6rHi22d/fbWomeWIF4acL7qoOS4n2Q8EF+RjbrcfT2iJfjSs/4hKX6/pivU6bf2iZEJPCm9A38Jdgmcr0mDLiaLGi1e1JSKarnO7RO8JMQCS04k+KyymDD07IBlF7OT8FbLfgqpGhhWHPLGhu68VoBMa3trj48KUzELpEYjiYiNl0hOYFZwq9mRpCSXtNHuOYFpfYtiItaQ/BhVqcXbvDU9/6NnKjLdHGQ2DkNrqa68cJVseUuAoTVwbKbPV/FsIpHshwLpre2SMB7igeW3GL7ptYja37g7ACDLwjvxRpRZSQTZxJkzymDXqw9fEhqCgUZG0xLYdz2BsupIU19M8qp04U58A779phcMfJ4TsIrwv5pl5AlSr80yh6ujdlpxhisoIQ26hbLcyyFBBAXDcNP6QuGB/xAIkoNqmQeYsu9Qu5TdKbW9VgFH6gC2P/BBtKRFu7DEGA0w1d7KgkuWtjj5Feam+ZSRgwJ6TNjILvmnPLQArhBwHJYQx5Y24rcM/4u6jABLug29o1jysgLmINri7gm/E6DkvELy41I/CkFDT01HbU57ER93OxuHgofln4kQdhiDDoA1dk4rI7QLAIospVargg1/UZLp+qoE2ebY8LuTybZqMOLESw9/Q00729vVbmGenummNp4ypKepW3+2vahdbvzeSrN4PwXx8cyFUrE2VJVQ4o6XeJlYDMyZq18OGvVrXp2ZoYunycNVchzMEHFySSRQmD7RKJFC4ZqGe7Rj3+W4sIsAdpi6TXwviL3TWpjODBVNqa7sRzZea3RZKBEYe+RCPFDPYU4mXBgoPY73I2S4/WKuKFpIdkRyxOVLE2Yp+JLuB51uPaX35cUwkGLRQPvSeiLluYuq0bU2T5utfFUATJNL/sFRrRVZZCk84qclF6xQUTB9is5tfu0h2obtKIYIglLrRA9R1QoL7rOY8Z5H6GWYt1Ec/6kMisxGTVIt8zfOixk5dv3LR/YXipVYysgfLjyfBrj3cgMppZb1A+lW3mXMyjbQrzUmAwBOYxy55+UX7513rhGuqKES5B/7b8iUusbrnx6g4doQPs2APpIfjIKMJjSo9w/ToEEhwogkwhcZaygmTF5v8b5Vq+vgNZM/2qOoDnjmpnyQ4NjmOWUJQ6BBq849Hc6Rm12zpxJrxqeuFOtgQhO0py1IMEW8pWAfkRtCUPQVCIeAdXCVVc6h1qV7jcG98J9QrzTqrySLDDT8DYQKBUULLjwk0urxW6xOHOo3mLbF7DbCQZtBIu9gRyNvzQEL256c/LuImQJv6qdv8u01itqZX7fhpGEfxH4k9VZm2puzGEkUoIFn1j+AgIfoL+5826B9X7J2gTqshdTiyKESPQwAKIn3zMbAy6J3D6bPJIt9k3oFVLStCtXTbsQILQWPwKYAizqdUQWNLmXa/S/dWIVtsXQkZHBEkJscb8ZFdItxDb8HNEEpV9Ef3yifmRV+j0fRlAA3KlT4Fz0Xhjw4LnNwTmVGpUeAc+X+OGgIDr+nCzEh8mUt9qsgX7GCVF12ckDMwxIEKkvvcQu2CVkInQF9/JfTZ5vu30e5x2m+qbOFhhyfD5ZEiSpQPhKfmS0+gYLQaRCjRhU0cYKBb3Gks8jl4TrCAZ7A73CPla6Tycpnr0fxRCHJndT51hqhCZhXtrsJXAswyXN+87rK2sVcKUFd0STgJ3mKqdHmYlBN5iIdsxtcpGTuJTk5kDwzfv7VGXuF5orixpUpMXMu/VD8lBFrGggoutSyzdMlg6HzqePkC1WvgvQoGtUMS22YN4WHdfJ/QEBTtMLrQ4fNBIBqO25rO2KQTHlCQkx++qg5NQ+RtIOcdO0EklN/vMD0qM/HMK1lQzgJIiJsW01moMzxJDCDVO1qMJgJMt/KtFes5yG32qx81JsFqOr69+uOtZw26dYJLhveB0bAUPhcjNOSnRG/ef/zsXbNK/iIQ/4dyEeO1Il0ZVr3r13XurDHqvQKETDIqdu9aRVzJU/Yv6upKBziEofJNfJ9v9wDit4mJcQKfFOyrMrX5UYErUu5JB7PxEVcoy7YywIPbbtmuTeaJM7X4xM8XA9G1/sjciQ6A/hbPJYxJrHE0A07rUFKo3UsIkgSr9uSI2SGcsnbK/mWW5cDQC1IE2b5DenjonPo6GaqOfZK6Cdgs4A4+uEWhwGnp+lJjCoHwCt4OeNfI8gGhkLi1R/2LO8lK8TsGU6giTMCbidkt1Z0keLjn6xudLwGdzeaauE8VIdTrRg90kRy05ql0FyEUrZvgy/Bu2yOV19SgVgiuIXzLF7VnSUddDBxGJxYc6MmmQ16AHmwGkOyDSk4y/lE2+AYPk7U/a8iPuXD/b5q8sJtC28Xi7yMvvF9q5MwP5gTSkoVTCXMufjxKRX36RpnOH3k+ZNL3yX9Hw4an03By/zEI+2aHO133TZ3A5ayYwC5AgrAAF9QrPTDlCRvfV8BTUHET9xQ2SfQGvTPQBkRnmg2UNGq1xLi9J5cinTwUt0xu3tpqAW6mploG6/97aMYlBpktNH/sEndf3Kfs93lfngrwmKkleX6a4RocQJBHDN2GS9kRy3xfsTdOp+5CPl7GES+hPv2MP+tHzYjVq8b9mNuccuLnMCpPyfYrc9KyPf0/G1+M/8WTragJy/ZypecdP+xNtQNVL+9qJxJ8NE7Fw/n83Rq6hNNm0NKdKvslB8UMdPv0pslRelSlaCw25EEfuG0NSf3oGel0dDXIAlgT15NAaSy3uWQZCL0iXWLuJhuDL5wE2sWcZ6XQ+HczK3SxFrCGRXuBQVmS73kAalYiaVYLwcB7FlP9IKLLK9ivT042n9ggdkvvnMEDUz/yNLcHJwawirYUQ/j+l25gRVUIQSj6yd8DGNJ5lUT7V2wZ/YaSj4Iaw6cLKMpn9OKQ6OsQpbOyZU2rTBya4v1DSKwGFmhUULC+rYPwayLolUO/AbEWYCmNPrlandjU4HNHRP92/w1kOlZILvg0ETThl7poc36xc6ajpXpRuuj5UAHPW8VdyPGKBqcP/ZjSQyjHPgiunRROZZ/y09kC3i144rC1ZUubvFhgExLS+7Z4EIrwn5jABrE7DjxSNBROM+LdzqAgnjC+A6zFhmUbkXNtrzJjcI6SlM7PAKNqFwtX5tGVxsqWDrjcE2XTQ2Ir9VgxOIJerGx1IkgMyOQUfOLFfd2Z8LR7nEssTabogDQXJP+2Zcb+xl9cp9vJ31Rf0kxac64MpqdueFQLoP9xV1bvKeu2TeSG7WW9hXyspuR0GIQ27rEbimM93unRGGe/+rnPaEFr/4z7QnetMLj2xSF5IwGFsgCJKgOOWFT4qam9VO8lw9MU23UbtIzui0bRn3dD2wxfc66O0AiEY8KsjKWwt7G50khvGtIwaIOPzo2D+iTonGGAsEuRg5iPayMlLt/tSR9EbEMXCad1Q2cBc7kEobinMcDYCCHg5Ld8QUaDCpks/rupiJIjrzytEQmRxM3J61X1dIpGtqSplgte+mzTRmw7JC81py7pI5SCKEG1YAuwWtbi+3Cx7u02cg31R3/mWZFJO6K0cX9I9GL1O/hlJnabMnWwTPUiTJ2NOx2y4ojfpQloAuTduIacX2IeocyRpEx1GZmXAvkwrMCGwhCVpbkemDZamO5AOz1I1zay7PcS7z9hFuCsTwYd3fv4dPn5uAPrqOYFd1kISDs8p6NS41MosSiY4w/T2IkudocIsTjH4kiIiUgF47nYeQvnBGI16rG3A0w4pqQupjfpAYneSIiHuiUgbgYu5wpgpsUiMMbDBS3R6Dkb93hxmj0DVTBtNr4Hy9xDCwLdrtBY3waiflr+AbiIkdc0DsTlP8Pv86roYs2IJfE0Q6YKx6GI1i5tDg+8O2lGQANlzE67avg2wyqRlEXT5Ho7cnp2fHKRWMFKSJiaBBzihBP+MN+UtobMjaTa7uBYKE+G5FJBUrxSx0N2VUAXTyu86iOfValmcSCRbBy3QkqmizeXq1YmDT+dFkTFz8AND9xkvfZZLdAAAToC+OglMQ9bQdGAemsJ8KoTVU5Ryp/Dr13AN8mRchZJZTu3UZVJk1LS8rgf02h8PxxqZqYJfM1Az0Bc2WpSrk/2upCa5b0tjBiO1iK+uYuasl6e3IBzbyaMb6tivYtocir4PT6I9EY8UsdKNbjKpd+YA7/zpNNlzBLdvjAj6iGCmMsqHR77pevVYvZaJLzZ+QcSDjmSC79u7Ql283BRRplIyEw027/gqqVz7QByZBhqkVs2MVyIIxM0Mg3qVVtWvCuAxR+etaSOq0zH2luhWhChDIuEdaYE25DvTsH3to9/YvleawkGPy0HzvFDKMmGTDm42gY8UExwQ4h9Bbgmd/Pibl3J2jisLONIr/8kV3PtzwoIEcq2QQ7xiAWwE4gwNLCTtIBzQbecLHkeGX22deA7bkrVRRgBaDTdb8UqTFlGOOY720HKTr15ZOtHkiUXNsgkQPiYN58s0r/9HUOmNMOWxZOWPqJkYTjs5oYNhbaZ/3usmCbHq1Im0UbTG+Is7Mb/KRGAY70zR0xhvO36IHxhF3UlV67pprmzSBUj8ggCc4u/f12L9nhSg2EaRWImJIVj75FLu30ThI5+UHlGLUjPy87rKNUKwA08Em6CTKlInqSs/l6bUJSmaZ/u2bzdQqUwdrF9FhQwaZAA35scWmrnk3Tkv/sapqkNxiUA35P7MgGF8gHkwzX4LKVyLcjngf2VPa0i10X5u7tjrmR6BfwqOHvKOkRF4evlQ5dk2vjJlvCSEXtqqAZhsV/3XXiXrnMw6wHQ9j7egjv/q6E5v16mSaby3J9UfwOTD8BYm1pEurCbcWftEz5mQkvaARVumCvxW7NVIrSSD4K6M16Mx0BVf0VOBrAt0W4rgkBxhBHiVMR1DDResC7RGW4SkH5y00q6i+cbIglkWwIqoct+m8tGkEg4No/mrFJ/yu5kENHFHDpUi3R5H3OSOFRYToZaJE3PFXvPb7hTGZ9qoTbhvc+8FZkJmx3LprCmRQlk2uc6PfucUN3olNly5UPRL3PqaPEaR/0XYSg34JDpD4Z/vo2Se/0ytvbhtIejGVw+Uv6xFy/EXq06v3qJZPeac1PDl6voS4SABnPjbJxyBWHc648wcyzQxwUuLsCxJsjcO8sQKj3K98liwH0EiNC592cZVuO4Iv/u842qBOBqTagktstaKh+GsK+Tjkfzklw1XNI0lh9fyvrqDlamgTf27+6vALOgXHqaWD23MjIv2ugz617WwtevFaXgDiviBBOEobojrglwuXeuXTpnNat5dNfllx+mv6vrlaPiXm0T7M4Gb7i2dkvLpNKEZJD4/SyzlDJAnzntwhZyyD8ut4pGgnG4iV3bwN6IHGYGco3G7CZXRsMZbvKw9BVA2bVhoV9T6xtLYdj/N28+mivY1K7lj1KXsY+ybOcerVSntjt0Q/c0Wh28Id76KNp9BnP5JAyCXPU5n5A5HtXC7KYIN8O0vnVrYIqJNI/nMW6C/H80IOlFtOazwEdgxbyyYdWT9vozX2q/95vz0QSVzcprxQ5xPfm7s5xg2hAPSTvVcZ0jfnIH6kTMTrQdAnq0xgJzc/J9KbqgMIOaAt+CSVIrt+QhU+C56RSEhL0ZsFra/+CL6hdiSqGPZDb7c06VQqQeNvAQe+c2YA04n34YXnYZ7MAo2Sss1xGolcB1cv0QyebKHBX9EUB3Td9rMNJVE3KbpoyBu7tiR70F9QwDHvV5/TZGUKH8T4EEKz8ir8qTkLvjvIl9cbiCf7Jo+0x9S8HkVncgAWHZ7Ip3LESjlbUHPDGffhIVq8YlF6VyWbCPv2lWDZlwJlmvBt7DHUhj3OxzvJkCP6LMBXkagwyK7/xvSqoWvXZ1XBxncbDU5Oa7iimymLPJvF80SJlS9QPdxO7Y7MZjj+M2xGoZGm/Jxg9dA6yfcEkIL9ufsBcvibRodqVJyHOT1rk5t0saer6WMpeQ+57Fk9YALvOJMNd5mWa2E3nqLWbGUggwFicZ2JMamKqQ8QC4VlVoDMQZuavtN/w6/sqnIwYmxedkiEOAO/F3wMka0zfhhLkA3nxDUHgoTWwAfpQv7QHPGh0XTjxc2QQtSb0HS53YmOkr5QDAspktkUp0dyiNn6EIRE0782CIbvDg96NCI9WefRElHllu92rZXqND9U0SNQ3EmVxsNkhGofhRx13FZ802ylVVcTsG2wfCAWUVAyjW74gSWr0E4r6enwEYDLMayDbxI1mqoxBaYgzhm3FC+/vTj3vMIrLhGqNvPwzwd3bPFhpjtxQ2DHYtmL1dTCh+zTOBL4tsyLh3KRFtZ7rrRuu+99N+ddMlxI0GkwZ6PtnJuRqTJCeEK3V+fj1LIM7abTGM2txgiYJgobIiqzt5aO5CYMRkDXwHdH1w6RNXkffinpVSn+iateDh5NbDmDxcL3gU2dZTBvnsNMFGhHvR4yx3CIL98BnIOGM20DXreV4lnzP6ZnGU+/dD7T/pr5+U5iivBW27Fv6xlcr0NQo0++vheM/ZGaUuM3WkhmzBGRx0oE+qTbGDAGkPjIxINCfDNorj1N06GYVnpdsZG9C94jYLj4nnhIJLq5cShWF7VFR2ZLMQpKSnUT0X1uiueKlRX4wsmYYEXpb4aQjdCULMh+Z+2KdrKre3tpwWbakRtYrGZFHFE+ObYcv5We3xFE9pDSVqnYCLFpAji70QBAOl15sMV4DgJUUkMpsJOAiLldvqEYqEm+DbQA8eJAuj2GmH8LH9dfsiXqahwTb06HtdnctB4nwx+2yLIbB6UAG6ZXmWz/i9/qwqfas41oWRPZFMm2lv9uG6/BY61qFhHtA5TLF1wEY3iKilzG6QjyR3PuRc+4s4fEbHPj9zmYYvNckSlWsshsQ5UU0rjn0V/OeoJw7KL6k/+UfBtO7EmlBW56t+1x85iFxL9fhspJavcwwCYM8AdPRyZ4lKccZi9UjV03AUY1B4wkGxo9S7fmWjgdV3RHpLorEZM9VDW9g/XYM573onCNKnrgml6l99pbGNS+fFAq2/UMZSBOs9EnExvVEFNqcq/HQROib395EDvJIRVEYC1z+qC8KYcNpttQxrDTThSqskx2DP3Orhbz9CaFLQgUux8cb6u5N/HGGWiYeh/HqBvQvQ8FPFhYgoprN8BxU1FgwLs7fJzLtK3cGDbH4gKLmHqa/uV6iCbD2WRQXO7pWkslnzOWuF+Dw+aFp34SB8hbAEIS30aeQLfLUykfY0Hw28rWpSTtNbs8B+HAHF+4dlTJBfcKdfZSZVTV5Lq/fsRY5DDk6P1/kJNdho3kAFsBNvHzlDOa09ShIseZCScXQUtEj490yCscwTkhf9jB3UFBjY0F3Wc6PnCXu4gs0Buhy9U9iwzLbSYjiiAX8NMzFmyCcpCJGL30l2Mf2kxAT08P9YHPGVaX2FDFend8mbphIz+a/5w28xjQVaO1+KEf7Zqy1dTTsPL42gZEo9AaFkDBQRrF1cLILuAThubAA5oeuP7o18MwC+8XHk0FXm0EqS5sULZkx+2egVtpi5eaucOxdoCSCwi+/tzgRr7lvQU5YDSBtLolQ4wQU2p2f50fJGsLdSZWUIGyhHyrifzjyad8oup/yWE6oGfmo27OSABshQexSWaD2IeAWHqt0EGIJB+uEJbLQLUrjhZlHo2goAmyJ/vJHBYgYJqzjRirdEnCPszcUYbvFq0LRRS7ypjoaNrGlz8JMyhQccTAuowlWU9aIg2Tf5okONlXPGRJjkgmP/XwQ37VnChJpnO1ob9dP011B/2syuYwiQnRW3ckWj9zdEsF02qKxyrW8U+gq/KwCqpw0P7RRoZ4j423q4VYZgOaCi1gI2Xs+YzcGdtqFTfwuPkKVV4fvumy3p08fUQuVaCE7v7BpAsXYJBeKpMTne7BcqTyGA0GEA2FarMRUwKJkDuVXJPeZa4tUMhMso31Uao3mKJXWn5CYmLGsgn0yCBWIYt6+QFPmnf0bfJai//VyWLKyeOE2w7ma+aFjieUgnbOyP2CHUEKLVQEfTvTW+mdLIIrHE+KYIAuEK5XqZpMMKt8PLVAqkLmdpeGiURwplJdXRwd/mBNLMc3W28sR8UNc3c72dVuuh+hIZ/TmdR97Y+9FAmkNkZ09SriH2Ex2pJ8gHpq95u+aPguZM2/PXflb4aldrc9ZQGCL2ljkTRn6Dl6Lto/TKIQNCYN9qH4XDCimC395+fflfLQCyFII6s4Y8qPnGvgjzEwURh9ExRT0CqjwahzoAI2BoBreuZ2EAkYN4vPbcRqnrJ773jDupD1LGqU8QOcgoaY95whKaJzCDS2SuC1hV8m7hS8FdSTNhAz1EwAPaiIttYf9t5LwLk8LBXn5A4uD0OCYdQkKFAF3vAkNKWX3uw/eR+p72pGKiHKiWwHnnMRZmI3azkACq1ZPFF0Hy9psxaM5vCGnFtGa6rLGrGGrZWjHxqF8+4Y6TP2uWzfAMJocUruu6NkCxTPxH3VkViehzlA4OqRMHUtVCryvdg8mngAC+kYEYkcC9PQIVu/yLfXJ1EmDK4MD7Zp1lRCUqWbjbOaCdSho3hdqNVMfqPoEOVi+7/zhFJYz73m/QKFxcoMKs9SbHTg6ZiOhWSDV1AigROq5XaJKdaDuNTXniETiiiPOXn0qgmR+0SiilQXwRS7wNqoZ2+MTidMWwoQA5vssNHo7N6pp39JwvTaRtE/yT+9K55A0Twmp7mmH0KQZ0Om63qE64aT+pzu7dMyI60ljOwza/GDs6fIEKj6qAtdf4zGAeNpT6l0drzpSe9gSFhyhlP/otW/4n37f6aT1oyqrJSZNjCtS6RZXttw+epF2jhDLeJ6p9dgfB2hwGecEy+FFD7540cqzU91zZnp1n7TuZio5gw8CpIw7NNOxoD7+30T4AcJgxZ5vQFDoj82oetBAwQXL0H4eL8qo4/pXFxh+EFTKaz9VkOpjIvbWgBdVdU+vjEpmFiSa/0TAzcChmu7Q/loVfyhF4Z99cSu2su3r7jZnKsrEg5Lk/5zajcfv0msmV835OMwvn5DyxVQ6809rXQxH1XND1YRF7519afV7ObLLAnY/4RUue8Uhf8EWuFZSuiziICOIcq2g1VRBRC3103xhxuGTWFYOmQMHOylfj4o6kmr270nbsVsLucoN7PhniuuMyuqIZaaJ3yYdLA7Nh1tAiBB4J66YVwkWf++ORgwyVN3NYWikOAE6cAVc5m9kBpD860vTtenqIWt+Pj+Dl4GJg1ddukBV1jp+QvtjGE0Rvchhw1yLoMx8tjXZwb93V/+IODuyDHgPeh8gHlwnrUsPyTIgsCGjQUbN9oISQJ5BTf3RQEaYtXZYXq+nc+x8+Ml6/WLHd6XKdATvyw20SVwxhW+6LW6efTj1Bu9MnFNqg+nC32JVIKkKqDLXJBkkmOUNKbDA8pd+Y+vG+q8qXw7DaUD7Ffar/dGrnAvjzKvG1dS9xZosHiOnKtsxI9AE9g4GRYscDaSMHdh97vZTu6a3kHRw7f+VlSr55Y6gf0dWI1ZZ+OPiRz1R+Yoc9XGNVziP86oZwfbGlOz7Tqz+oKXjQbrL35N6xbeemvDBqLDN2jmAaeinf6X1EXI0nGSA2yQSlo0YHGQMJ5YSHQZskCZA7xsMuRQPBnkyUJVJa0NR3zwQ/CygumPhkRdg1UhH8wwAGz0NNIEcXHyrWciBD03zOZ3np8PqH9zgGbryKmN1J6uoKBd6p0LZuYbtffdOQGj5E4S23KE593Wkj7jIlj0xF+0ogqlgtbxvAe77Zudbe/uJpFH3ft+xzBAiAE95wpU2nud/T3k54QI50/bgQUW+b0beiddbNTl/IT8/+3NSc1supXxpZZaqaAs+hudElyq21qkuA4/EVbHIKi7ZgIqcDJv5vtmADmfd3QkE6Bn7v6W3CkRfAloKOwIE1HKi7vpkgffoNWxUi65yCCtDM0YA1ineObZyMbkVeBfxPWyZMLMssBNOMESSlOAO2/UsSnvzn2dS8WS70wG3DTAoFJhruOEbEWSpe4xsZo2Rz5kJhtGdOglOmbPDk5r1GIhFzkzmcnS8gGkG2Ss0zR2SWP1JwlmmyPPUjyvkIC3qdiOk5SEjrUbvGKUibjNaReNZDjZivouc4asbbTfPLtpoU/VvEyjjgCfyyK62u4DkySrD1cWaohROsubM/zACa5MLQ3ADdMAbe+ZdqFYLTxJEgb3imDwF+pu1Z3TlzDdDyaJwWQtAVbAGjVaQBV46vWFPIf+FAjsGo94g0idZ3nojO05umQ0wufahSv4mcqSotavYjcAWLy43v/BBrW0+Xpac7StBCRid9d3P5DznYIkAqvWMdrzJofctGc8ijk8zYXjD+0jm2q190Hw07EIwgkq40nsMRpOjO+MNw2qWZZJN2gdB1ZM52XstHpycwclmKa9O48N8tY/sq26nYXUgmNx2qredBIrZLeFxj+DUnVpmYRsYnkBtZRfI73UyVTTEs0LV2sLBq9K9Vhvj1UI+yVJldzxu0ojmQ/UmEqLkmJG5tisIXEXTOsdFy59Ob/+vNQOye9a1alTdW5OPhMzGz704dPbrY9L5WoJniwcG5diWHssB1z6z3s2+u28aoOLzkDX4RWo58e+EyehMTLD5WLdVeQsGj9/B+avei6f6tXmKnhTKqZq+rC3T711jCfth9SxSiuhIu1Ip2I4ePP2uz/3lKFj7tdTFPRnalnSeXyo7ASAaZfMdVn58Q6G3kbvexpt578qUELgEl5P+myWqNn91qubdkHRwkXJHeH3+piCyb6P/naGMVz6WnUq8K/dfTOQGaL0Wa0b/lbc4KZlKz+Fwzx1/okgaWPHTlkgzquViLUNCycZVNtRFe/HHIMcrlIH5d9nU6vwHZFj/Vvcf4uT5r7nf5pjNqgRsB+9qJds6tb3NTK3gOkNJkovZTxEs6ceY0XKlhOrD/2KSQc0/7ou7zr9Rc66MeXt2j+TBHCZexG5tLVAWndWyfvmIHYpJyP6nHcd7MJj7JMleE1IN6/A2eql9Ti66NFfXE41Fm7T1q5iO5mjR2itOWK1UrrpDDM5OF+FO+JTsVAhV/Fp7txwUYmLUf/XkKtuhMRfGMiujy7DzQd74rET39RzT+R+Bhnjq8EPERyIdx3jFm010PYKBeveIRlkqy0jHQAiQQHLBhYdvaM4l06IJNpHUe5D522CByDnJuMR+9JvT+Ou1jjmdTlKafdtbROMx9NrNaXVSZ8mrfpU7dfYQkGBP9JbC9mKaEKnJ2oWLIvt91ECpLx1Wd7WWJO9tqU11H43bI2xUStyIIZP3RnAFBzQSrjRK3oM/1UL0tOKp6TVQCZqKAHHWczpIhEChpLxbEOvJETIJBUIKaa+sVEjq1ZNogFs3fiQHvhQdnuDJ/TMveBTJxhjsf4Li4KInFKFwrGjmugM6DJW/jw4tIC09YUF8IVP4rkWPL7YFknSuhwM655VfClks0yPqI8TsqRHS8k4pRbVWVahj7jjeTa17yIadfQiRPi9TikNCYEDgNEhXQWw4uJWi6Fh/qOOQcTRUJW2MWWYGdMxC8PBztwhH+Ka4S14rDnuP6/0SkfEln+LKpOLnjXwoO1+l4EEXuYH/BEjwtc00Qr/njCVfh1HEijmK0jFvGn2lh9+GbPONrKdYTfj8glpdK5+0px+DDKuvj3ZKMeaqT9QodSlKNqJWXMqHEYox2xeL9K8uC8YMKkQQ32BKoUDWUA4d0vG/+dHvwXQoXMrLPIL6PkXsO0Z08eFQ520+0WwWZj14SdWIbezNpd4gOZI3RnP8DLJ5XmNsV9qeAbqjtTkjDT3t5yl4Zr9TiyVGCMtKB2LIyaszdyIr8xvVYt5sc8uiunavV/XkKs9cfqHk48QBxHO6Rh/HTpSO+yHNS8NsQvWEe4MYLFw4vqEKpZJxkd///bR7YiD9wSjodpMIv6T7g2YH6muQhYyQ9xCLhKmzIvEJAdxE5BpuO3gZCBpnt5nPf+BBabFuraLsGdBEJMyczHqT0MJJv/VusivcmEWnahAq9a67JLBpqwy0D2gvHIuFWBAgOenJSKLlK1TIk5otYagnsmAz1SYiDd4WdFkFcep6PdBQYW4ystLMglMXqJBSMvV6DZvmIys4LV9pQ0Xsd3CIbc5kSurlUsqwiKCXVNdoaLbwxcU2tIcjgNgqCGIOpxyOrPrgx+TijwFM2cA8DmLhMuUxZ9qGG4uUuh+ueNNuLfsKrokqntCySMIWl0O/j8kZHuMgdXPM+C30fIWeEIQxg+GshyI0z/fnjClcPQ8hHHpIwWFP4Tw562K8mVReVuQUOMHWL/Iap6q3icqauM65kHnLAzqfU6IRyQK2Q+t+AgJ1kNI7dqnC9fQo6HZMFvLY560/6KiBsfI3p3eXSR06gBXlme17wq2l7oDsMFLzZEQ1lTa4uE7RlCjnyY7okHvoHUMsWfrTd4r50JJay6JbnGxZcqWdWFdb7dKHLWbK5+00s1XchYAYU/yZUEByR90gO2sccuH8dYaWR+cJJ14oDHhQM6/b3vX3zPd9wzn4rfjoNzr+Pomkq4EEtV+0mnyKQZhntauDNffvYn3oPlBLeVJttsfa8fbnCBonubIJLkWNxgR7WTKfH2fOj+bjYNLpuMGUWYGsQIGVWmJ4TLIfvkpHLrM8qDsME1dbA0qIgLCW86rmJoM5d9yF4CC9BUzPlYtWlvRj6/SXD8cx0y49aQb9fYdOJipSuVUFFJeBWbRyd9uJ1H+/VHkB8VoBpBWi5sVDzx5SQq30MYrC2NsKaBZLlmrapRXkYZVGGn2tNpg8cZNbZfXSNrEWjz9BXRdyViJJOGU6K9w2TQ56j83g6GWykoDIS+M5qBFI7Gh28WLTvc6giwIlS8Pvnakuh+DhoT8aPsjFbYKCPXD4qiB2I5RGWQrDaowieRq5K7Yk7lX2ddEY1bHqt3LitlMQIT7JWKJmIMVyiCQnqZrv3qYDJNII+aKK0PN5tb7/hzAlzR6nb6n1kKC+/jRUVgcuvG6VR/4ITobHyW9hJL+Eh65w0Ud+/KFNMAYoCU5kQQtGiBcrUmHOQL6frfws+YASibslCa6iCgsDNov1IYQ+rHUW2dgTQthsxkSNvcPYdj3awZ4VgPhLc9A+pIOBRhIezMKNArFQ9OILcoEzSQFMn7e3HF3iu8xCudKJD9daEhKa1rnTaLKb48RDgIe9mp78hzp+3W85Z68eCLm+2arjYC4HzlGUBTOUdJkusXPv2BmA9dQx4Q3djPQ0acM3FGAR2jaIWjnKs5odB03kaEVMl4bUEE7LP0roPmsjVrawbloqPl2d3bzRVcCZNi5pgUtfJR2IRvxkDphu4cPGOsbOWgwJ6bIriIV6PvH2LCfpDhqoLrWYvrg9wMNjfqpmCZnuf4AZqYJn/Duf3ZFcjpICaGNemIm+8VL1M5okK0SgkO3rC4XDy4Djf9OQO1qQk0c/7nITmtGSesG23j3pSEVBHhMPGGR2/K/P2utj7gTzfzGnNOK7pelXAzPTj+Zu0zYjlrgbpVea3ltT02Ag8hdgwiay4axeR04QYctf+LZ/4Oi93XVsYscMAOBNx6LeVpAmceEYBhSxcz8AG5/NzIwT+ueO3UIzlJF1B2xq9JES5+XM8wiUYv79N+fQw306BxWYLk+2hI5HnjbKpg2oXnGHZh4RXlLbNoCHAt1i14+nJoNBuznx1eDjioxwsz7ACLT6jfdh/+z3eFa28fjKKlof69sb0L4wusljfAgxwbrYr8jeuXGf1f1rB5QnNr3m//9/GZzPAAjicz9J7axGAgVQaaC7FGO1L0Q7SfFUjz2W13sOlpFkpgifX6lg02Etjdmo3kGaQGqWKuEoXRiCTd+mrtI6z9VNb6WPZ3mKFXK7y61265OninvQX4upTpfbOjPd7JkrhRmST1t1rRN0lMgzfwLU8TTVdvUaiffl9k9iB3/RP5u1toNh1yYaYWwBxURPj4YNUblI3CedeLjs29zO8IJ0ZNnSnC5ghAQ9t5Frpgfoafx3qg4/Ksgh3/nGAE="}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":0,"cache_read_input_tokens":4728,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":0,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"a940970d-2bdb-4cb4-9b26-84c43a7c1972"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01AwZWRfBQ4QEyxDHtVHmqvc","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"Let me start building a Tetris game in TypeScript. I'll create the necessary files for a browser-based Tetris game.\n\nLet me first check the current directory structure.","signature":"EuYCClsIDBgCKkCB5J377S3RWafKy8eA0z7Hoq4DNUBVBy2MrI3vCohCVSZzhqA51txnzOFFx7DOSEc27QknLLjo192V8SRgq+I2MhFjbGF1ZGUtc29ubmV0LTQtNjgAEgy+dpOyO5mFBasoYlQaDMn4ouXJUZGDLib6BCIwJbf2pikJtAvzpi+f75sRSTYGmS1rtzfabIlHcdLYgFHFEyRxmybhv6Rr1Y/EQ4DdKrgBZtye4Jh8M52KIhlbos56hUZTfgdBISn+tmcH7CRRIA3sm12dP0DtCzj53FKrZaSO2GxlaK495vall/E2F5aAQ4ZyvVHPlPbHo26SZYPNQ7jRjRY4iQPGm9HupohVEn2FBzlvQd+3IV7lYpsHMiJHRu4RrKNV9rl5HLkf3KIV6/WdSafjclBrV7HRiPwtTbKd7pW2h2oblBJIdvV48Ojya2WSn+yXBD7XyvDbCdvrqLHvAjJidDt7YxgB"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":528,"cache_read_input_tokens":4241,"cache_creation":{"ephemeral_5m_input_tokens":528,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"4fa2b1f5-5508-4884-a794-7df803e4f1db"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01AwZWRfBQ4QEyxDHtVHmqvc","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01MffCG6U1pANyUS4KRbi4KB","name":"Bash","input":{"command":"ls /tmp/loop-bench-q82ugmwv","description":"List files in working directory"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":528,"cache_read_input_tokens":4241,"cache_creation":{"ephemeral_5m_input_tokens":528,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"9c5a8e1d-1bdd-4021-860a-663373791220"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01MffCG6U1pANyUS4KRbi4KB","type":"tool_result","content":"node_modules\npackage.json\npackage-lock.json","is_error":false}]},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"6139a279-039f-4425-9eba-44f629e750ee","timestamp":"2026-04-04T21:12:31.026Z","tool_use_result":{"stdout":"node_modules\npackage.json\npackage-lock.json","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01Kubg1oFSDj3o9V8UX8jNDV","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01LbmPncfGFztNpe6kw6ph5P","name":"Read","input":{"file_path":"/tmp/loop-bench-q82ugmwv/package.json"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":167,"cache_read_input_tokens":4769,"cache_creation":{"ephemeral_5m_input_tokens":167,"ephemeral_1h_input_tokens":0},"output_tokens":65,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"14ffd3a4-0738-4966-81ed-f52061e2d7f7"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01LbmPncfGFztNpe6kw6ph5P","type":"tool_result","content":"1\t{\n2\t \"name\": \"loop-bench-q82ugmwv\",\n3\t \"version\": \"1.0.0\",\n4\t \"description\": \"\",\n5\t \"main\": \"index.js\",\n6\t \"scripts\": {\n7\t \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n8\t },\n9\t \"keywords\": [],\n10\t \"author\": \"\",\n11\t \"license\": \"ISC\",\n12\t \"type\": \"commonjs\",\n13\t \"devDependencies\": {\n14\t \"@eslint/js\": \"^10.0.1\",\n15\t \"@playwright/test\": \"^1.59.1\",\n16\t \"@types/node\": \"^25.5.2\",\n17\t \"eslint\": \"^10.2.0\",\n18\t \"typescript\": \"^6.0.2\"\n19\t }\n20\t}\n21\t"}]},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"28d5c513-134f-4ed8-bb9b-ffc700d12f64","timestamp":"2026-04-04T21:12:33.906Z","tool_use_result":{"type":"text","file":{"filePath":"/tmp/loop-bench-q82ugmwv/package.json","content":"{\n \"name\": \"loop-bench-q82ugmwv\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },\n \"keywords\": [],\n \"author\": \"\",\n \"license\": \"ISC\",\n \"type\": \"commonjs\",\n \"devDependencies\": {\n \"@eslint/js\": \"^10.0.1\",\n \"@playwright/test\": \"^1.59.1\",\n \"@types/node\": \"^25.5.2\",\n \"eslint\": \"^10.2.0\",\n \"typescript\": \"^6.0.2\"\n }\n}\n","numLines":21,"startLine":1,"totalLines":21}}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_012D6CU6DTSptzrbofg1DZzg","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"I'll build a complete Tetris game. I'll create:\n1. `index.html` - The main HTML file\n2. `src/tetris.ts` - The TypeScript source\n3. `tsconfig.json` - TypeScript configuration\n\nLet me write the complete Tetris game.","signature":"EpMDClsIDBgCKkBLFfxXeFpaS2qvr4Yo1/WG9VsmTkqHErN0O9vLHyLUGfpZFFjGICyeUJTrVJpxkTEpdY0hEHkBgi9Ly6CTTPo1MhFjbGF1ZGUtc29ubmV0LTQtNjgAEgw+A15flcJVP2gYFlgaDGPSqpwGg54YpafaCiIwSyHeKqarMJGcpQWvUEot70BvJLM/yhG1xvME6tjwl0GsFpbmkwRbNmInYslmr9JqKuUBnNQU9PF75EHA9qNOgR+pBeimvri9chkmK5+B+SnISmr4MQ6O8J8bv3stqTrsbzGWwiOn/YGVlTAElrbapHPoHcq6Vk6StwH+2RcsgqSkpYkbWRghrAqsA1y0av0JBaJD9Zt8NyJj/RiyxBEqeV+RABqRO/fUWGSI/ceIJZ2ZouIMOJ23OfaPvvibdrz0lE1M+RUusIZ+QYm6yiBDMwgjTzBjc6spf1sxbUibPeUE0mbiTIE1G+dIxUp04vgRTbpKqIscDzZJjnV7cOi5AVOGSMWdOySt0N/rXyQvfutxy4g5lS0UhxgB"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":482,"cache_read_input_tokens":4769,"cache_creation":{"ephemeral_5m_input_tokens":482,"ephemeral_1h_input_tokens":0},"output_tokens":8,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"b9799835-41c3-493a-a4b8-31b2cfe30204"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_012D6CU6DTSptzrbofg1DZzg","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01GtVVPNuAB1EaAb3fWYxoY6","name":"Bash","input":{"command":"ls /tmp/loop-bench-q82ugmwv/node_modules/.bin/ | head -20","description":"Check available binaries"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":482,"cache_read_input_tokens":4769,"cache_creation":{"ephemeral_5m_input_tokens":482,"ephemeral_1h_input_tokens":0},"output_tokens":8,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"246041a3-aec9-43e5-979b-96806c5d9203"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01GtVVPNuAB1EaAb3fWYxoY6","type":"tool_result","content":"acorn\neslint\nnode-which\nplaywright\nplaywright-core\ntsc\ntsserver","is_error":false}]},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"abeb1c31-41b6-4f40-a61a-8a32634ac688","timestamp":"2026-04-04T21:12:37.319Z","tool_use_result":{"stdout":"acorn\neslint\nnode-which\nplaywright\nplaywright-core\ntsc\ntsserver","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01MKaGTdmbRKMM5NnUxcJup7","type":"message","role":"assistant","content":[{"type":"text","text":"Now I'll create the full Tetris game:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":211,"cache_read_input_tokens":5251,"cache_creation":{"ephemeral_5m_input_tokens":211,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"8cdba558-f182-4c9a-acc4-3d03b4e03eab"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01MKaGTdmbRKMM5NnUxcJup7","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_013GWJZDwWEjqRR16CcRDv4q","name":"Bash","input":{"command":"cat > /tmp/loop-bench-q82ugmwv/tsconfig.json << 'EOF'\n{\n \"compilerOptions\": {\n \"target\": \"ES2020\",\n \"module\": \"ES2020\",\n \"moduleResolution\": \"bundler\",\n \"strict\": true,\n \"outDir\": \"./dist\",\n \"rootDir\": \"./src\",\n \"lib\": [\"ES2020\", \"DOM\"]\n },\n \"include\": [\"src/**/*\"]\n}\nEOF","description":"Create tsconfig.json"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":211,"cache_read_input_tokens":5251,"cache_creation":{"ephemeral_5m_input_tokens":211,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"a8db2507-4580-46cb-b01c-f89d11900b6a"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_013GWJZDwWEjqRR16CcRDv4q","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"2e666805-102c-4810-94bf-3fb4622b154b","timestamp":"2026-04-04T21:12:40.668Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01MdA2uRs1BHvLX2ZrcnDhoA","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_019J4Twhj6ijKBV6S2AyFPx4","name":"Bash","input":{"command":"mkdir -p /tmp/loop-bench-q82ugmwv/src /tmp/loop-bench-q82ugmwv/dist","description":"Create src and dist directories"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":223,"cache_read_input_tokens":5462,"cache_creation":{"ephemeral_5m_input_tokens":223,"ephemeral_1h_input_tokens":0},"output_tokens":64,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"ff866310-f30b-4167-9f78-423b81668565"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_019J4Twhj6ijKBV6S2AyFPx4","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"4af386db-e612-45fa-836c-1c788a9200f7","timestamp":"2026-04-04T21:12:43.139Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":true}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01VBzSF3unuvsfiaG81v8uhh","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01PnhiwtvFjEEfKuPdSH8mFF","name":"Bash","input":{"command":"cat > /tmp/loop-bench-q82ugmwv/src/tetris.ts << 'TSEOF'\n// ============================================================\n// Tetris – complete implementation\n// ============================================================\n\n// ── Types ────────────────────────────────────────────────────\n\ntype Color = string;\ntype Grid = (Color | null)[][];\ntype TetrominoShape = number[][];\n\ninterface Tetromino {\n shape: TetrominoShape;\n color: Color;\n x: number;\n y: number;\n}\n\n// ── Constants ────────────────────────────────────────────────\n\nconst COLS = 10;\nconst ROWS = 20;\nconst BLOCK = 30; // px per cell\nconst PREVIEW_BLOCK = 24;\n\nconst COLORS: Record<string, Color> = {\n I: \"#00f0f0\",\n O: \"#f0f000\",\n T: \"#a000f0\",\n S: \"#00f000\",\n Z: \"#f00000\",\n J: \"#0000f0\",\n L: \"#f0a000\",\n};\n\nconst SHAPES: Record<string, TetrominoShape> = {\n I: [[1, 1, 1, 1]],\n O: [\n [1, 1],\n [1, 1],\n ],\n T: [\n [0, 1, 0],\n [1, 1, 1],\n ],\n S: [\n [0, 1, 1],\n [1, 1, 0],\n ],\n Z: [\n [1, 1, 0],\n [0, 1, 1],\n ],\n J: [\n [1, 0, 0],\n [1, 1, 1],\n ],\n L: [\n [0, 0, 1],\n [1, 1, 1],\n ],\n};\n\nconst PIECE_KEYS = Object.keys(SHAPES);\n\n// Points awarded per number of lines cleared simultaneously\nconst LINE_POINTS = [0, 100, 300, 500, 800];\n\n// Drop interval in ms at each level (level 0 … 29)\nfunction dropInterval(level: number): number {\n // Classic NES-style speed curve (clamped at level 29)\n const speeds = [\n 800, 716, 633, 550, 466, 383, 300, 216, 133, 100, 83, 83, 83, 66, 66, 66,\n 50, 50, 50, 33, 33, 33, 33, 33, 33, 16, 16, 16, 16, 16,\n ];\n return speeds[Math.min(level, speeds.length - 1)];\n}\n\n// ── Piece factory ────────────────────────────────────────────\n\nfunction randomPiece(): Tetromino {\n const key = PIECE_KEYS[Math.floor(Math.random() * PIECE_KEYS.length)];\n const shape = SHAPES[key].map((row) => [...row]);\n return {\n shape,\n color: COLORS[key],\n x: Math.floor((COLS - shape[0].length) / 2),\n y: 0,\n };\n}\n\n// ── Rotation ─────────────────────────────────────────────────\n\nfunction rotate(shape: TetrominoShape): TetrominoShape {\n const rows = shape.length;\n const cols = shape[0].length;\n const result: TetrominoShape = Array.from({ length: cols }, () =>\n new Array(rows).fill(0)\n );\n for (let r = 0; r < rows; r++) {\n for (let c = 0; c < cols; c++) {\n result[c][rows - 1 - r] = shape[r][c];\n }\n }\n return result;\n}\n\n// ── Board helpers ────────────────────────────────────────────\n\nfunction emptyGrid(): Grid {\n return Array.from({ length: ROWS }, () => new Array<Color | null>(COLS).fill(null));\n}\n\nfunction isValid(grid: Grid, piece: Tetromino, dx = 0, dy = 0, shape?: TetrominoShape): boolean {\n const s = shape ?? piece.shape;\n for (let r = 0; r < s.length; r++) {\n for (let c = 0; c < s[r].length; c++) {\n if (!s[r][c]) continue;\n const nx = piece.x + c + dx;\n const ny = piece.y + r + dy;\n if (nx < 0 || nx >= COLS || ny >= ROWS) return false;\n if (ny < 0) continue; // allow above board\n if (grid[ny][nx] !== null) return false;\n }\n }\n return true;\n}\n\nfunction lock(grid: Grid, piece: Tetromino): Grid {\n const next = grid.map((row) => [...row]);\n for (let r = 0; r < piece.shape.length; r++) {\n for (let c = 0; c < piece.shape[r].length; c++) {\n if (!piece.shape[r][c]) continue;\n const ny = piece.y + r;\n const nx = piece.x + c;\n if (ny >= 0) next[ny][nx] = piece.color;\n }\n }\n return next;\n}\n\nfunction clearLines(grid: Grid): { grid: Grid; linesCleared: number } {\n const kept = grid.filter((row) => row.some((cell) => cell === null));\n const linesCleared = ROWS - kept.length;\n const newRows: Grid = Array.from({ length: linesCleared }, () =>\n new Array<Color | null>(COLS).fill(null)\n );\n return { grid: [...newRows, ...kept], linesCleared };\n}\n\n// ── Wall‑kick data (SRS) ──────────────────────────────────────\n// Offsets to try when a rotation is blocked\nconst KICKS: [number, number][] = [\n [0, 0],\n [-1, 0],\n [1, 0],\n [-1, -1],\n [1, -1],\n [0, -2],\n];\n\nfunction tryRotate(grid: Grid, piece: Tetromino): Tetromino | null {\n const newShape = rotate(piece.shape);\n for (const [dx, dy] of KICKS) {\n if (isValid(grid, piece, dx, dy, newShape)) {\n return { ...piece, shape: newShape, x: piece.x + dx, y: piece.y + dy };\n }\n }\n return null;\n}\n\n// ── Ghost piece ───────────────────────────────────────────────\n\nfunction ghostY(grid: Grid, piece: Tetromino): number {\n let dy = 0;\n while (isValid(grid, piece, 0, dy + 1)) dy++;\n return piece.y + dy;\n}\n\n// ── Canvas rendering ──────────────────────────────────────────\n\nfunction drawBlock(\n ctx: CanvasRenderingContext2D,\n x: number,\n y: number,\n color: Color,\n size: number,\n alpha = 1\n): void {\n ctx.save();\n ctx.globalAlpha = alpha;\n // Main fill\n ctx.fillStyle = color;\n ctx.fillRect(x * size + 1, y * size + 1, size - 2, size - 2);\n // Highlight top-left edge\n ctx.fillStyle = \"rgba(255,255,255,0.35)\";\n ctx.fillRect(x * size + 1, y * size + 1, size - 2, 3);\n ctx.fillRect(x * size + 1, y * size + 1, 3, size - 2);\n // Shadow bottom-right edge\n ctx.fillStyle = \"rgba(0,0,0,0.25)\";\n ctx.fillRect(x * size + 1, y * size + size - 4, size - 2, 3);\n ctx.fillRect(x * size + size - 4, y * size + 1, 3, size - 2);\n ctx.restore();\n}\n\nfunction drawGrid(ctx: CanvasRenderingContext2D, grid: Grid): void {\n for (let r = 0; r < ROWS; r++) {\n for (let c = 0; c < COLS; c++) {\n const cell = grid[r][c];\n if (cell) {\n drawBlock(ctx, c, r, cell, BLOCK);\n } else {\n ctx.fillStyle = \"rgba(255,255,255,0.03)\";\n ctx.fillRect(c * BLOCK, r * BLOCK, BLOCK, BLOCK);\n }\n }\n }\n}\n\nfunction drawPiece(ctx: CanvasRenderingContext2D, piece: Tetromino, alpha = 1): void {\n for (let r = 0; r < piece.shape.length; r++) {\n for (let c = 0; c < piece.shape[r].length; c++) {\n if (piece.shape[r][c]) {\n drawBlock(ctx, piece.x + c, piece.y + r, piece.color, BLOCK, alpha);\n }\n }\n }\n}\n\nfunction drawGhost(ctx: CanvasRenderingContext2D, grid: Grid, piece: Tetromino): void {\n const gy = ghostY(grid, piece);\n if (gy === piece.y) return;\n const ghost: Tetromino = { ...piece, y: gy };\n ctx.save();\n ctx.globalAlpha = 0.18;\n for (let r = 0; r < ghost.shape.length; r++) {\n for (let c = 0; c < ghost.shape[r].length; c++) {\n if (ghost.shape[r][c]) {\n ctx.fillStyle = piece.color;\n ctx.fillRect(\n (ghost.x + c) * BLOCK + 1,\n (ghost.y + r) * BLOCK + 1,\n BLOCK - 2,\n BLOCK - 2\n );\n ctx.strokeStyle = piece.color;\n ctx.lineWidth = 1.5;\n ctx.strokeRect(\n (ghost.x + c) * BLOCK + 2,\n (ghost.y + r) * BLOCK + 2,\n BLOCK - 4,\n BLOCK - 4\n );\n }\n }\n }\n ctx.restore();\n}\n\nfunction drawPreview(ctx: CanvasRenderingContext2D, piece: Tetromino): void {\n const pw = 4 * PREVIEW_BLOCK;\n const ph = 4 * PREVIEW_BLOCK;\n ctx.clearRect(0, 0, pw, ph);\n ctx.fillStyle = \"#111\";\n ctx.fillRect(0, 0, pw, ph);\n\n const cols = piece.shape[0].length;\n const rows = piece.shape.length;\n const offsetX = Math.floor((4 - cols) / 2);\n const offsetY = Math.floor((4 - rows) / 2);\n\n for (let r = 0; r < rows; r++) {\n for (let c = 0; c < cols; c++) {\n if (piece.shape[r][c]) {\n drawBlock(ctx, offsetX + c, offsetY + r, piece.color, PREVIEW_BLOCK);\n }\n }\n }\n}\n\n// ── Game class ────────────────────────────────────────────────\n\nclass Tetris {\n private canvas: HTMLCanvasElement;\n private ctx: CanvasRenderingContext2D;\n private previewCanvas: HTMLCanvasElement;\n private previewCtx: CanvasRenderingContext2D;\n\n private grid: Grid = emptyGrid();\n private current: Tetromino = randomPiece();\n private next: Tetromino = randomPiece();\n\n private score = 0;\n private lines = 0;\n private level = 0;\n private highScore = 0;\n\n private paused = false;\n private gameOver = false;\n\n private lastTime = 0;\n private dropAccum = 0;\n private rafId = 0;\n\n // soft-drop tracking\n private softDropping = false;\n\n constructor() {\n this.canvas = document.getElementById(\"board\") as HTMLCanvasElement;\n this.ctx = this.canvas.getContext(\"2d\")!;\n this.previewCanvas = document.getElementById(\"preview\") as HTMLCanvasElement;\n this.previewCtx = this.previewCanvas.getContext(\"2d\")!;\n\n this.canvas.width = COLS * BLOCK;\n this.canvas.height = ROWS * BLOCK;\n this.previewCanvas.width = 4 * PREVIEW_BLOCK;\n this.previewCanvas.height = 4 * PREVIEW_BLOCK;\n\n this.loadHighScore();\n this.bindKeys();\n this.render(0);\n }\n\n // ── Persistence ──────────────────────────────────────────────\n\n private loadHighScore(): void {\n const stored = localStorage.getItem(\"tetris-highscore\");\n if (stored) this.highScore = parseInt(stored, 10);\n this.updateHUD();\n }\n\n private saveHighScore(): void {\n if (this.score > this.highScore) {\n this.highScore = this.score;\n localStorage.setItem(\"tetris-highscore\", String(this.highScore));\n }\n }\n\n // ── HUD ───────────────────────────────────────────────────────\n\n private updateHUD(): void {\n (document.getElementById(\"score\") as HTMLElement).textContent = String(this.score);\n (document.getElementById(\"lines\") as HTMLElement).textContent = String(this.lines);\n (document.getElementById(\"level\") as HTMLElement).textContent = String(this.level);\n (document.getElementById(\"highscore\") as HTMLElement).textContent = String(this.highScore);\n }\n\n // ── Input ─────────────────────────────────────────────────────\n\n private bindKeys(): void {\n document.addEventListener(\"keydown\", (e) => {\n if (this.gameOver) {\n if (e.code === \"Enter\" || e.code === \"Space\") this.restart();\n return;\n }\n if (e.code === \"KeyP\" || e.code === \"Escape\") {\n this.togglePause();\n return;\n }\n if (this.paused) return;\n\n switch (e.code) {\n case \"ArrowLeft\":\n e.preventDefault();\n this.move(-1, 0);\n break;\n case \"ArrowRight\":\n e.preventDefault();\n this.move(1, 0);\n break;\n case \"ArrowDown\":\n e.preventDefault();\n this.softDropping = true;\n this.move(0, 1);\n break;\n case \"ArrowUp\":\n case \"KeyX\":\n e.preventDefault();\n this.rotatePiece();\n break;\n case \"KeyZ\":\n e.preventDefault();\n this.rotateCCW();\n break;\n case \"Space\":\n e.preventDefault();\n this.hardDrop();\n break;\n case \"KeyC\":\n // hold – not implemented but key reserved\n break;\n }\n });\n\n document.addEventListener(\"keyup\", (e) => {\n if (e.code === \"ArrowDown\") this.softDropping = false;\n });\n }\n\n // ── Actions ───────────────────────────────────────────────────\n\n private move(dx: number, dy: number): boolean {\n if (isValid(this.grid, this.current, dx, dy)) {\n this.current = { ...this.current, x: this.current.x + dx, y: this.current.y + dy };\n if (dy > 0 && this.softDropping) {\n // award 1 point per soft-drop row\n this.score += 1;\n this.updateHUD();\n }\n return true;\n }\n return false;\n }\n\n private rotatePiece(): void {\n const rotated = tryRotate(this.grid, this.current);\n if (rotated) this.current = rotated;\n }\n\n private rotateCCW(): void {\n // Three CW rotations = one CCW rotation\n let piece = this.current;\n for (let i = 0; i < 3; i++) {\n const r = tryRotate(this.grid, piece);\n if (!r) return;\n piece = r;\n }\n this.current = piece;\n }\n\n private hardDrop(): void {\n let dropped = 0;\n while (this.move(0, 1)) dropped++;\n this.score += dropped * 2;\n this.updateHUD();\n this.lockPiece();\n }\n\n private lockPiece(): void {\n this.grid = lock(this.grid, this.current);\n const { grid, linesCleared } = clearLines(this.grid);\n this.grid = grid;\n\n if (linesCleared > 0) {\n this.score += LINE_POINTS[linesCleared] * (this.level + 1);\n this.lines += linesCleared;\n this.level = Math.floor(this.lines / 10);\n this.updateHUD();\n }\n\n this.current = this.next;\n this.next = randomPiece();\n this.dropAccum = 0;\n\n if (!isValid(this.grid, this.current)) {\n this.endGame();\n }\n }\n\n private togglePause(): void {\n if (this.gameOver) return;\n this.paused = !this.paused;\n (document.getElementById(\"pause-overlay\") as HTMLElement).style.display = this.paused\n ? \"flex\"\n : \"none\";\n if (!this.paused) {\n this.lastTime = performance.now();\n this.rafId = requestAnimationFrame((t) => this.render(t));\n }\n }\n\n private endGame(): void {\n this.gameOver = true;\n this.saveHighScore();\n this.updateHUD();\n (document.getElementById(\"gameover-overlay\") as HTMLElement).style.display = \"flex\";\n (document.getElementById(\"final-score\") as HTMLElement).textContent = String(this.score);\n cancelAnimationFrame(this.rafId);\n }\n\n private restart(): void {\n this.grid = emptyGrid();\n this.current = randomPiece();\n this.next = randomPiece();\n this.score = 0;\n this.lines = 0;\n this.level = 0;\n this.paused = false;\n this.gameOver = false;\n this.dropAccum = 0;\n this.softDropping = false;\n this.updateHUD();\n (document.getElementById(\"gameover-overlay\") as HTMLElement).style.display = \"none\";\n (document.getElementById(\"pause-overlay\") as HTMLElement).style.display = \"none\";\n this.lastTime = performance.now();\n this.rafId = requestAnimationFrame((t) => this.render(t));\n }\n\n // ── Game loop ─────────────────────────────────────────────────\n\n private render(timestamp: number): void {\n if (this.paused || this.gameOver) return;\n\n const delta = timestamp - this.lastTime;\n this.lastTime = timestamp;\n this.dropAccum += delta;\n\n const interval = this.softDropping\n ? Math.min(dropInterval(this.level) / 20, 50)\n : dropInterval(this.level);\n\n if (this.dropAccum >= interval) {\n this.dropAccum = 0;\n if (!this.move(0, 1)) {\n this.lockPiece();\n }\n }\n\n this.draw();\n this.rafId = requestAnimationFrame((t) => this.render(t));\n }\n\n private draw(): void {\n const ctx = this.ctx;\n // Background\n ctx.fillStyle = \"#111\";\n ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);\n\n drawGrid(ctx, this.grid);\n drawGhost(ctx, this.grid, this.current);\n drawPiece(ctx, this.current);\n\n // Grid lines\n ctx.strokeStyle = \"rgba(255,255,255,0.04)\";\n ctx.lineWidth = 0.5;\n for (let c = 0; c <= COLS; c++) {\n ctx.beginPath();\n ctx.moveTo(c * BLOCK, 0);\n ctx.lineTo(c * BLOCK, this.canvas.height);\n ctx.stroke();\n }\n for (let r = 0; r <= ROWS; r++) {\n ctx.beginPath();\n ctx.moveTo(0, r * BLOCK);\n ctx.lineTo(this.canvas.width, r * BLOCK);\n ctx.stroke();\n }\n\n drawPreview(this.previewCtx, this.next);\n }\n}\n\n// ── Boot ──────────────────────────────────────────────────────\n\nwindow.addEventListener(\"DOMContentLoaded\", () => {\n new Tetris();\n});\nTSEOF","description":"Write the Tetris TypeScript source file"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":123,"cache_read_input_tokens":5685,"cache_creation":{"ephemeral_5m_input_tokens":123,"ephemeral_1h_input_tokens":0},"output_tokens":60,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"9c188ba6-cd77-4aa4-871b-ffdbd73b0d5f"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01PnhiwtvFjEEfKuPdSH8mFF","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"701f224f-2a40-4988-9f66-922d7d3906c1","timestamp":"2026-04-04T21:13:40.579Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_013SRPQ7q34zmpXfC9tm9ixP","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01HgeLWP1z9AQTcg7DjT9G2q","name":"Bash","input":{"command":"./node_modules/.bin/tsc --project tsconfig.json 2>&1","description":"Compile TypeScript"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":5578,"cache_read_input_tokens":5808,"cache_creation":{"ephemeral_5m_input_tokens":5578,"ephemeral_1h_input_tokens":0},"output_tokens":37,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"ea96f7cf-f7da-43e6-b218-085e9420af8e"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01HgeLWP1z9AQTcg7DjT9G2q","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"b294ab7f-84a5-4ef2-86d8-52ccd6218938","timestamp":"2026-04-04T21:13:45.398Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01UMrkoy7Takwh2YPkSobqGQ","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01B4TajASeppxLEwo1rWrqe9","name":"Bash","input":{"command":"ls /tmp/loop-bench-q82ugmwv/dist/","description":"Verify compiled output"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":108,"cache_read_input_tokens":11386,"cache_creation":{"ephemeral_5m_input_tokens":108,"ephemeral_1h_input_tokens":0},"output_tokens":36,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"5d201bfc-83bd-4c08-bcf4-3e2960e73bcc"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01B4TajASeppxLEwo1rWrqe9","type":"tool_result","content":"tetris.js","is_error":false}]},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"d90cf097-0153-45f5-abbb-5aaaa0dd942b","timestamp":"2026-04-04T21:13:52.773Z","tool_use_result":{"stdout":"tetris.js","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_015Q26s7CQT33QGT6rpC1jg2","type":"message","role":"assistant","content":[{"type":"text","text":"Now create the HTML page:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":212,"cache_read_input_tokens":11386,"cache_creation":{"ephemeral_5m_input_tokens":212,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"84bf1968-e5eb-43dc-b881-a10b12908fc9"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_015Q26s7CQT33QGT6rpC1jg2","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01MorSkDNEYbZTeCncLS2k3f","name":"Bash","input":{"command":"cat > /tmp/loop-bench-q82ugmwv/index.html << 'EOF'\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Tetris</title>\n <style>\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\n body {\n background: #0a0a0f;\n color: #e0e0e0;\n font-family: \"Segoe UI\", system-ui, sans-serif;\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 100vh;\n gap: 0;\n }\n\n /* ── Layout wrapper ── */\n #game-wrapper {\n display: flex;\n align-items: flex-start;\n gap: 20px;\n }\n\n /* ── Board ── */\n #board-container {\n position: relative;\n }\n\n #board {\n display: block;\n border: 2px solid #333;\n border-radius: 4px;\n box-shadow: 0 0 40px rgba(0, 240, 240, 0.08);\n }\n\n /* ── Overlays ── */\n .overlay {\n display: none;\n position: absolute;\n inset: 0;\n background: rgba(0,0,0,0.82);\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 16px;\n border-radius: 4px;\n backdrop-filter: blur(4px);\n }\n\n .overlay h2 {\n font-size: 2rem;\n letter-spacing: 4px;\n text-transform: uppercase;\n }\n\n .overlay p {\n color: #aaa;\n font-size: 0.9rem;\n letter-spacing: 1px;\n }\n\n #gameover-overlay h2 { color: #f04040; }\n #pause-overlay h2 { color: #f0f000; }\n\n .final-score-label { color: #aaa; font-size: 0.85rem; letter-spacing: 2px; text-transform: uppercase; }\n #final-score { font-size: 2.5rem; font-weight: 700; color: #fff; }\n\n /* ── Side panel ── */\n #side-panel {\n display: flex;\n flex-direction: column;\n gap: 20px;\n width: 130px;\n }\n\n .panel-box {\n background: #141418;\n border: 1px solid #2a2a35;\n border-radius: 6px;\n padding: 12px 14px;\n }\n\n .panel-box .label {\n font-size: 0.68rem;\n letter-spacing: 2px;\n text-transform: uppercase;\n color: #666;\n margin-bottom: 4px;\n }\n\n .panel-box .value {\n font-size: 1.5rem;\n font-weight: 700;\n color: #fff;\n letter-spacing: 1px;\n }\n\n #preview-box .label { margin-bottom: 8px; }\n\n #preview {\n display: block;\n border-radius: 3px;\n }\n\n /* ── Controls help ── */\n #controls {\n background: #141418;\n border: 1px solid #2a2a35;\n border-radius: 6px;\n padding: 12px 14px;\n font-size: 0.7rem;\n line-height: 1.9;\n color: #888;\n }\n\n #controls .ctrl-title {\n font-size: 0.68rem;\n letter-spacing: 2px;\n text-transform: uppercase;\n color: #555;\n margin-bottom: 6px;\n }\n\n kbd {\n display: inline-block;\n background: #222;\n border: 1px solid #444;\n border-radius: 3px;\n padding: 0 4px;\n font-family: inherit;\n font-size: 0.68rem;\n color: #ccc;\n }\n </style>\n</head>\n<body>\n <div id=\"game-wrapper\">\n\n <!-- Board + overlays -->\n <div id=\"board-container\">\n <canvas id=\"board\"></canvas>\n\n <div id=\"pause-overlay\" class=\"overlay\">\n <h2>Paused</h2>\n <p>Press <kbd>P</kbd> or <kbd>Esc</kbd> to resume</p>\n </div>\n\n <div id=\"gameover-overlay\" class=\"overlay\">\n <h2>Game Over</h2>\n <p class=\"final-score-label\">Score</p>\n <div id=\"final-score\">0</div>\n <p>Press <kbd>Enter</kbd> or <kbd>Space</kbd> to restart</p>\n </div>\n </div>\n\n <!-- Side panel -->\n <div id=\"side-panel\">\n\n <div class=\"panel-box\" id=\"preview-box\">\n <div class=\"label\">Next</div>\n <canvas id=\"preview\"></canvas>\n </div>\n\n <div class=\"panel-box\">\n <div class=\"label\">Score</div>\n <div class=\"value\" id=\"score\">0</div>\n </div>\n\n <div class=\"panel-box\">\n <div class=\"label\">Lines</div>\n <div class=\"value\" id=\"lines\">0</div>\n </div>\n\n <div class=\"panel-box\">\n <div class=\"label\">Level</div>\n <div class=\"value\" id=\"level\">0</div>\n </div>\n\n <div class=\"panel-box\">\n <div class=\"label\">Best</div>\n <div class=\"value\" id=\"highscore\">0</div>\n </div>\n\n <div id=\"controls\">\n <div class=\"ctrl-title\">Controls</div>\n <div><kbd>←</kbd><kbd>→</kbd> Move</div>\n <div><kbd>↑</kbd> / <kbd>X</kbd> Rotate CW</div>\n <div><kbd>Z</kbd> Rotate CCW</div>\n <div><kbd>↓</kbd> Soft drop</div>\n <div><kbd>Space</kbd> Hard drop</div>\n <div><kbd>P</kbd> Pause</div>\n </div>\n\n </div>\n </div>\n\n <script type=\"module\" src=\"dist/tetris.js\"></script>\n</body>\n</html>\nEOF","description":"Write the HTML file"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":212,"cache_read_input_tokens":11386,"cache_creation":{"ephemeral_5m_input_tokens":212,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"418873a4-c13e-4321-aa0b-5bce99c2f597"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01MorSkDNEYbZTeCncLS2k3f","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"5f92b975-5a33-41bd-ab64-da77d2ba94da","timestamp":"2026-04-04T21:14:12.956Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01JEPFnYRkGBvPEzHDAjngdg","type":"message","role":"assistant","content":[{"type":"text","text":"Let me verify everything looks correct:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":1842,"cache_read_input_tokens":11598,"cache_creation":{"ephemeral_5m_input_tokens":1842,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"594416ab-9f20-4af9-af61-b12ec5162b1e"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01JEPFnYRkGBvPEzHDAjngdg","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_019hQXxSJc5JjqBUp351LXj3","name":"Read","input":{"file_path":"/tmp/loop-bench-q82ugmwv/dist/tetris.js","limit":30},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":1842,"cache_read_input_tokens":11598,"cache_creation":{"ephemeral_5m_input_tokens":1842,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"7a67398b-c90c-4440-a401-fcfb81a9f539"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_019hQXxSJc5JjqBUp351LXj3","type":"tool_result","content":"1\t\"use strict\";\n2\t// ============================================================\n3\t// Tetris – complete implementation\n4\t// ============================================================\n5\t// ── Constants ────────────────────────────────────────────────\n6\tconst COLS = 10;\n7\tconst ROWS = 20;\n8\tconst BLOCK = 30; // px per cell\n9\tconst PREVIEW_BLOCK = 24;\n10\tconst COLORS = {\n11\t I: \"#00f0f0\",\n12\t O: \"#f0f000\",\n13\t T: \"#a000f0\",\n14\t S: \"#00f000\",\n15\t Z: \"#f00000\",\n16\t J: \"#0000f0\",\n17\t L: \"#f0a000\",\n18\t};\n19\tconst SHAPES = {\n20\t I: [[1, 1, 1, 1]],\n21\t O: [\n22\t [1, 1],\n23\t [1, 1],\n24\t ],\n25\t T: [\n26\t [0, 1, 0],\n27\t [1, 1, 1],\n28\t ],\n29\t S: [\n30\t [0, 1, 1],"}]},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"d41cc10c-ab1b-4f0d-8351-deffd2309791","timestamp":"2026-04-04T21:14:15.543Z","tool_use_result":{"type":"text","file":{"filePath":"/tmp/loop-bench-q82ugmwv/dist/tetris.js","content":"\"use strict\";\n// ============================================================\n// Tetris – complete implementation\n// ============================================================\n// ── Constants ────────────────────────────────────────────────\nconst COLS = 10;\nconst ROWS = 20;\nconst BLOCK = 30; // px per cell\nconst PREVIEW_BLOCK = 24;\nconst COLORS = {\n I: \"#00f0f0\",\n O: \"#f0f000\",\n T: \"#a000f0\",\n S: \"#00f000\",\n Z: \"#f00000\",\n J: \"#0000f0\",\n L: \"#f0a000\",\n};\nconst SHAPES = {\n I: [[1, 1, 1, 1]],\n O: [\n [1, 1],\n [1, 1],\n ],\n T: [\n [0, 1, 0],\n [1, 1, 1],\n ],\n S: [\n [0, 1, 1],","numLines":30,"startLine":1,"totalLines":467}}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01VFMgSbZDH4jrqdCDX7XGS8","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01AyaTeZtcw53vHZSnCiQvBL","name":"Bash","input":{"command":"# Quick sanity check: look for the module type issue in the compiled JS\nhead -5 /tmp/loop-bench-q82ugmwv/dist/tetris.js && echo \"---\" && tail -5 /tmp/loop-bench-q82ugmwv/dist/tetris.js","description":"Check top and bottom of compiled JS"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":418,"cache_read_input_tokens":13440,"cache_creation":{"ephemeral_5m_input_tokens":418,"ephemeral_1h_input_tokens":0},"output_tokens":35,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"8f95155a-afec-4a6b-b466-949c2168d2d0"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01AyaTeZtcw53vHZSnCiQvBL","type":"tool_result","content":"\"use strict\";\n// ============================================================\n// Tetris – complete implementation\n// ============================================================\n// ── Constants ────────────────────────────────────────────────\n---\n}\n// ── Boot ──────────────────────────────────────────────────────\nwindow.addEventListener(\"DOMContentLoaded\", () => {\n new Tetris();\n});","is_error":false}]},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"63ba833f-8182-41de-aa0a-10e95834ff86","timestamp":"2026-04-04T21:14:18.185Z","tool_use_result":{"stdout":"\"use strict\";\n// ============================================================\n// Tetris – complete implementation\n// ============================================================\n// ── Constants ────────────────────────────────────────────────\n---\n}\n// ── Boot ──────────────────────────────────────────────────────\nwindow.addEventListener(\"DOMContentLoaded\", () => {\n new Tetris();\n});","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01HJP41fns5RdBTTE7vXs1cA","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"The compiled JS starts with \"use strict\" and doesn't have `export {}` at the top, which is important when using it as a module. However, since the HTML uses `type=\"module\"`, modules automatically use strict mode. The tsconfig has `\"module\": \"ES2020\"` but the compiled output doesn't seem to be ES module format (it has \"use strict\" but no `export {}` or `import`). Let me check the tsconfig more carefully.\n\nWait, the `\"module\": \"ES2020\"` setting means it should output ES modules. But the file starts with \"use strict\" without `export {}`. Let me check the full compiled file...\n\n\nActually, I'm realizing there's a conflict here—the package.json declares `\"type\": \"commonjs\"` while the tsconfig specifies `\"module\": \"ES2020\"`, and the TypeScript compiler seems to be following the package.json setting instead, outputting CommonJS-style code with \"use strict\" rather than proper ES modules. the \"use strict\" declaration is redundant since modules are automatically strict, but it won't cause problems. Let me verify the HTML is pointing to the right compiled file and then run a quick playwright test to ensure the canvas renders without any JavaScript errors.","signature":"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"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":243,"cache_read_input_tokens":13858,"cache_creation":{"ephemeral_5m_input_tokens":243,"ephemeral_1h_input_tokens":0},"output_tokens":10,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"0ce00289-0a55-43f0-8edf-2dda38ff2f04"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01HJP41fns5RdBTTE7vXs1cA","type":"message","role":"assistant","content":[{"type":"text","text":"Now let me run a quick Playwright smoke test to verify the game works:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":243,"cache_read_input_tokens":13858,"cache_creation":{"ephemeral_5m_input_tokens":243,"ephemeral_1h_input_tokens":0},"output_tokens":10,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"f6d9690f-5864-4c90-9e7e-0431b954a0b9"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01HJP41fns5RdBTTE7vXs1cA","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_013ZfoPxdQx7gMyHAkqZBmZa","name":"Bash","input":{"command":"cat > /tmp/smoke-test.mjs << 'EOF'\nimport { chromium } from '@playwright/test';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\n(async () => {\n const browser = await chromium.launch();\n const page = await browser.newPage();\n\n const errors = [];\n page.on('pageerror', e => errors.push(e.message));\n page.on('console', msg => {\n if (msg.type() === 'error') errors.push(msg.text());\n });\n\n const file = path.resolve('/tmp/loop-bench-q82ugmwv/index.html');\n await page.goto(`file://${file}`);\n await page.waitForTimeout(1000);\n\n // Check canvas exists and has dimensions\n const boardSize = await page.evaluate(() => {\n const c = document.getElementById('board');\n return { w: c.width, h: c.height };\n });\n\n console.log('Board canvas size:', boardSize);\n console.log('Errors:', errors.length ? errors : 'none');\n\n // Check HUD elements exist\n const hudValues = await page.evaluate(() => {\n return {\n score: document.getElementById('score').textContent,\n lines: document.getElementById('lines').textContent,\n level: document.getElementById('level').textContent,\n };\n });\n console.log('HUD values:', hudValues);\n\n // Simulate a few keystrokes\n await page.keyboard.press('ArrowLeft');\n await page.keyboard.press('ArrowRight');\n await page.keyboard.press('ArrowUp');\n await page.waitForTimeout(200);\n\n // Hard drop\n await page.keyboard.press('Space');\n await page.waitForTimeout(200);\n\n const scoreAfter = await page.evaluate(() => document.getElementById('score').textContent);\n console.log('Score after hard drop:', scoreAfter);\n\n // Pause / resume\n await page.keyboard.press('p');\n const pauseVisible = await page.evaluate(() => {\n const el = document.getElementById('pause-overlay');\n return el.style.display;\n });\n console.log('Pause overlay display:', pauseVisible);\n\n await page.keyboard.press('p'); // resume\n\n await browser.close();\n if (errors.length) process.exit(1);\n console.log('\\n✅ Smoke test passed');\n})();\nEOF\nnode /tmp/smoke-test.mjs","description":"Run Playwright smoke test to verify game functionality","timeout":30000},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":243,"cache_read_input_tokens":13858,"cache_creation":{"ephemeral_5m_input_tokens":243,"ephemeral_1h_input_tokens":0},"output_tokens":10,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"a34fbdc7-75eb-4dd1-aec7-069734453f22"} +{"type":"user","message":{"role":"user","content":[{"type":"tool_result","content":"Exit code 1\nnode:internal/modules/package_json_reader:314\n throw new ERR_MODULE_NOT_FOUND(packageName, fileURLToPath(base), null);\n ^\n\nError [ERR_MODULE_NOT_FOUND]: Cannot find package '@playwright/test' imported from /tmp/smoke-test.mjs\n at Object.getPackageJSONURL (node:internal/modules/package_json_reader:314:9)\n at packageResolve (node:internal/modules/esm/resolve:767:81)\n at moduleResolve (node:internal/modules/esm/resolve:853:18)\n at defaultResolve (node:internal/modules/esm/resolve:983:11)\n at #cachedDefaultResolve (node:internal/modules/esm/loader:731:20)\n at ModuleLoader.resolve (node:internal/modules/esm/loader:708:38)\n at ModuleLoader.getModuleJobForImport (node:internal/modules/esm/loader:310:38)\n at ModuleJob._link (node:internal/modules/esm/module_job:182:49) {\n code: 'ERR_MODULE_NOT_FOUND'\n}\n\nNode.js v22.22.0","is_error":true,"tool_use_id":"toolu_013ZfoPxdQx7gMyHAkqZBmZa"}]},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"558c0068-f594-46c0-9e47-36514c4ba15d","timestamp":"2026-04-04T21:14:42.219Z","tool_use_result":"Error: Exit code 1\nnode:internal/modules/package_json_reader:314\n throw new ERR_MODULE_NOT_FOUND(packageName, fileURLToPath(base), null);\n ^\n\nError [ERR_MODULE_NOT_FOUND]: Cannot find package '@playwright/test' imported from /tmp/smoke-test.mjs\n at Object.getPackageJSONURL (node:internal/modules/package_json_reader:314:9)\n at packageResolve (node:internal/modules/esm/resolve:767:81)\n at moduleResolve (node:internal/modules/esm/resolve:853:18)\n at defaultResolve (node:internal/modules/esm/resolve:983:11)\n at #cachedDefaultResolve (node:internal/modules/esm/loader:731:20)\n at ModuleLoader.resolve (node:internal/modules/esm/loader:708:38)\n at ModuleLoader.getModuleJobForImport (node:internal/modules/esm/loader:310:38)\n at ModuleJob._link (node:internal/modules/esm/module_job:182:49) {\n code: 'ERR_MODULE_NOT_FOUND'\n}\n\nNode.js v22.22.0"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01GTrhV99QhFbC3Q76pDXMrc","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_018KCbuw53g7tHrRN3sAqSdo","name":"Bash","input":{"command":"cat > /tmp/loop-bench-q82ugmwv/smoke-test.mjs << 'EOF'\nimport { chromium } from './node_modules/playwright-core/index.js';\nimport path from 'path';\n\n(async () => {\n const browser = await chromium.launch();\n const page = await browser.newPage();\n\n const errors = [];\n page.on('pageerror', e => errors.push(e.message));\n page.on('console', msg => {\n if (msg.type() === 'error') errors.push(msg.text());\n });\n\n const file = path.resolve('/tmp/loop-bench-q82ugmwv/index.html');\n await page.goto(`file://${file}`);\n await page.waitForTimeout(1200);\n\n const boardSize = await page.evaluate(() => {\n const c = document.getElementById('board');\n return { w: c.width, h: c.height };\n });\n console.log('Board size:', boardSize);\n\n const hudValues = await page.evaluate(() => ({\n score: document.getElementById('score').textContent,\n lines: document.getElementById('lines').textContent,\n level: document.getElementById('level').textContent,\n }));\n console.log('HUD:', hudValues);\n\n await page.keyboard.press('ArrowLeft');\n await page.keyboard.press('ArrowRight');\n await page.keyboard.press('ArrowUp');\n await page.keyboard.press('Space'); // hard drop\n await page.waitForTimeout(300);\n\n const scoreAfter = await page.evaluate(() => document.getElementById('score').textContent);\n console.log('Score after hard drop:', scoreAfter);\n\n const pauseVisible = await page.evaluate(() => {\n document.dispatchEvent(new KeyboardEvent('keydown', { code: 'KeyP', bubbles: true }));\n return document.getElementById('pause-overlay').style.display;\n });\n console.log('Pause overlay after P:', pauseVisible);\n\n await browser.close();\n\n if (errors.length) { console.error('JS Errors:', errors); process.exit(1); }\n console.log('\\n✅ All checks passed');\n})();\nEOF\nnode smoke-test.mjs","description":"Run smoke test from project directory","timeout":30000},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":1826,"cache_read_input_tokens":14101,"cache_creation":{"ephemeral_5m_input_tokens":1826,"ephemeral_1h_input_tokens":0},"output_tokens":56,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"6f0a227f-a56f-46dd-85c0-77d5f3e00769"} +{"type":"user","message":{"role":"user","content":[{"type":"tool_result","content":"Exit code 1\nfile:///tmp/loop-bench-q82ugmwv/smoke-test.mjs:1\nimport { chromium } from './node_modules/playwright-core/index.js';\n ^^^^^^^^\nSyntaxError: Named export 'chromium' not found. The requested module './node_modules/playwright-core/index.js' is a CommonJS module, which may not support all module.exports as named exports.\nCommonJS modules can always be imported via the default export, for example using:\n\nimport pkg from './node_modules/playwright-core/index.js';\nconst { chromium } = pkg;\n\n at ModuleJob._instantiate (node:internal/modules/esm/module_job:226:21)\n at async ModuleJob.run (node:internal/modules/esm/module_job:335:5)\n at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:665:26)\n at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:117:5)\n\nNode.js v22.22.0","is_error":true,"tool_use_id":"toolu_018KCbuw53g7tHrRN3sAqSdo"}]},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"d419c182-9d6c-4d3d-9fa8-918e3963227c","timestamp":"2026-04-04T21:14:51.428Z","tool_use_result":"Error: Exit code 1\nfile:///tmp/loop-bench-q82ugmwv/smoke-test.mjs:1\nimport { chromium } from './node_modules/playwright-core/index.js';\n ^^^^^^^^\nSyntaxError: Named export 'chromium' not found. The requested module './node_modules/playwright-core/index.js' is a CommonJS module, which may not support all module.exports as named exports.\nCommonJS modules can always be imported via the default export, for example using:\n\nimport pkg from './node_modules/playwright-core/index.js';\nconst { chromium } = pkg;\n\n at ModuleJob._instantiate (node:internal/modules/esm/module_job:226:21)\n at async ModuleJob.run (node:internal/modules/esm/module_job:335:5)\n at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:665:26)\n at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:117:5)\n\nNode.js v22.22.0"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01Nenxz6Q3KKe4h4hYUCFXsu","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01DD22XKxStHRzBPo8EG1KTY","name":"Bash","input":{"command":"node -e \"\nconst { chromium } = require('./node_modules/@playwright/test');\n(async () => {\n const browser = await chromium.launch();\n const page = await browser.newPage();\n const errors = [];\n page.on('pageerror', e => errors.push(e.message));\n const file = require('path').resolve('./index.html');\n await page.goto('file://' + file);\n await page.waitForTimeout(1200);\n const sz = await page.evaluate(() => ({ w: document.getElementById('board').width, h: document.getElementById('board').height }));\n console.log('Canvas size:', sz);\n const hud = await page.evaluate(() => ({ score: document.getElementById('score').textContent, lines: document.getElementById('lines').textContent, level: document.getElementById('level').textContent }));\n console.log('HUD:', hud);\n await page.keyboard.press('Space');\n await page.waitForTimeout(400);\n const s2 = await page.evaluate(() => document.getElementById('score').textContent);\n console.log('Score after hard drop:', s2);\n await browser.close();\n if (errors.length) { console.error('Errors:', errors); process.exit(1); }\n console.log('PASS');\n})();\n\"","description":"Run Playwright smoke test with CommonJS require","timeout":30000},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":937,"cache_read_input_tokens":15927,"cache_creation":{"ephemeral_5m_input_tokens":937,"ephemeral_1h_input_tokens":0},"output_tokens":57,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"f29e6575-63e5-4f81-9a2a-0b59aba442b7"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01DD22XKxStHRzBPo8EG1KTY","type":"tool_result","content":"Canvas size: { w: 300, h: 150 }\nHUD: { score: '0', lines: '0', level: '0' }\nScore after hard drop: 0\nPASS","is_error":false}]},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"8a524397-1a83-499d-b7d6-215345b98e33","timestamp":"2026-04-04T21:14:59.941Z","tool_use_result":{"stdout":"Canvas size: { w: 300, h: 150 }\nHUD: { score: '0', lines: '0', level: '0' }\nScore after hard drop: 0\nPASS","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01K2L94QYRbSwkr3DcEmKFkv","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"The canvas size is 300x150 which is the default canvas size, not the expected 300x600 (10*30 x 20*30). This is because the JS isn't loading properly due to the `type=\"module\"` and the ES module issue. Let me check what's happening.\n\nThe canvas is 300x150, which is the default HTML canvas size (300x150 is the default when no width/height attributes are set). This means the Tetris JS code isn't running and setting the canvas dimensions.\n\nThe issue is likely that the page can't load the JS file via file:// protocol with type=\"module\" due to CORS restrictions on file:// URLs.\n\n\nI need to fix this by either removing the `type=\"module\"` attribute and using regular JavaScript, or switching to HTTP serving. The real problem is that Chrome blocks ES module imports from `file://` protocol, which is why the script never executes. Let me adjust the TypeScript configuration to output standard JavaScript instead.","signature":"EqwLClsIDBgCKkDR+wk7KlWQhTUEjrUPuBP46gsHVAo4zmPPIoS77ZygvR3o73YwnQfIvEntjnMnYK/TbNPVK08xEJbOuVPkqVboMhFjbGF1ZGUtc29ubmV0LTQtNjgAEgw3iw9rdLRaa8HVm9AaDL8PWSgFtsuJXKVUQCIw8lQjz2TyBmjw0uyZWmGK7qBRmCGZI84OSYXk+ONPw0tH5eTYe4lUVjEU7hP75KHXKv4JeIxe48FdMCrH12P2sxWY76aCiOea5ZemuYb3K1ur+DX8XWuq5/03PAzcJ3SMqxY4hS0+7YheZ6ifA+om9CyjJbHfPToIhkwXr9ptkxsgDhoq17DYMJF3+isj7V9h6NVCLEL1GrI5XBsq6V9e1SdGEssxXZVuanoy1LshNOZG3PzNfT/8N91SJF77y/MG+lY7/gk1qDpa1TORIoVKRhzqA1MV/pj5YKeJ8l1q7u94OSdNyiQdSL99NLgYwB4cv4Z9sBUgKQSr075qx8zRYmTtKuy3ljD0FHOgYq74LZLmNNqnRIiMy+5gtUPmYR9JRroEkFcismy8SLxh8uHSIQQvVbe/WwN/FYe18757/nY3iguLYISbhHlrtb6DK97IwJYR2boIeYiQh0IcayxksIOrasO3hzCDKzj3UPDHq7Pm3E7hfQKHsVR5ogeIx2ptm+LkPsuzaBDKmaxxPJEiigO0p/DpsAvCkzeUlXkqbh3pd2XpgUB0MaYAa623XZk6+bNGXV9tQ4u86Plolu5PLPiOa8bOyeszOAurysFYmcFUEb1V70lJzoeqZLx7UZgIg70W/qIhYjMBV4z3x8ESOeIuKWHW6Z6+hOpnDAnlRK2NYeqPHqvKvFDSwdoxa5C0luSUugWAXdjz+V8J2sz/xNULsEdqRGUNX221uczS/Q1AinwYeEISNpCytfAeMR0sal3m4KBj9Xblnf/6DrMWTIevajX78gJpJmlKw6e1vU+PZI8qkKBiN8i7pqWGqbThbBQhgrCePnpWLznorhs87vJHp2oDku0PAj5knLBWfr0EGr6dDaevPC6zwNzyWPRSxtHQG67OMfJhlYHh2vouyPottYK6+w5yUyuaBR9hhrXOCppm7vl3HxeW8kw7CULj/OekEEdP7TmbzLZ83nkaQTA8FzVHnLEHltg9yfklTZ2dazEDLccAtt1zZwH7OFR7scPeZavmPmDRQ6d7l8Rli0YRGe+XBaZYlONO17NSiLJ5KTPfR3LTqq7yMliRaCwK9shVQtaxAIbsxwlbGSU9LC1w4rgkAXs3nMg+xIlK771ImnN6j43b3pCkNN1JjiQQGT+YU67mk+062BUsCK8ulkyO0GfroZGmT4zwP2Rbq5JJQtuJMYJ9s+DzVx1YuRpI/2Yg9U7A+eJTqTdp1Mt7faAbUFuABJ4zYshSgESVsf553plTc5L9n1rfwympwpvXqOHV5GViirgiaIKtDPgx2al5rZkDLH5FqcdYklAiIBZdlhn/+Zb/GUhM154VGsfKfPcIbd8+d5vpqJPh/cGNRVXoiaPXcsXE5k09ujYaa44qpngLnEyJB0cdtoGf1X3Jhq4MefaplCO6rZZu6xJZlVC2HP5BnAW+hBlOvv4NUlRMBq+wESOEXp6Cxa36P4tktY4AG7a8hWIO9EukHN63x6R6iDXrXU0NafW0IV9wdk3xTDlZbxsz1p1NP0kXUhjINPJTbWh6vV2Szd4Y3a/cfttvq1kYwWdqrmbTw022b8hhAFZOq9B012fEHf8zi/+yNxi5s10nXMHycl0G9x7IbFhXZSBocadkujCHvC3or0DQo/1xsJnfpxtmkPt64RPq08Oa5eh09Usrjmylsif+4jjdpFD8G3117HkyFEvBN2RLPe6NZ+fad4cfzIsqDdE1sly61DAffLHqWctG4QR3AriOdchPGH6EvPge09bi07vlGAE="}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":475,"cache_read_input_tokens":16864,"cache_creation":{"ephemeral_5m_input_tokens":475,"ephemeral_1h_input_tokens":0},"output_tokens":9,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"d8cd2100-3854-4199-aa43-ba7b0040ce3d"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01K2L94QYRbSwkr3DcEmKFkv","type":"message","role":"assistant","content":[{"type":"text","text":"The canvas size is wrong — the JS isn't executing due to `type=\"module\"` being blocked on `file://` URLs. Let me fix that:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":475,"cache_read_input_tokens":16864,"cache_creation":{"ephemeral_5m_input_tokens":475,"ephemeral_1h_input_tokens":0},"output_tokens":9,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"d823ecd2-e69b-4051-abbd-47ad14558eeb"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01K2L94QYRbSwkr3DcEmKFkv","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01FcKBouCboo8XUZkYabrfXd","name":"Edit","input":{"replace_all":false,"file_path":"/tmp/loop-bench-q82ugmwv/tsconfig.json","old_string":" \"module\": \"ES2020\",\n \"moduleResolution\": \"bundler\",","new_string":" \"module\": \"commonjs\","},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":475,"cache_read_input_tokens":16864,"cache_creation":{"ephemeral_5m_input_tokens":475,"ephemeral_1h_input_tokens":0},"output_tokens":9,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"4f3f93b2-5026-4d85-88f5-3c2e05633a87"} +{"type":"user","message":{"role":"user","content":[{"type":"tool_result","content":"<tool_use_error>File has not been read yet. Read it first before writing to it.</tool_use_error>","is_error":true,"tool_use_id":"toolu_01FcKBouCboo8XUZkYabrfXd"}]},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"94b6d2b3-ad6a-4939-86a7-ac688ccae171","timestamp":"2026-04-04T21:15:11.032Z","tool_use_result":"Error: File has not been read yet. Read it first before writing to it."} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01XpBPeDtLwCVP694iQHrEFE","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01Ktwb7Lp2ch7pHqT3dSFmpZ","name":"Read","input":{"file_path":"/tmp/loop-bench-q82ugmwv/tsconfig.json"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":573,"cache_read_input_tokens":17339,"cache_creation":{"ephemeral_5m_input_tokens":573,"ephemeral_1h_input_tokens":0},"output_tokens":51,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"6bb6c28e-11b9-453d-8f6d-9dd4fbb0c617"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01Ktwb7Lp2ch7pHqT3dSFmpZ","type":"tool_result","content":"1\t{\n2\t \"compilerOptions\": {\n3\t \"target\": \"ES2020\",\n4\t \"module\": \"ES2020\",\n5\t \"moduleResolution\": \"bundler\",\n6\t \"strict\": true,\n7\t \"outDir\": \"./dist\",\n8\t \"rootDir\": \"./src\",\n9\t \"lib\": [\"ES2020\", \"DOM\"]\n10\t },\n11\t \"include\": [\"src/**/*\"]\n12\t}\n13\t"}]},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"900eba16-7aea-42be-8321-c485e1fccb44","timestamp":"2026-04-04T21:15:15.999Z","tool_use_result":{"type":"text","file":{"filePath":"/tmp/loop-bench-q82ugmwv/tsconfig.json","content":"{\n \"compilerOptions\": {\n \"target\": \"ES2020\",\n \"module\": \"ES2020\",\n \"moduleResolution\": \"bundler\",\n \"strict\": true,\n \"outDir\": \"./dist\",\n \"rootDir\": \"./src\",\n \"lib\": [\"ES2020\", \"DOM\"]\n },\n \"include\": [\"src/**/*\"]\n}\n","numLines":13,"startLine":1,"totalLines":13}}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01NejM81LCGnptwHEtZH3NMf","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01HLZCgBr5XnSQnLPwNZTXy9","name":"Edit","input":{"replace_all":false,"file_path":"/tmp/loop-bench-q82ugmwv/tsconfig.json","old_string":" \"module\": \"ES2020\",\n \"moduleResolution\": \"bundler\",\n","new_string":" \"module\": \"commonjs\",\n"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":775,"cache_read_input_tokens":17339,"cache_creation":{"ephemeral_5m_input_tokens":775,"ephemeral_1h_input_tokens":0},"output_tokens":62,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"50a051dd-d412-47b0-96c6-0734f4fe120e"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01HLZCgBr5XnSQnLPwNZTXy9","type":"tool_result","content":"The file /tmp/loop-bench-q82ugmwv/tsconfig.json has been updated successfully."}]},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"42df0662-da1e-4baf-b3ca-a18b381f0a20","timestamp":"2026-04-04T21:15:18.301Z","tool_use_result":{"filePath":"/tmp/loop-bench-q82ugmwv/tsconfig.json","oldString":" \"module\": \"ES2020\",\n \"moduleResolution\": \"bundler\",\n","newString":" \"module\": \"commonjs\",\n","originalFile":"{\n \"compilerOptions\": {\n \"target\": \"ES2020\",\n \"module\": \"ES2020\",\n \"moduleResolution\": \"bundler\",\n \"strict\": true,\n \"outDir\": \"./dist\",\n \"rootDir\": \"./src\",\n \"lib\": [\"ES2020\", \"DOM\"]\n },\n \"include\": [\"src/**/*\"]\n}\n","structuredPatch":[{"oldStart":1,"oldLines":8,"newStart":1,"newLines":7,"lines":[" {"," \"compilerOptions\": {"," \"target\": \"ES2020\",","- \"module\": \"ES2020\",","- \"moduleResolution\": \"bundler\",","+ \"module\": \"commonjs\","," \"strict\": true,"," \"outDir\": \"./dist\","," \"rootDir\": \"./src\","]}],"userModified":false,"replaceAll":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01PzHdHFtrihdxUsxd4UQUEv","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_013aEfkynnq8ZGaSTftCZKqs","name":"Edit","input":{"replace_all":false,"file_path":"/tmp/loop-bench-q82ugmwv/index.html","old_string":" <script type=\"module\" src=\"dist/tetris.js\"></script>","new_string":" <script src=\"dist/tetris.js\"></script>"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":191,"cache_read_input_tokens":18114,"cache_creation":{"ephemeral_5m_input_tokens":191,"ephemeral_1h_input_tokens":0},"output_tokens":52,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"b0a403ce-cba0-466b-8636-cc71b50a6b91"} +{"type":"user","message":{"role":"user","content":[{"type":"tool_result","content":"<tool_use_error>File has not been read yet. Read it first before writing to it.</tool_use_error>","is_error":true,"tool_use_id":"toolu_013aEfkynnq8ZGaSTftCZKqs"}]},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"390544d6-9419-46df-9541-cde420bf2cd8","timestamp":"2026-04-04T21:15:21.165Z","tool_use_result":"Error: File has not been read yet. Read it first before writing to it."} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_016NvMenGjiJajoBUnPnUVKp","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01EqTZnqLNhVt3vfDVdv7kNN","name":"Read","input":{"file_path":"/tmp/loop-bench-q82ugmwv/index.html","limit":10,"offset":145},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":200,"cache_read_input_tokens":18305,"cache_creation":{"ephemeral_5m_input_tokens":200,"ephemeral_1h_input_tokens":0},"output_tokens":62,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"df403704-f48c-42dc-b926-237946941433"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01EqTZnqLNhVt3vfDVdv7kNN","type":"tool_result","content":"145\t <canvas id=\"board\"></canvas>\n146\t\n147\t <div id=\"pause-overlay\" class=\"overlay\">\n148\t <h2>Paused</h2>\n149\t <p>Press <kbd>P</kbd> or <kbd>Esc</kbd> to resume</p>\n150\t </div>\n151\t\n152\t <div id=\"gameover-overlay\" class=\"overlay\">\n153\t <h2>Game Over</h2>\n154\t <p class=\"final-score-label\">Score</p>"}]},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"207bbbf8-0060-4583-82b1-44cf1f7d937f","timestamp":"2026-04-04T21:15:24.130Z","tool_use_result":{"type":"text","file":{"filePath":"/tmp/loop-bench-q82ugmwv/index.html","content":" <canvas id=\"board\"></canvas>\n\n <div id=\"pause-overlay\" class=\"overlay\">\n <h2>Paused</h2>\n <p>Press <kbd>P</kbd> or <kbd>Esc</kbd> to resume</p>\n </div>\n\n <div id=\"gameover-overlay\" class=\"overlay\">\n <h2>Game Over</h2>\n <p class=\"final-score-label\">Score</p>","numLines":10,"startLine":145,"totalLines":204}}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01VzbfyrUFvqy7QUE5t7pRJm","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01AogRjUAi44uXyF6FBHXv9z","name":"Read","input":{"file_path":"/tmp/loop-bench-q82ugmwv/index.html","limit":10,"offset":205},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":248,"cache_read_input_tokens":18505,"cache_creation":{"ephemeral_5m_input_tokens":248,"ephemeral_1h_input_tokens":0},"output_tokens":61,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"9aa6613a-b607-4ee4-828b-99ee76803887"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01AogRjUAi44uXyF6FBHXv9z","type":"tool_result","content":"<system-reminder>Warning: the file exists but is shorter than the provided offset (205). The file has 204 lines.</system-reminder>"}]},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"473cec6d-2c7e-475f-bb2a-2f467730dc30","timestamp":"2026-04-04T21:15:27.408Z","tool_use_result":{"type":"text","file":{"filePath":"/tmp/loop-bench-q82ugmwv/index.html","content":"","numLines":0,"startLine":205,"totalLines":204}}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01FCwP51waCva7YXkrmT1hmz","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01NC2uxzrwJxZuGju8iCoiBT","name":"Read","input":{"file_path":"/tmp/loop-bench-q82ugmwv/index.html","limit":10,"offset":195},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":393,"cache_read_input_tokens":18505,"cache_creation":{"ephemeral_5m_input_tokens":393,"ephemeral_1h_input_tokens":0},"output_tokens":72,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"f6802c2e-d5df-448c-b093-32074e79d8f3"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01NC2uxzrwJxZuGju8iCoiBT","type":"tool_result","content":"195\t <div><kbd>P</kbd> Pause</div>\n196\t </div>\n197\t\n198\t </div>\n199\t </div>\n200\t\n201\t <script type=\"module\" src=\"dist/tetris.js\"></script>\n202\t</body>\n203\t</html>\n204\t"}]},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"b9519a76-a3ee-41d6-93b8-798bc99e3df3","timestamp":"2026-04-04T21:15:31.203Z","tool_use_result":{"type":"text","file":{"filePath":"/tmp/loop-bench-q82ugmwv/index.html","content":" <div><kbd>P</kbd> Pause</div>\n </div>\n\n </div>\n </div>\n\n <script type=\"module\" src=\"dist/tetris.js\"></script>\n</body>\n</html>\n","numLines":10,"startLine":195,"totalLines":204}}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01RczAAhg5rAQbBtP9gBC7QU","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01KjeiHQ4p4VCTjkvbsaz66D","name":"Edit","input":{"replace_all":false,"file_path":"/tmp/loop-bench-q82ugmwv/index.html","old_string":" <script type=\"module\" src=\"dist/tetris.js\"></script>","new_string":" <script src=\"dist/tetris.js\"></script>"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":195,"cache_read_input_tokens":18898,"cache_creation":{"ephemeral_5m_input_tokens":195,"ephemeral_1h_input_tokens":0},"output_tokens":64,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"89349a3f-b2d9-4867-b548-8852b03c7e8f"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01KjeiHQ4p4VCTjkvbsaz66D","type":"tool_result","content":"The file /tmp/loop-bench-q82ugmwv/index.html has been updated successfully."}]},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"60a11d52-6693-48dd-9636-7bbb42500772","timestamp":"2026-04-04T21:15:34.799Z","tool_use_result":{"filePath":"/tmp/loop-bench-q82ugmwv/index.html","oldString":" <script type=\"module\" src=\"dist/tetris.js\"></script>","newString":" <script src=\"dist/tetris.js\"></script>","originalFile":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Tetris</title>\n <style>\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\n body {\n background: #0a0a0f;\n color: #e0e0e0;\n font-family: \"Segoe UI\", system-ui, sans-serif;\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 100vh;\n gap: 0;\n }\n\n /* ── Layout wrapper ── */\n #game-wrapper {\n display: flex;\n align-items: flex-start;\n gap: 20px;\n }\n\n /* ── Board ── */\n #board-container {\n position: relative;\n }\n\n #board {\n display: block;\n border: 2px solid #333;\n border-radius: 4px;\n box-shadow: 0 0 40px rgba(0, 240, 240, 0.08);\n }\n\n /* ── Overlays ── */\n .overlay {\n display: none;\n position: absolute;\n inset: 0;\n background: rgba(0,0,0,0.82);\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 16px;\n border-radius: 4px;\n backdrop-filter: blur(4px);\n }\n\n .overlay h2 {\n font-size: 2rem;\n letter-spacing: 4px;\n text-transform: uppercase;\n }\n\n .overlay p {\n color: #aaa;\n font-size: 0.9rem;\n letter-spacing: 1px;\n }\n\n #gameover-overlay h2 { color: #f04040; }\n #pause-overlay h2 { color: #f0f000; }\n\n .final-score-label { color: #aaa; font-size: 0.85rem; letter-spacing: 2px; text-transform: uppercase; }\n #final-score { font-size: 2.5rem; font-weight: 700; color: #fff; }\n\n /* ── Side panel ── */\n #side-panel {\n display: flex;\n flex-direction: column;\n gap: 20px;\n width: 130px;\n }\n\n .panel-box {\n background: #141418;\n border: 1px solid #2a2a35;\n border-radius: 6px;\n padding: 12px 14px;\n }\n\n .panel-box .label {\n font-size: 0.68rem;\n letter-spacing: 2px;\n text-transform: uppercase;\n color: #666;\n margin-bottom: 4px;\n }\n\n .panel-box .value {\n font-size: 1.5rem;\n font-weight: 700;\n color: #fff;\n letter-spacing: 1px;\n }\n\n #preview-box .label { margin-bottom: 8px; }\n\n #preview {\n display: block;\n border-radius: 3px;\n }\n\n /* ── Controls help ── */\n #controls {\n background: #141418;\n border: 1px solid #2a2a35;\n border-radius: 6px;\n padding: 12px 14px;\n font-size: 0.7rem;\n line-height: 1.9;\n color: #888;\n }\n\n #controls .ctrl-title {\n font-size: 0.68rem;\n letter-spacing: 2px;\n text-transform: uppercase;\n color: #555;\n margin-bottom: 6px;\n }\n\n kbd {\n display: inline-block;\n background: #222;\n border: 1px solid #444;\n border-radius: 3px;\n padding: 0 4px;\n font-family: inherit;\n font-size: 0.68rem;\n color: #ccc;\n }\n </style>\n</head>\n<body>\n <div id=\"game-wrapper\">\n\n <!-- Board + overlays -->\n <div id=\"board-container\">\n <canvas id=\"board\"></canvas>\n\n <div id=\"pause-overlay\" class=\"overlay\">\n <h2>Paused</h2>\n <p>Press <kbd>P</kbd> or <kbd>Esc</kbd> to resume</p>\n </div>\n\n <div id=\"gameover-overlay\" class=\"overlay\">\n <h2>Game Over</h2>\n <p class=\"final-score-label\">Score</p>\n <div id=\"final-score\">0</div>\n <p>Press <kbd>Enter</kbd> or <kbd>Space</kbd> to restart</p>\n </div>\n </div>\n\n <!-- Side panel -->\n <div id=\"side-panel\">\n\n <div class=\"panel-box\" id=\"preview-box\">\n <div class=\"label\">Next</div>\n <canvas id=\"preview\"></canvas>\n </div>\n\n <div class=\"panel-box\">\n <div class=\"label\">Score</div>\n <div class=\"value\" id=\"score\">0</div>\n </div>\n\n <div class=\"panel-box\">\n <div class=\"label\">Lines</div>\n <div class=\"value\" id=\"lines\">0</div>\n </div>\n\n <div class=\"panel-box\">\n <div class=\"label\">Level</div>\n <div class=\"value\" id=\"level\">0</div>\n </div>\n\n <div class=\"panel-box\">\n <div class=\"label\">Best</div>\n <div class=\"value\" id=\"highscore\">0</div>\n </div>\n\n <div id=\"controls\">\n <div class=\"ctrl-title\">Controls</div>\n <div><kbd>←</kbd><kbd>→</kbd> Move</div>\n <div><kbd>↑</kbd> / <kbd>X</kbd> Rotate CW</div>\n <div><kbd>Z</kbd> Rotate CCW</div>\n <div><kbd>↓</kbd> Soft drop</div>\n <div><kbd>Space</kbd> Hard drop</div>\n <div><kbd>P</kbd> Pause</div>\n </div>\n\n </div>\n </div>\n\n <script type=\"module\" src=\"dist/tetris.js\"></script>\n</body>\n</html>\n","structuredPatch":[{"oldStart":198,"oldLines":6,"newStart":198,"newLines":6,"lines":[" </div>"," </div>"," ","- <script type=\"module\" src=\"dist/tetris.js\"></script>","+ <script src=\"dist/tetris.js\"></script>"," </body>"," </html>"]}],"userModified":false,"replaceAll":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01KTuQPGZ84KeiFfNFBaEQHz","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01QkMeiZS6mNJgG2ybVtPdGC","name":"Bash","input":{"command":"./node_modules/.bin/tsc --project tsconfig.json 2>&1 && head -5 dist/tetris.js","description":"Recompile with CommonJS module format"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":384,"cache_read_input_tokens":18898,"cache_creation":{"ephemeral_5m_input_tokens":384,"ephemeral_1h_input_tokens":0},"output_tokens":52,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"a89c6022-c381-4098-8d6f-081b15599a64"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01QkMeiZS6mNJgG2ybVtPdGC","type":"tool_result","content":"\"use strict\";\n// ============================================================\n// Tetris – complete implementation\n// ============================================================\n// ── Constants ────────────────────────────────────────────────","is_error":false}]},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"5960d9c1-cdb0-4a14-807c-3d46d204b33b","timestamp":"2026-04-04T21:15:39.858Z","tool_use_result":{"stdout":"\"use strict\";\n// ============================================================\n// Tetris – complete implementation\n// ============================================================\n// ── Constants ────────────────────────────────────────────────","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01KrJRL3nxLGTikyAc69msUH","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01S9J2PVcRKLKLg4tdgJ23tK","name":"Bash","input":{"command":"node -e \"\nconst { chromium } = require('./node_modules/@playwright/test');\n(async () => {\n const browser = await chromium.launch();\n const page = await browser.newPage();\n const errors = [];\n page.on('pageerror', e => errors.push(e.message));\n const file = require('path').resolve('./index.html');\n await page.goto('file://' + file);\n await page.waitForTimeout(1200);\n const sz = await page.evaluate(() => ({\n w: document.getElementById('board').width,\n h: document.getElementById('board').height\n }));\n console.log('Canvas size (expect 300x600):', sz);\n\n const hud = await page.evaluate(() => ({\n score: document.getElementById('score').textContent,\n lines: document.getElementById('lines').textContent,\n level: document.getElementById('level').textContent,\n }));\n console.log('HUD:', hud);\n\n // Hard drop should give points (2 per row)\n await page.keyboard.press('Space');\n await page.waitForTimeout(400);\n const s2 = await page.evaluate(() => document.getElementById('score').textContent);\n console.log('Score after hard drop:', s2, '(should be > 0)');\n\n // Pause test\n await page.keyboard.press('p');\n await page.waitForTimeout(100);\n const pauseVisible = await page.evaluate(() =>\n document.getElementById('pause-overlay').style.display\n );\n console.log('Pause overlay after P:', pauseVisible, '(expect flex)');\n\n await browser.close();\n if (errors.length) { console.error('Errors:', errors); process.exit(1); }\n if (sz.w !== 300 || sz.h !== 600) { console.error('Wrong canvas size!'); process.exit(1); }\n console.log('\\nPASS');\n})();\n\"","description":"Re-run smoke test after fixing module format","timeout":30000},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":158,"cache_read_input_tokens":19282,"cache_creation":{"ephemeral_5m_input_tokens":158,"ephemeral_1h_input_tokens":0},"output_tokens":42,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"17403f19-5b47-434c-abdc-03c79f4d63ef"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01S9J2PVcRKLKLg4tdgJ23tK","type":"tool_result","content":"Canvas size (expect 300x600): { w: 300, h: 600 }\nHUD: { score: '0', lines: '0', level: '0' }\nScore after hard drop: 34 (should be > 0)\nPause overlay after P: flex (expect flex)\n\nPASS","is_error":false}]},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"465e89a2-1b14-4939-a0b3-652ef84833d8","timestamp":"2026-04-04T21:15:51.309Z","tool_use_result":{"stdout":"Canvas size (expect 300x600): { w: 300, h: 600 }\nHUD: { score: '0', lines: '0', level: '0' }\nScore after hard drop: 34 (should be > 0)\nPause overlay after P: flex (expect flex)\n\nPASS","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01Xe6m9Yis9MNo2XkMHeTVmY","type":"message","role":"assistant","content":[{"type":"text","text":"All checks pass. Let me do a more thorough test — line clearing, level progression, and game-over detection:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":663,"cache_read_input_tokens":19440,"cache_creation":{"ephemeral_5m_input_tokens":663,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"a12f2261-361f-47ad-bd65-5a80f2652dee"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01Xe6m9Yis9MNo2XkMHeTVmY","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_017e8VYhHE6HfMPEuZJse8yE","name":"Bash","input":{"command":"node -e \"\nconst { chromium } = require('./node_modules/@playwright/test');\n(async () => {\n const browser = await chromium.launch();\n const page = await browser.newPage();\n const errors = [];\n page.on('pageerror', e => errors.push(e.message));\n await page.goto('file://' + require('path').resolve('./index.html'));\n await page.waitForTimeout(800);\n\n // Stack pieces until game over\n for (let i = 0; i < 80; i++) {\n await page.keyboard.press('Space');\n await page.waitForTimeout(30);\n }\n await page.waitForTimeout(500);\n\n const gameOverVisible = await page.evaluate(() =>\n document.getElementById('gameover-overlay').style.display\n );\n const finalScore = await page.evaluate(() =>\n document.getElementById('final-score').textContent\n );\n const highscore = await page.evaluate(() =>\n document.getElementById('highscore').textContent\n );\n console.log('Game Over overlay:', gameOverVisible, '(expect flex)');\n console.log('Final score:', finalScore);\n console.log('High score saved:', highscore);\n\n // Restart\n await page.keyboard.press('Enter');\n await page.waitForTimeout(400);\n const afterRestart = await page.evaluate(() => ({\n score: document.getElementById('score').textContent,\n goVisible: document.getElementById('gameover-overlay').style.display,\n }));\n console.log('After restart:', afterRestart);\n\n await browser.close();\n if (errors.length) { console.error('Errors:', errors); process.exit(1); }\n if (gameOverVisible !== 'flex') { console.error('Game over not triggered'); process.exit(1); }\n if (afterRestart.score !== '0') { console.error('Restart did not reset score'); process.exit(1); }\n if (afterRestart.goVisible === 'flex') { console.error('Game over overlay still visible after restart'); process.exit(1); }\n console.log('\\nPASS');\n})();\n\"","description":"Test game-over detection and restart","timeout":30000},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":663,"cache_read_input_tokens":19440,"cache_creation":{"ephemeral_5m_input_tokens":663,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"4772bd25-2fd4-433f-aa69-c8bff97ec191"} +{"type":"user","message":{"role":"user","content":[{"type":"tool_result","content":"Exit code 1\nGame Over overlay: none (expect flex)\nFinal score: 186\nHigh score saved: 222\nAfter restart: { score: '168', goVisible: 'none' }\nGame over not triggered","is_error":true,"tool_use_id":"toolu_017e8VYhHE6HfMPEuZJse8yE"}]},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"8a625461-9886-4565-b1e2-4bb2d97247ea","timestamp":"2026-04-04T21:16:04.871Z","tool_use_result":"Error: Exit code 1\nGame Over overlay: none (expect flex)\nFinal score: 186\nHigh score saved: 222\nAfter restart: { score: '168', goVisible: 'none' }\nGame over not triggered"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01RFg1HVdLQbU3oekZYUPArg","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"The game over isn't being triggered because when we hard drop 80 pieces, lines get cleared and the game doesn't fill up. Let me try a different approach - do more hard drops or try to fill it faster. Maybe I need to use fewer pieces so no lines get cleared, or use more drops.\n\nActually wait - the score shows 186 and high score shows 222. This means a previous test had a higher score. Let me think more carefully...\n\nActually the issue is that when we do hard drops with Space, the pieces fall and may or may not trigger game over depending on whether lines are cleared. The game board is 10 columns wide and each piece is small. With random pieces, many lines may get cleared.\n\n\nI'm considering just doing 200 hard drops instead to guarantee the board fills up, but looking at the output more carefully - the score after restart shows 168, which shouldn't happen if the restart actually worked. The game over overlay disappeared but the score didn't reset to 0, so either the restart didn't trigger properly or something's preserving the old state. The game loop must be running between my checks during that 500ms wait, causing additional auto-drops to occur. But the score progression doesn't make sense—the final score of 186 with a highscore of 222 suggests a previous run, yet after pressing Enter the score shows 168, which is lower than 186 and shouldn't be possible unless something else is resetting the game state. I see the issue now - `Enter` isn't bound to anything in the keydown handler, so it does nothing. The real puzzle is why the score drops from 186 to 168. Looking at the test flow more carefully: the final score of 186 only appears in the DOM if `endGame()` was actually called, which sets the final-score element. But if the game never ended, that element should still show \"0\", not 186. So either the game did end at some point, or there's something else setting that final-score value.\n\nI think what's happening is the game over triggered during the 80-drop loop, but then the space bar was pressed again after game over, which triggered `restart()` since space is also a restart key. That would reset the score and start a fresh game that accumulated to 186. By the time the test checks if the overlay is visible, it's already hidden because the game restarted. Let me trace through the exact sequence of events to confirm.\n\nActually, I see the real issue now—space acts as both a hard drop during gameplay and a restart button after game over. So pressing space 80 times causes the game to end partway through, then the remaining presses restart the game and continue dropping, which is why the final score is from a new game. The game logic itself is working correctly; I just need to adjust my test to use a different key like Enter and verify the game over state without triggering additional actions.","signature":"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"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":721,"cache_read_input_tokens":20103,"cache_creation":{"ephemeral_5m_input_tokens":721,"ephemeral_1h_input_tokens":0},"output_tokens":10,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"17db53f6-6df8-4370-86b2-5bd0eaa5c638"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01RFg1HVdLQbU3oekZYUPArg","type":"message","role":"assistant","content":[{"type":"text","text":"The game loop is fine — the test issue is that Space both drops and restarts. Let me write a better test:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":721,"cache_read_input_tokens":20103,"cache_creation":{"ephemeral_5m_input_tokens":721,"ephemeral_1h_input_tokens":0},"output_tokens":10,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"ac03b164-b6ca-4ad7-9e96-d817ed739f34"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01RFg1HVdLQbU3oekZYUPArg","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_017FGwVxWGa7QBv3Aa4QtmJk","name":"Bash","input":{"command":"node -e \"\nconst { chromium } = require('./node_modules/@playwright/test');\n(async () => {\n const browser = await chromium.launch();\n const page = await browser.newPage();\n const errors = [];\n page.on('pageerror', e => errors.push(e.message));\n await page.goto('file://' + require('path').resolve('./index.html'));\n await page.waitForTimeout(800);\n\n // Fill the board via internal state to force game over\n const triggered = await page.evaluate(() => {\n // Directly fill the board, then drop a piece that can't fit\n // We'll fill rows 0-19, columns 0-9 except leaving column 0 of row 0 empty\n // Actually just fill the top row to trigger game over check on next lock\n return new Promise(resolve => {\n // Override the grid externally: we can't easily since it's in a class closure.\n // Instead use keyboard: fill board by holding arrow keys then dropping\n resolve('cannot easily inspect internal state');\n });\n });\n\n // Alternative: just hard-drop 100 times WITHOUT pressing enter/space on game over\n // Use different approach: press ArrowDown repeats + small delays\n // Actually let's just trust the core game logic and test the observable behavior\n\n // Stack pieces high by moving to a fixed column and dropping\n for (let i = 0; i < 100; i++) {\n await page.keyboard.press('ArrowLeft'); // push to left wall\n await page.keyboard.press('ArrowLeft');\n await page.keyboard.press('ArrowLeft');\n await page.keyboard.press('ArrowLeft');\n await page.keyboard.press('ArrowLeft');\n // hard drop via ArrowDown held (soft drop to lock faster) - don't use Space\n for (let j = 0; j < 25; j++) await page.keyboard.press('ArrowDown');\n await page.waitForTimeout(50);\n // check if game over\n const go = await page.evaluate(() =>\n document.getElementById('gameover-overlay').style.display);\n if (go === 'flex') {\n console.log('Game over detected after', i+1, 'pieces');\n break;\n }\n }\n\n const gameOverVisible = await page.evaluate(() =>\n document.getElementById('gameover-overlay').style.display\n );\n console.log('Game Over overlay:', gameOverVisible);\n\n // Restart with Enter\n await page.keyboard.press('Enter');\n await page.waitForTimeout(300);\n const afterRestart = await page.evaluate(() => ({\n score: document.getElementById('score').textContent,\n goVisible: document.getElementById('gameover-overlay').style.display,\n }));\n console.log('After restart:', afterRestart);\n\n // Check line clearing: need to fill a complete row\n // Use scoring: clear lines by constructing a specific scenario\n // Just verify score increases with soft drop\n await page.keyboard.press('ArrowDown');\n await page.waitForTimeout(50);\n const scoreAfterDrop = await page.evaluate(() => document.getElementById('score').textContent);\n console.log('Score after soft-drop step:', scoreAfterDrop);\n\n await browser.close();\n if (errors.length) { console.error('JS Errors:', errors); process.exit(1); }\n if (gameOverVisible !== 'flex') { console.error('FAIL: game over never triggered'); process.exit(1); }\n if (afterRestart.score !== '0') { console.error('FAIL: restart did not reset score to 0'); process.exit(1); }\n if (afterRestart.goVisible === 'flex') { console.error('FAIL: game over overlay still visible after restart'); process.exit(1); }\n console.log('\\nPASS - all game mechanics verified');\n})();\n\"","description":"Test game over and restart with ArrowDown soft drops instead of Space","timeout":60000},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":721,"cache_read_input_tokens":20103,"cache_creation":{"ephemeral_5m_input_tokens":721,"ephemeral_1h_input_tokens":0},"output_tokens":10,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"15b6ac26-3d73-4068-8f90-f653f17ff70a"} +{"type":"user","message":{"role":"user","content":[{"type":"tool_result","content":"Exit code 1\nGame Over overlay: \nAfter restart: { score: '190', goVisible: '' }\nScore after soft-drop step: 190\nFAIL: game over never triggered","is_error":true,"tool_use_id":"toolu_017FGwVxWGa7QBv3Aa4QtmJk"}]},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"c4706d22-de16-4506-8f0d-9df4c60ace07","timestamp":"2026-04-04T21:17:00.683Z","tool_use_result":"Error: Exit code 1\nGame Over overlay: \nAfter restart: { score: '190', goVisible: '' }\nScore after soft-drop step: 190\nFAIL: game over never triggered"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01UqnvPVcmZCRuqo6pFunbuZ","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"The game over overlay has display:'' (empty string, not 'flex'). This means the style is not set because the overlay hasn't been shown yet. \n\nThe problem is that ArrowDown soft-drops aren't enough to lock pieces - soft drop just moves the piece down one step per key press. I need to hold ArrowDown or press it many more times. Alternatively, use hard drop (Space) but be careful about the restart case.\n\nLet me think about this differently. The pieces lock when they can't move down. With soft drop (ArrowDown), each press moves the piece down by 1. \n\nI'm pressing ArrowDown 25 times which should move the piece down ~20 rows to the bottom, but soft drops only trigger move(0, 1) - they don't directly lock the piece. Locking happens automatically when the drop timer fires and the piece can't move down further. The issue is that with `softDropping = true`, the interval gets reduced to speed up the drop, but I need to make sure the timer actually fires when the piece reaches the bottom. The issue is that `move(0, 1)` doesn't lock the piece when it can't move further—locking only happens in the game loop when the timer fires. Since `softDropping` is false between button presses, the auto-drop interval is the full 800ms, but my test only waits 50ms between ArrowDown presses, so the piece never gets a chance to lock before the next move attempt.\n\nI should switch to using Space for hard drops instead, which locks immediately. After each hard drop, I'll check if the game-over overlay appeared—if it has, I'll stop pressing Space and use Enter to restart instead.","signature":"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"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":2847,"cache_read_input_tokens":20824,"cache_creation":{"ephemeral_5m_input_tokens":2847,"ephemeral_1h_input_tokens":0},"output_tokens":8,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"3c698241-ef40-4936-87e7-00bf19a5a68c"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01UqnvPVcmZCRuqo6pFunbuZ","type":"message","role":"assistant","content":[{"type":"text","text":"The game loop needs the timer to lock pieces after soft drops. Let me use hard drop (Space) but properly detect game over before pressing Space again:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":2847,"cache_read_input_tokens":20824,"cache_creation":{"ephemeral_5m_input_tokens":2847,"ephemeral_1h_input_tokens":0},"output_tokens":8,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"170cb2cf-3c84-4343-b89e-dd20932a1e48"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01UqnvPVcmZCRuqo6pFunbuZ","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01Joh6m2UcCNguuo7DBbZRDq","name":"Bash","input":{"command":"node -e \"\nconst { chromium } = require('./node_modules/@playwright/test');\n(async () => {\n const browser = await chromium.launch();\n const page = await browser.newPage();\n const errors = [];\n page.on('pageerror', e => errors.push(e.message));\n await page.goto('file://' + require('path').resolve('./index.html'));\n await page.waitForTimeout(500);\n\n let gameOverDetected = false;\n\n // Hard-drop pieces; check for game over before each press\n for (let i = 0; i < 120; i++) {\n const goVisible = await page.evaluate(() =>\n document.getElementById('gameover-overlay').style.display);\n if (goVisible === 'flex') {\n console.log('Game over detected after', i, 'pieces');\n gameOverDetected = true;\n break;\n }\n // Move to column 0, then hard drop\n for (let k = 0; k < 5; k++) await page.keyboard.press('ArrowLeft');\n await page.keyboard.press('Space');\n await page.waitForTimeout(80);\n }\n\n const gameOverVisible = await page.evaluate(() =>\n document.getElementById('gameover-overlay').style.display);\n const finalScore = await page.evaluate(() => document.getElementById('final-score').textContent);\n console.log('Game Over overlay:', gameOverVisible, '| Final score:', finalScore);\n\n // Restart with Enter\n await page.keyboard.press('Enter');\n await page.waitForTimeout(300);\n const afterRestart = await page.evaluate(() => ({\n score: document.getElementById('score').textContent,\n lines: document.getElementById('lines').textContent,\n goVisible: document.getElementById('gameover-overlay').style.display,\n }));\n console.log('After restart:', afterRestart);\n\n // Verify rotation works: rotate current piece\n const beforeRot = await page.evaluate(() => document.getElementById('score').textContent);\n await page.keyboard.press('ArrowUp');\n await page.keyboard.press('ArrowUp');\n const afterRot = await page.evaluate(() => document.getElementById('score').textContent);\n console.log('Rotation test: score unchanged:', beforeRot === afterRot);\n\n // Verify scoring increases with hard drop\n await page.keyboard.press('Space');\n await page.waitForTimeout(200);\n const scoreAfterDrop = await page.evaluate(() => document.getElementById('score').textContent);\n console.log('Score after first hard drop in new game:', scoreAfterDrop, '(expect > 0)');\n\n await browser.close();\n if (errors.length) { console.error('JS Errors:', errors); process.exit(1); }\n if (!gameOverDetected) { console.error('FAIL: game over never triggered'); process.exit(1); }\n if (afterRestart.score !== '0') { console.error('FAIL: restart did not reset score'); process.exit(1); }\n if (afterRestart.lines !== '0') { console.error('FAIL: restart did not reset lines'); process.exit(1); }\n if (afterRestart.goVisible === 'flex') { console.error('FAIL: game over overlay still visible'); process.exit(1); }\n if (parseInt(scoreAfterDrop) <= 0) { console.error('FAIL: hard drop did not give points'); process.exit(1); }\n console.log('\\nALL TESTS PASSED');\n})();\n\"","description":"Comprehensive game mechanics test - game over, restart, rotation, scoring","timeout":60000},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":2847,"cache_read_input_tokens":20824,"cache_creation":{"ephemeral_5m_input_tokens":2847,"ephemeral_1h_input_tokens":0},"output_tokens":8,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"cd8f95f8-e551-4933-8ffb-d9153bd41200"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01Joh6m2UcCNguuo7DBbZRDq","type":"tool_result","content":"Game over detected after 15 pieces\nGame Over overlay: flex | Final score: 304\nAfter restart: { score: '0', lines: '0', goVisible: 'none' }\nRotation test: score unchanged: true\nScore after first hard drop in new game: 36 (expect > 0)\n\nALL TESTS PASSED","is_error":false}]},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"5fbd5fc7-e66f-4321-959b-460ab2e3c503","timestamp":"2026-04-04T21:17:30.384Z","tool_use_result":{"stdout":"Game over detected after 15 pieces\nGame Over overlay: flex | Final score: 304\nAfter restart: { score: '0', lines: '0', goVisible: 'none' }\nRotation test: score unchanged: true\nScore after first hard drop in new game: 36 (expect > 0)\n\nALL TESTS PASSED","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01DNYZ8EjojHwDGBq1gjrSY3","type":"message","role":"assistant","content":[{"type":"text","text":"All mechanics verified. Let me also do a quick test of line-clearing scoring:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":1966,"cache_read_input_tokens":23671,"cache_creation":{"ephemeral_5m_input_tokens":1966,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"785df422-d6d2-4506-9806-c920da9beacf","uuid":"56a1d09c-d7a2-40dc-b45e-aa30329a2754"} diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/claude_output.json b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/claude_output.json @@ -0,0 +1 @@ +{"type":"result","subtype":"success","is_error":false,"duration_ms":820517,"duration_api_ms":816433,"num_turns":29,"result":"Everything is in order. Here's a complete summary of everything that was built:\n\n---\n\n## Tetris — complete ✅\n\n### Files created\n\n| File | Purpose |\n|---|---|\n| `index.html` | Game page — layout, dark theme CSS, loads compiled JS |\n| `src/tetris.ts` | Full TypeScript source (~673 lines) |\n| `dist/tetris.js` | Compiled, ready-to-run JavaScript |\n| `tsconfig.json` | TypeScript compiler config |\n\n### To play\nOpen `index.html` in any modern browser (or serve from a local web server).\n\nTo rebuild after editing the source:\n```bash\nnpm run build\n```\n\n---\n\n### Features implemented\n\n**Core mechanics**\n- All 7 tetrominoes (I, O, T, S, Z, J, L) with authentic colours\n- **7-bag randomiser** — guarantees a fair distribution; no long droughts\n- **CW & CCW rotation** with wall-kick offsets (tries ±1, ±2 nudges so pieces don't get stuck at walls)\n- **Lock delay** (500 ms) — piece doesn't instantly lock when it touches the floor, giving you time to slide or rotate\n- **Ghost piece** — faint outline shows exactly where the piece will land\n\n**Gameplay**\n- **Soft drop** (↓) — 20× gravity, +1 point per cell\n- **Hard drop** (Space) — instantly slams the piece down, +2 points per cell\n- **Hold** (C / Shift) — swap the active piece to a hold slot; can only use once per piece\n- **Line clearing** with a white-flash animation before rows vanish\n- **Scoring** — 100 / 300 / 500 / 800 × level for 1 / 2 / 3 / 4 lines (standard Guideline values)\n- **Levelling** — every 10 lines cleared advances the level\n- **Speed curve** — 13-step drop interval table (800 ms → 50 ms) matching standard Tetris feel\n- **High score** persisted in `localStorage`\n\n**Controls**\n| Key | Action |\n|---|---|\n| `←` `→` | Move (with DAS + ARR auto-repeat) |\n| `↑` or `X` | Rotate clockwise |\n| `Z` | Rotate counter-clockwise |\n| `↓` | Soft drop |\n| `Space` | Hard drop |\n| `C` / `Shift` | Hold piece |\n| `P` | Pause / resume |\n| `R` / `Enter` | Restart (on game over) |","stop_reason":"end_turn","session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","total_cost_usd":1.78234365,"usage":{"input_tokens":34,"cache_creation_input_tokens":108669,"cache_read_input_tokens":1477443,"output_tokens":62100,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":108669},"inference_geo":"","iterations":[],"speed":"standard"},"modelUsage":{"claude-sonnet-4-6":{"inputTokens":34,"outputTokens":62100,"cacheReadInputTokens":1477443,"cacheCreationInputTokens":108669,"webSearchRequests":0,"costUSD":1.78234365,"contextWindow":200000,"maxOutputTokens":32000}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","uuid":"ca656ed4-b951-43ce-862a-260ed6d3ed39"} +\ No newline at end of file diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/claude_stderr.log b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/claude_stderr.log diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/eval_results.json b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/eval_results.json @@ -0,0 +1,263 @@ +{ + "structural": { + "pass": true, + "checks": [ + { + "name": "entry_point_exists", + "pass": true, + "detail": "index.html found" + }, + { + "name": "package_json_exists", + "pass": true, + "detail": "package.json found" + }, + { + "name": "build_succeeds", + "pass": true, + "detail": "npm run build completed successfully" + }, + { + "name": "typescript_compiles", + "pass": true, + "detail": "tsc --noEmit passed" + } + ], + "score": 1.0 + }, + "functional": { + "pass": false, + "error": "playwright eval not yet wired", + "score": 0 + }, + "quality": { + "lint": { + "pass": true, + "errors": 0, + "warnings": 0 + }, + "typecheck": { + "pass": true + }, + "performance": { + "bundle_size_bytes": 23112, + "size_under_512kb": true + }, + "score": 0.67 + }, + "code_analysis": { + "files": { + "total": 6, + "code": 3, + "docs": 0, + "unnecessary": 0, + "unnecessary_list": [] + }, + "lines_of_code": 1448, + "dependencies": { + "production": 0, + "dev": 5, + "total": 5 + }, + "complexity": "moderate", + "console_logs": 0, + "magic_numbers": { + "count": 78, + "excessive": true + }, + "function_length": { + "count": 75, + "average": 7.3, + "max": 32, + "long_functions": 0 + }, + "max_nesting_depth": 18, + "global_declarations": 22, + "naming": { + "dominant_style": "camelCase", + "consistency_pct": 100.0, + "camel_case": 528, + "snake_case": 0 + }, + "error_handling": { + "try_catch_blocks": 0, + "has_error_handling": false + }, + "comments": { + "comment_lines": 95, + "source_lines": 1062, + "ratio_pct": 8.9 + }, + "separation_of_concerns": { + "verdict": "mixed", + "files_with_rendering": 2, + "files_with_logic": 2, + "files_with_both": 2 + }, + "html_validation": { + "valid": false, + "errors": 3 + }, + "duplication_percentage": 0.0, + "score": 0.85 + }, + "transcript_analysis": { + "total_events": 74, + "tool_calls": { + "total": 28, + "bash": 13, + "write": 0, + "edit": 5, + "read": 10 + }, + "wasted_turns": { + "total": 1, + "docs": 0, + "ascii_art": 1, + "server_starts": 0 + }, + "errors_encountered": 0, + "thinking_blocks": 4, + "text_blocks": 10, + "productivity_ratio": 0.96, + "self_tested": false, + "score": 0.95 + }, + "gameplay_bot": { + "pass": false, + "score": 0.63, + "total": 16, + "passed": 10, + "failed": 6, + "report": { + "implementation": { + "renderer": "canvas", + "grid_detected": true, + "grid_bounds": { + "x": 0, + "y": 0, + "width": 64, + "height": 128 + }, + "controls": { + "left": "ArrowLeft", + "right": "ArrowRight", + "down": "ArrowDown", + "rotate": "x", + "drop": "Space" + }, + "start_mechanism": "auto", + "score_element_found": true + }, + "tests": [ + { + "name": "game_loads", + "pass": true, + "detail": "no console errors" + }, + { + "name": "game_starts", + "pass": true, + "detail": "started via auto" + }, + { + "name": "auto_drop", + "pass": true, + "detail": "pixels changed after 5s with no input" + }, + { + "name": "move_left", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "move_right", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "move_down", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "rotate", + "pass": true, + "detail": "piece shape changed after rotate key" + }, + { + "name": "all_pieces_rotate", + "pass": false, + "detail": "could not detect any piece rotations" + }, + { + "name": "hard_drop", + "pass": false, + "detail": "no change detected after hard drop key" + }, + { + "name": "piece_locks", + "pass": false, + "detail": "could not verify piece locking at bottom" + }, + { + "name": "new_piece_spawns", + "pass": false, + "detail": "could not detect new piece at top" + }, + { + "name": "multiple_pieces", + "pass": false, + "detail": "grid did not accumulate filled cells" + }, + { + "name": "line_clear", + "pass": true, + "detail": "line cleared via strategic placement" + }, + { + "name": "score_changes", + "pass": false, + "detail": "score did not increase: [1] -> no change after polling" + }, + { + "name": "game_over", + "pass": true, + "detail": "game stopped after stacking to top" + }, + { + "name": "playable_30s", + "pass": true, + "detail": "played for 30s, placed 79 pieces, no crashes" + } + ], + "summary": { + "total": 16, + "passed": 10, + "failed": 6, + "score": 0.63 + }, + "gameplay": { + "pieces_placed": 189, + "lines_cleared": 1, + "max_score_observed": 1, + "play_duration_seconds": 30, + "errors_during_play": 0 + }, + "performance": { + "load_time_ms": 29 + }, + "accessibility": { + "issues": [ + "no headings found", + "canvas without aria-label or role", + "canvas without aria-label or role", + "canvas without aria-label or role" + ], + "issue_count": 4, + "pass": false + } + } + }, + "score": 0.6995 +} +\ No newline at end of file diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/gameplay-bot-report.json b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/gameplay-bot-report.json @@ -0,0 +1,129 @@ +{ + "implementation": { + "renderer": "canvas", + "grid_detected": true, + "grid_bounds": { + "x": 0, + "y": 0, + "width": 64, + "height": 128 + }, + "controls": { + "left": "ArrowLeft", + "right": "ArrowRight", + "down": "ArrowDown", + "rotate": "x", + "drop": "Space" + }, + "start_mechanism": "auto", + "score_element_found": true + }, + "tests": [ + { + "name": "game_loads", + "pass": true, + "detail": "no console errors" + }, + { + "name": "game_starts", + "pass": true, + "detail": "started via auto" + }, + { + "name": "auto_drop", + "pass": true, + "detail": "pixels changed after 5s with no input" + }, + { + "name": "move_left", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "move_right", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "move_down", + "pass": true, + "detail": "grid state changed after key press" + }, + { + "name": "rotate", + "pass": true, + "detail": "piece shape changed after rotate key" + }, + { + "name": "all_pieces_rotate", + "pass": false, + "detail": "could not detect any piece rotations" + }, + { + "name": "hard_drop", + "pass": false, + "detail": "no change detected after hard drop key" + }, + { + "name": "piece_locks", + "pass": false, + "detail": "could not verify piece locking at bottom" + }, + { + "name": "new_piece_spawns", + "pass": false, + "detail": "could not detect new piece at top" + }, + { + "name": "multiple_pieces", + "pass": false, + "detail": "grid did not accumulate filled cells" + }, + { + "name": "line_clear", + "pass": true, + "detail": "line cleared via strategic placement" + }, + { + "name": "score_changes", + "pass": false, + "detail": "score did not increase: [1] -> no change after polling" + }, + { + "name": "game_over", + "pass": true, + "detail": "game stopped after stacking to top" + }, + { + "name": "playable_30s", + "pass": true, + "detail": "played for 30s, placed 79 pieces, no crashes" + } + ], + "summary": { + "total": 16, + "passed": 10, + "failed": 6, + "score": 0.63 + }, + "gameplay": { + "pieces_placed": 189, + "lines_cleared": 1, + "max_score_observed": 1, + "play_duration_seconds": 30, + "errors_during_play": 0 + }, + "performance": { + "load_time_ms": 29 + }, + "accessibility": { + "issues": [ + "no headings found", + "canvas without aria-label or role", + "canvas without aria-label or role", + "canvas without aria-label or role" + ], + "issue_count": 4, + "pass": false + } +} +\ No newline at end of file diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/meta.json b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/meta.json @@ -0,0 +1,30 @@ +{ + "model": "sonnet", + "effort": "high", + "prompt_style": "simple", + "language": "typescript", + "human_language": "en", + "tool_read": "on", + "tool_write": "on", + "tool_edit": "on", + "tool_glob": "on", + "tool_grep": "on", + "linter": "on", + "playwright": "on", + "context_file": "none", + "sub_agents": "on", + "web_search": "on", + "max_budget": "low", + "task": "tetris", + "cell_id": "tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on", + "runs_per_cell": 3, + "timeout_seconds": 1200, + "max_budget_usd": 2.0, + "run_id": "tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3", + "run_number": 3, + "claude_version": "2.1.92 (Claude Code)", + "started_at": "2026-04-04T20:57:28.503641+00:00", + "wall_time_seconds": 821, + "exit_code": 0, + "completed_at": "2026-04-04T21:11:17.256644+00:00" +} +\ No newline at end of file diff --git a/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/transcript.jsonl b/results/runs/tetris_context_file=none_effort=high_human_language=en_language=typescript_linter=on_max_budget=low_model=sonnet_playwright=on_prompt_style=simple_sub_agents=on_tool_edit=on_tool_glob=on_tool_grep=on_tool_read=on_tool_write=on_web_search=on_run3/transcript.jsonl @@ -0,0 +1,74 @@ +{"type": "harness", "subtype": "config", "model": "sonnet", "effort": "high", "tools": ["Bash", "Read", "Write", "Edit", "Glob", "Grep", "Agent", "WebSearch", "WebFetch"], "max_budget_usd": 2.0, "timeout_seconds": 1200, "task": "tetris", "language": "typescript", "prompt_style": "simple"} +{"type": "user", "subtype": "prompt", "message": {"role": "user", "content": "Build a playable Tetris game that runs in a browser. It should have all the standard mechanics: piece rotation, line clearing, scoring, and increasing speed. Use keyboard controls for movement and rotation.\n\n\nUse TypeScript."}} +{"type":"system","subtype":"init","cwd":"/tmp/loop-bench-iuvnueix","session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","tools":["Bash","Edit","Read"],"mcp_servers":[],"model":"claude-sonnet-4-6","permissionMode":"dontAsk","slash_commands":["update-config","debug","simplify","batch","loop","schedule","claude-api","compact","context","cost","heapdump","init","review","security-review","insights"],"apiKeySource":"apiKeyHelper","claude_code_version":"2.1.92","output_style":"default","agents":["general-purpose","statusline-setup","Explore","Plan"],"skills":["update-config","debug","simplify","batch","loop","schedule","claude-api"],"plugins":[{"name":"claude-hud","path":"/root/.claude/plugins/marketplaces/claude-hud/","source":"claude-hud@claude-hud"},{"name":"rust-analyzer-lsp","path":"/root/.claude/plugins/marketplaces/claude-plugins-official/plugins/rust-analyzer-lsp","source":"rust-analyzer-lsp@claude-plugins-official"}],"uuid":"53423ad8-8192-4a85-aa7c-480ae153e90e","fast_mode_state":"off"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01E7WbXPcfRazEfjxr7WiCdP","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"The user wants a playable Tetris game that runs in a browser using TypeScript. Let me build a complete Tetris game with all standard mechanics:\n\n1. All 7 Tetris pieces (I, O, T, S, Z, J, L)\n2. Piece rotation\n3. Line clearing\n4. Scoring system\n5. Increasing speed\n6. Keyboard controls (arrow keys for movement, up arrow or Z for rotation)\n\nI'll create:\n- `index.html` - The main HTML file\n\n\n- `src/game.ts` - The TypeScript source with all game logic\n- `package.json` and `tsconfig.json` for the build setup\n\nI'm designing the game with a 10×20 board and implementing all seven tetrominoes with their standard colors, then I'll set up the piece rotation mechanics, line clearing detection, and scoring system.\n\nWriting the TypeScript code... Defining the tetromino piece rotations... Finishing up the tetromino rotation definitions for the L piece, then I need to structure the full game properly. I'm thinking through the best way to organize this—I could write it as TypeScript with a build process, or keep it simpler and more direct. Let me set up the core files: an HTML canvas to render on, the game logic in TypeScript, and the necessary configuration files to get it running. Writing HTML and CSS styles... Still writing styles...\n\nNow I'm setting up the game overlay with the title and instructions, then building out the main game interface with the canvas and sidebar panels for tracking score, level, and lines cleared. Writing the Tetris game logic... Defining the piece shapes... Defining the rotation states and wall kick offsets for the Tetris pieces... Defining the game constants and core classes... Setting up the canvas contexts and UI elements, then attaching event listeners for game controls and initializing the game board when the start button is clicked. I'm selecting a random piece type and initializing it with starting position and rotation, then spawning it by centering it on the board and checking if the placement is valid—if not, the game ends immediately.\n\nNow I'm implementing the collision detection logic that checks whether a piece can occupy a given position, accounting for board boundaries and existing blocks, with support for testing rotations and movements without modifying the actual piece state.\n\nLocking pieces to the board... Still writing rotation logic... Writing game input handler... Writing game loop logic... Writing the block rendering... Writing the rendering logic... Still writing the render method...","signature":"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"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":4727,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":4727,"ephemeral_1h_input_tokens":0},"output_tokens":0,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"3990e45b-4641-4fa9-b938-71bfe2c4ec6d"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01JeLSynRSkDLv7hRes1oFG6","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"The user wants a playable Tetris game in the browser using TypeScript. Let me build a complete Tetris game with:\n\n1. All standard Tetris pieces (I, O, T, S, Z, J, L)\n2. Piece rotation\n3. Line clearing\n4. Scoring system\n5. Increasing speed\n6. Keyboard controls (arrow keys for movement, up arrow or Z for rotation)\n\nI'll create a project structure with:\n- `index.html` - the main HTML file\n- `src/tetris.ts` - the TypeScript game logic\n\n\n- `package.json` and `tsconfig.json` for TypeScript compilation\n\nI'm thinking about the best approach here—I could use a build system, but a simpler solution would be to compile TypeScript to JavaScript with `tsc` and load it in the HTML file. Now I'm planning out the core game structure: a 10x20 board with 30px cells, and I need to define all seven tetromino shapes with their rotation states.\n\nFor controls, I'll use arrow keys for movement and rotation, space for hard drop, and P to pause. The scoring system scales with level—clearing four lines gives the biggest bonus—and the level increases every 10 lines cleared, which also speeds up the game.\n\nSetting up the project structure... Defining the tetromino shapes... Writing the tetromino utility functions... Writing the game logic... Setting up the game class... Setting up the keyboard controls...\n\nNow I'm handling the piece movement and rotation logic, checking if moves are valid before applying them, and implementing both clockwise and counterclockwise rotation options. Finishing the lock piece logic...\n\nAfter placing the piece on the board, I'm checking for completed lines and updating the score and level accordingly. Then I'm spawning the next piece and checking if the game is over by seeing if the new piece can fit in its starting position. Finally, I'm refreshing the display with all the updated game state.\n\nNow I'm moving on to rendering individual cells on the canvas with a filled rectangle and a subtle white highlight on the top edge to give them dimension.\n\nStill writing rendering logic... Drawing the game board and pieces... Writing the rendering logic... Writing the game loop and initialization methods...\n\nNow I'm setting up the HTML structure with styling for the game board and UI elements, using a dark theme with proper layout and typography. Still writing styles... Writing the HTML structure...\n\nNow I'm thinking through the project setup more carefully—I want to keep this straightforward to run. I should create a `package.json` with TypeScript as a dev dependency, set up a `tsconfig.json`, and organize the source files, then compile with `tsc`. I'm considering whether to add a dev server or keep it simple, maybe using Vite for a better experience since it handles TypeScript compilation automatically.\n\nI'm settling on a straightforward approach: TypeScript source in `src/`, compiled output to `dist/`, and a simple HTML file that loads the compiled JavaScript. For the visual design, I want a dark grid background, colorful pieces with 3D shading, a clean sidebar, smooth animations, and a ghost piece preview. I'll also need to handle keyboard controls properly so arrow keys don't scroll the page.\n\nWriting the complete project structure now...\n\nActually, I should add some polish to make this feel like a proper Tetris game. I'm thinking about wall kicks for rotation (simplified version rather than full SRS), a ghost piece to show landing positions, lock delay before pieces settle, level progression from clearing lines, proper Tetris scoring, and line clear animations. Let me implement these features while keeping the code manageable.\n\nI also need to handle the game loop carefully to account for tab switching and timing issues. Writing the TypeScript implementation now with a solid structure using constants for the board dimensions and piece definitions, type definitions for the game state, and a main Tetris class that handles all the game logic.\n\nWriting the full implementation... Defining the tetromino shapes and types... Writing game logic... Writing the game class... Setting up the canvas contexts and UI elements, then initializing the game state by loading the high score from storage, configuring keyboard controls, and starting the game loop.\n\nNow I'm initializing the board with a fresh piece queue and resetting all game variables like score, level, and timers. Then I'm setting up the keyboard handler with delayed auto-shift and auto-repeat logic to make piece movement feel responsive.\n\nSetting up key bindings... Still writing game logic... Writing the rendering logic... Finishing the border styling and then moving into the render method where I'm setting up the canvas background with a dark color and drawing grid lines across the entire viewport. Still writing render logic... Writing the text rendering method... Finishing up the game loop and initialization, then considering polish features like line-clear animations and piece randomization, but deciding to keep it straightforward for now.\n\nNow I'm working through the rotation logic for the pieces—specifically how to rotate them clockwise by transposing and reversing rows, and I'm verifying it works correctly with the J-piece as a test case. Hmm, that doesn't look right. Let me reconsider.\n\nFor clockwise rotation, the formula is New[i][j] = Old[N-1-j][i] where N is the matrix size. Let me trace through a J piece to verify this works correctly—mapping each position in the rotated matrix back to the original, I can see the piece transforms into a vertical bar with a top-right extension, which is exactly what I'd expect.\n\nNow I'm checking my rotation function...\n\nVerifying the rotation logic... Now I'm exploring a counterclockwise rotation approach—I could apply CW three times, or use transpose with column reversal, or reverse rows then transpose. Let me try extracting each column as a new row and then reversing the row order to see if that gives me the counterclockwise effect.\n\nTesting this with the J piece: extracting columns gives me the intermediate rows, and after reversing them I get the correct CCW rotation. Now let me verify the CW function works the same way...\n\nVerifying the CW rotation... Now I'm verifying the CCW rotation by checking it against the expected J-piece orientation—extracting columns and reversing them gives me the correct counterclockwise result.\n\nFor the O piece, I realize using a 3x4 matrix causes rotation issues, so I should standardize on 2x2 for the square piece and 3x3 for the other tetrominoes to keep the logic consistent. I'm tracing through the rotation logic for the I piece—extracting each column, reversing it, and placing it as a new row. After the clockwise rotation, the horizontal line becomes vertical, which looks right, but let me verify this is actually correct.\n\nLooking at standard Tetris, the I piece should rotate from a horizontal bar to a vertical one, and my result places it at column index 2, which is the right-center position in a 4-wide bounding box—that matches the Super Rotation System behavior, so the rotation functions are working as intended.\n\nNow I'm ready to start building out all the necessary files for the game.\n\nI'll implement a 7-bag randomizer to ensure fair piece distribution throughout gameplay. The structure will be straightforward: create a bag with one of each piece type, shuffle it, draw pieces sequentially, and generate a fresh shuffled bag once it's depleted. I'm planning the TypeScript file to include constants, types, utility functions for board operations and rotation, and the randomizer logic.\n\nNow I'm setting up the Game class with methods for movement, rotation with wall kicks, line clearing, and rendering. I'll also build out the HTML and styling to create an attractive interface with the game board prominently displayed alongside score and level information.\n\nWriting the complete implementation... Implementing DAS with a key tracking system...\n\nActually, the browser's native key repeat is simpler—it fires keydown events automatically after an initial delay, which works fine for Tetris. I'll just listen for keydown events and prevent the default scroll behavior on arrow keys. Now I'm thinking about adding a line clear animation that flashes completed rows white before removing them.\n\nImplementing the flash animation...\n\nNow I need to think about state management more carefully—when rows are flashing, the current piece shouldn't be dropping. I should introduce distinct game states: playing for normal gameplay, flashing for the line clear animation, game over, and paused.\n\nActually, I'll keep this simpler with just a few boolean flags: `gameOver`, `paused`, and `clearingLines` to track when we're animating line clears. When `clearingLines` is true, I'll skip processing drops and inputs.\n\nI've been overthinking this—time to actually write the code. I'm going to implement the core features: basic Tetris mechanics, 7-bag randomizer, ghost piece, hard drop, basic wall kicks, a clean visual design, score and level tracking with localStorage for high scores, pause functionality, and a simple line clear flash animation. Let me start building the files now.\n\nI'm reconsidering the architecture—instead of juggling multiple canvas elements, I'll use CSS and DOM for the HUD and reserve canvas just for the game board and next piece preview. Now I'm writing the main game logic...\n\nDefining the core constants and piece colors... Defining the piece types and board structure...\n\nNow I'm thinking through the spawn logic—I need to handle the case where a piece spawns and immediately collides with existing blocks at the top of the board, which should trigger an immediate game over. I also want to allow pieces to spawn partially above the visible play area, which is standard Tetris behavior, so I need to account for that edge case when checking collisions.\n\nThe collision detection already handles this correctly by only checking cells that are actually within the board bounds, and the rendering skips any cells above the visible area. All the Tetris piece shapes fit within square bounding boxes, so the standard clockwise and counterclockwise rotation functions will work without needing special handling for non-square matrices.\n\nTime to write out all the code files. Setting up the Tetris game constants and piece definitions... I'm defining the Piece interface with position and rotation properties, then setting up the game board initialization and rotation functions for the Tetris pieces. Now I'm building a Bag class that manages piece distribution using a shuffled pool so each piece type appears once per cycle.\n\nImplementing piece creation and collision detection... Writing game logic... Setting up the game class... Setting up the game state with initial values for the board, score tracking, and game flags, then configuring keyboard controls to handle player input while preventing default browser scrolling behavior.\n\nStill writing input handler... Writing the game mechanics... Writing game logic... Writing the rendering logic... Still writing canvas rendering... Writing the rendering logic... Writing the game over screen and text rendering...\n\nNow I'm setting up the preview panel to display the next piece, centering it within the preview canvas and drawing each block of the piece with its color. The game loop is starting up next.\n\nHandling the game timing and update logic...\n\nNow I'm implementing the input handling for left and right movement with delayed auto-repeat, where holding a direction key triggers movement after an initial delay and then repeats at a faster rate. I'm also tracking soft drop input to accelerate the piece's fall speed when the down arrow is held.\n\nChecking if the piece can drop further each frame, and if it can't fit below, I lock it in place and trigger the line-clearing logic. Then I'm rendering the game board and preview windows for the next piece and held piece, setting up the animation loop to continue running.\n\nI realize I need to add a `peek()` method to the Bag class so I can look ahead at the next piece without removing it from the queue.\n\nActually, a cleaner approach is to pre-generate and store the next piece explicitly. I'll add a `next` property that holds the upcoming piece, then populate it during initialization and update it whenever I spawn a new piece. This way the preview window always has something ready to display without needing to peek into the bag.\n\nNow I'm rethinking the keyboard input handling for DAS and ARR. The current approach of tracking `wasDown` on keydown might miss the initial key press if I'm checking whether the key exists before adding it to the set. Let me refactor the input logic...\n\nSimplifying keyboard handling...\n\nI think the cleanest approach is to track which keys are currently held, trigger immediate actions on the first keydown, and then handle auto-repeat in the game loop using proper DAS/ARR timing rather than relying on the OS key repeat rate. This way I can control the exact repeat behavior for movement while keeping rotation and hard drop as keydown-only events.\n\nImplementing the input handler... I'm overcomplicating this—I should just rely on the browser's built-in key repeat for movement and use the `e.repeat` property to distinguish between first presses and repeats, so rotation and hard drop only trigger once while movement happens continuously.\n\nSetting up the event handler...\n\nI notice the soft drop might be too slow if I rely on the OS key repeat rate, so I should track when ArrowDown is being held down instead and handle it in the game loop for smoother continuous dropping.\n\nLet me settle on a hybrid approach: discrete actions like rotation and hard drop use the `e.repeat` check to fire once, left and right movement use DAS/ARR logic in the game loop, and soft drop gets handled by tracking the held state and increasing drop speed during each frame.\n\nSetting up keyboard tracking... Setting up the key press handler to toggle pause with P, restart the game with Enter or R when it's over, and then routing arrow keys and other inputs to movement, rotation, and soft drop actions...\n\nImplementing DAS and ARR timing...\n\nNow I'm setting up the drop speed curve with standard Tetris progression, where each level increases the fall speed until reaching near-instant drops at higher levels. I'm settling on a lookup table approach for drop speeds since it's cleaner and matches the standard Tetris progression better than a formula. Now I'll stop deliberating and start building—setting up the core constants like board dimensions and timing values, then implementing the bag randomizer and utility functions I'll need.\n\nFor the HTML, I'm creating a three-panel layout with the game board centered, the hold piece on the left, and the next piece, score, level, and controls stacked on the right, all wrapped in a dark theme.\n\nLet me start writing the actual implementation now.\n\nI'm deciding between inlining everything in a single HTML file or using separate files with TypeScript compilation. Since we're using TypeScript anyway, I'll go with a separate source file that compiles to JavaScript, then load it from the HTML.\n\nConfiguring TypeScript... Defining the game constants and piece data... Defining the Tetris piece types and game structure... Writing game logic... Writing the piece randomizer... Setting up the game class with canvas references for the board, next piece preview, and hold display, then initializing all the game state variables like score, level, and timing counters for piece movement and line clearing animations. Setting up the game state...\n\nNow I'm wiring up the input handling to track key presses and releases, preventing default behavior for game controls while managing the timing for delayed auto-repeat and auto-shift mechanics. Writing the game controls... Writing game logic... Writing the rendering methods... Finishing the gradient overlay and adding the shine effects with highlights on the top-left and shadows on the bottom-right to give the cells depth, then moving into the board rendering logic.\n\nNow I'm setting up the canvas dimensions and filling in the background with a dark color, then drawing a subtle grid pattern across the board before iterating through each cell to render them.\n\nRendering the board cells...\n\nNow I'm drawing the ghost piece with reduced opacity to show where the current piece will land, then rendering the actual piece on top of it. After that, I need to handle the pause overlay display.\n\nAdding pause and game over screens... Writing the preview renderer...\n\nNow I'm setting up the main game loop that handles delta time calculations and manages the flash animation when rows are cleared—once the flash timer expires, I remove the completed rows, update the score and level, and spawn the next piece.\n\nHandling horizontal movement with DAS and ARR timers...\n\nNow I'm finishing the drop logic—checking if the piece can move down, incrementing the score on soft drops, and locking the piece when it hits the bottom. Then rendering the board and preview windows before requesting the next animation frame to keep the game loop running.\n\nWriting the HTML and styles... Still writing styles... Still writing styles... I'm finishing up the control key mappings for the game interface, then reconsidering the layout—putting TETRIS as a horizontal title at the top makes more sense than having it vertically stacked, and I should reorganize the panels so the board is centered with the hold piece on the left and score information on the right. I'm going to stop overthinking the layout and just start building the files—I'll create the HTML, TypeScript game logic, and config files, then compile everything with TypeScript. I need to handle that edge case with the counter-clockwise rotation too.\n\nVerifying the rotation logic... The rotation functions are working correctly for both counterclockwise and clockwise rotations. Now I'm ready to write out the complete implementation.\n\nI'm thinking through the soft drop scoring logic in the game loop—when the player holds down, the piece drops faster and should award points for each row descended, but I need to make sure I'm not double-counting points from both the initial key press and the accelerated drop in the loop. Refining the soft drop logic to avoid double-counting on the first frame by resetting the drop counter after an immediate drop, which prevents the game loop from firing again right away.\n\nNow I'm ready to start writing the actual implementation. Before I do, I need to finalize the soft drop speed calculation—capping it at 50ms means soft drop matches normal drop speed at higher levels, which is acceptable since the game is already moving fast at that point. drop at 40ms, which is faster than the normal 800ms interval. So the formula is working correctly — soft drop divides the normal interval by 20 and caps it at 50ms minimum, ensuring it's always significantly faster than the regular drop speed.\n\nI'm settling on a simpler approach: just use a fixed 50ms interval when the down key is held, since that's the standard soft drop speed in Tetris and keeps the logic cleaner.\n\nNow I'm ready to start writing the actual implementation across four files: the main TypeScript game logic, the HTML structure with styling, and the necessary config files.","signature":"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"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":0,"cache_read_input_tokens":4727,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":0,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"9f3b9e4b-7243-45c9-b138-5ca9045972aa"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01JeLSynRSkDLv7hRes1oFG6","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_019utcpH5TzqzCWxCCUUzw8Z","name":"Bash","input":{"command":"ls /tmp/loop-bench-iuvnueix","description":"Check current directory contents"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":3,"cache_creation_input_tokens":0,"cache_read_input_tokens":4727,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":0,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"8c0eff29-0fe9-4fd7-9d4e-bb6168fcac1d"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_019utcpH5TzqzCWxCCUUzw8Z","type":"tool_result","content":"node_modules\npackage.json\npackage-lock.json","is_error":false}]},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"0930167c-86b1-409e-b617-aa047e845bfa","timestamp":"2026-04-04T21:07:13.041Z","tool_use_result":{"stdout":"node_modules\npackage.json\npackage-lock.json","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01HfDgoNcep8gnYwZczS32Ts","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01FSwrqfQGCa94Yh2fq13Zgw","name":"Read","input":{"file_path":"/tmp/loop-bench-iuvnueix/package.json"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":38204,"cache_read_input_tokens":4241,"cache_creation":{"ephemeral_5m_input_tokens":38204,"ephemeral_1h_input_tokens":0},"output_tokens":59,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"1d8d16e7-8fa0-49f8-bb90-a1326da99675"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01FSwrqfQGCa94Yh2fq13Zgw","type":"tool_result","content":"1\t{\n2\t \"name\": \"loop-bench-iuvnueix\",\n3\t \"version\": \"1.0.0\",\n4\t \"description\": \"\",\n5\t \"main\": \"index.js\",\n6\t \"scripts\": {\n7\t \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n8\t },\n9\t \"keywords\": [],\n10\t \"author\": \"\",\n11\t \"license\": \"ISC\",\n12\t \"type\": \"commonjs\",\n13\t \"devDependencies\": {\n14\t \"@eslint/js\": \"^10.0.1\",\n15\t \"@playwright/test\": \"^1.59.1\",\n16\t \"@types/node\": \"^25.5.2\",\n17\t \"eslint\": \"^10.2.0\",\n18\t \"typescript\": \"^6.0.2\"\n19\t }\n20\t}\n21\t"}]},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"2ba191e2-90e1-479b-89c5-653291aaae62","timestamp":"2026-04-04T21:07:14.972Z","tool_use_result":{"type":"text","file":{"filePath":"/tmp/loop-bench-iuvnueix/package.json","content":"{\n \"name\": \"loop-bench-iuvnueix\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },\n \"keywords\": [],\n \"author\": \"\",\n \"license\": \"ISC\",\n \"type\": \"commonjs\",\n \"devDependencies\": {\n \"@eslint/js\": \"^10.0.1\",\n \"@playwright/test\": \"^1.59.1\",\n \"@types/node\": \"^25.5.2\",\n \"eslint\": \"^10.2.0\",\n \"typescript\": \"^6.0.2\"\n }\n}\n","numLines":21,"startLine":1,"totalLines":21}}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01XBVxaNWKTp9JxJh3Umnq5M","type":"message","role":"assistant","content":[{"type":"text","text":"Good — TypeScript is already installed. Let me build the full Tetris game now."}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":38476,"cache_read_input_tokens":4282,"cache_creation":{"ephemeral_5m_input_tokens":38476,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"619b3e27-7033-4eb2-8558-4ef83c3582a5"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01XBVxaNWKTp9JxJh3Umnq5M","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01AekFUvdGPWG1jpB1kGEkLA","name":"Bash","input":{"command":"ls /tmp/loop-bench-iuvnueix/node_modules/.bin/tsc && node /tmp/loop-bench-iuvnueix/node_modules/.bin/tsc --version","description":"Verify tsc is available"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":38476,"cache_read_input_tokens":4282,"cache_creation":{"ephemeral_5m_input_tokens":38476,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"082db909-7c60-4f96-80b7-906b2244a883"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01AekFUvdGPWG1jpB1kGEkLA","type":"tool_result","content":"/tmp/loop-bench-iuvnueix/node_modules/.bin/tsc\nVersion 6.0.2","is_error":false}]},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"e4fb89db-3736-4729-8aa7-cb2fd60a6d62","timestamp":"2026-04-04T21:07:17.595Z","tool_use_result":{"stdout":"/tmp/loop-bench-iuvnueix/node_modules/.bin/tsc\nVersion 6.0.2","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01AjNMV2Xgarq3TSPNxC7x2M","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01RL31T6QALVw6bQrPPeDcJF","name":"Bash","input":{"command":"mkdir -p /tmp/loop-bench-iuvnueix/src /tmp/loop-bench-iuvnueix/dist","description":"Create src and dist directories"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":180,"cache_read_input_tokens":42758,"cache_creation":{"ephemeral_5m_input_tokens":180,"ephemeral_1h_input_tokens":0},"output_tokens":54,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"af63a47b-65e8-4bb2-bdd1-f1bcdc3a8ac0"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01RL31T6QALVw6bQrPPeDcJF","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"927bff6d-145c-4eb6-8784-f4de76f4fb57","timestamp":"2026-04-04T21:07:19.310Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":true}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_016aY5h7Q6QumiQTKEAeFyKH","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01L9YiYAWUKoQwix7xM9m4n7","name":"Bash","input":{"command":"cat > /tmp/loop-bench-iuvnueix/tsconfig.json << 'EOF'\n{\n \"compilerOptions\": {\n \"target\": \"ES2017\",\n \"module\": \"none\",\n \"strict\": true,\n \"outDir\": \"dist\",\n \"noEmitOnError\": true,\n \"lib\": [\"ES2017\", \"DOM\"]\n },\n \"include\": [\"src/**/*.ts\"]\n}\nEOF","description":"Write tsconfig.json"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":121,"cache_read_input_tokens":42938,"cache_creation":{"ephemeral_5m_input_tokens":121,"ephemeral_1h_input_tokens":0},"output_tokens":58,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"ea549bff-17bf-4b6b-87d5-ea001c6f385b"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01L9YiYAWUKoQwix7xM9m4n7","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"173b7137-ba47-46d6-a359-87e313de1f21","timestamp":"2026-04-04T21:07:22.748Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_012dhmd7z5AMzChiVdXFweYe","type":"message","role":"assistant","content":[{"type":"text","text":"Now let me write the full TypeScript source:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":200,"cache_read_input_tokens":43059,"cache_creation":{"ephemeral_5m_input_tokens":200,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"580b18b5-2930-4c6a-85f7-aa147d148bde"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_012dhmd7z5AMzChiVdXFweYe","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01BYK2dqwigThaH7X7T89oak","name":"Bash","input":{"command":"cat > /tmp/loop-bench-iuvnueix/src/tetris.ts << 'TSEOF'\n// ─── Constants ────────────────────────────────────────────────────────────────\n\nconst COLS = 10;\nconst ROWS = 20;\nconst CELL = 32; // px per grid cell\nconst DAS = 170; // Delayed Auto Shift (ms)\nconst ARR = 50; // Auto Repeat Rate (ms)\nconst FLASH_MS = 280; // Line-clear flash duration (ms)\nconst LOCK_DELAY = 500; // Lock delay after piece hits floor (ms)\n\n// ─── Piece Definitions ────────────────────────────────────────────────────────\n\nconst COLORS: Record<string, string> = {\n I: '#00c8ff',\n O: '#ffd700',\n T: '#c040f0',\n S: '#10e060',\n Z: '#ff3030',\n J: '#3878ff',\n L: '#ff8800',\n};\n\n// All pieces use square bounding boxes (2×2, 3×3, or 4×4)\nconst SHAPES: Record<string, number[][]> = {\n I: [[0,0,0,0],\n [1,1,1,1],\n [0,0,0,0],\n [0,0,0,0]],\n O: [[1,1],\n [1,1]],\n T: [[0,1,0],\n [1,1,1],\n [0,0,0]],\n S: [[0,1,1],\n [1,1,0],\n [0,0,0]],\n Z: [[1,1,0],\n [0,1,1],\n [0,0,0]],\n J: [[1,0,0],\n [1,1,1],\n [0,0,0]],\n L: [[0,0,1],\n [1,1,1],\n [0,0,0]],\n};\n\n// Standard speed curve (ms per gravity drop) for levels 1-13\nconst SPEED_CURVE = [800,717,633,550,467,383,300,217,133,100,83,67,50];\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ntype Cell = string | null;\ntype Board = Cell[][];\n\ninterface Piece {\n type: string;\n shape: number[][];\n color: string;\n x: number;\n y: number;\n}\n\n// ─── Utility Functions ────────────────────────────────────────────────────────\n\nfunction createBoard(): Board {\n return Array.from({ length: ROWS }, () => new Array<Cell>(COLS).fill(null));\n}\n\n/** Rotate matrix 90° clockwise */\nfunction rotateCW(m: number[][]): number[][] {\n return m[0].map((_, i) => m.map(row => row[i]).reverse());\n}\n\n/** Rotate matrix 90° counter-clockwise */\nfunction rotateCCW(m: number[][]): number[][] {\n return m[0].map((_, i) => m.map(row => row[row.length - 1 - i])).reverse();\n}\n\n/** Spawn a new piece centered at the top */\nfunction makePiece(type: string): Piece {\n const shape = SHAPES[type].map(r => [...r]);\n return {\n type,\n shape,\n color: COLORS[type],\n x: Math.floor((COLS - shape[0].length) / 2),\n y: 0,\n };\n}\n\n/** Returns true if the piece (optionally with offsets / alternate shape) fits on the board */\nfunction fits(board: Board, piece: Piece, dx = 0, dy = 0, shape = piece.shape): boolean {\n for (let r = 0; r < shape.length; r++) {\n for (let c = 0; c < shape[r].length; c++) {\n if (!shape[r][c]) continue;\n const nx = piece.x + c + dx;\n const ny = piece.y + r + dy;\n if (nx < 0 || nx >= COLS || ny >= ROWS) return false;\n if (ny >= 0 && board[ny][nx]) return false;\n }\n }\n return true;\n}\n\n/** Place the piece onto the board, returning a new board */\nfunction stamp(board: Board, piece: Piece): Board {\n const b = board.map(r => [...r]);\n piece.shape.forEach((row, r) => {\n row.forEach((cell, c) => {\n if (cell) {\n const ny = piece.y + r;\n const nx = piece.x + c;\n if (ny >= 0 && ny < ROWS && nx >= 0 && nx < COLS) b[ny][nx] = piece.color;\n }\n });\n });\n return b;\n}\n\n/** Return indices of completely filled rows */\nfunction fullRows(board: Board): number[] {\n return board.reduce<number[]>((acc, row, i) => {\n if (row.every(c => c !== null)) acc.push(i);\n return acc;\n }, []);\n}\n\n/** Remove the specified rows and prepend empty rows at the top */\nfunction removeRows(board: Board, rows: number[]): Board {\n const kept = board.filter((_, i) => !rows.includes(i));\n const empties = Array.from({ length: rows.length }, () => new Array<Cell>(COLS).fill(null));\n return [...empties, ...kept];\n}\n\nconst SCORE_TABLE = [0, 100, 300, 500, 800];\n\nfunction lineScore(n: number, level: number): number {\n return (SCORE_TABLE[n] ?? 0) * level;\n}\n\nfunction dropInterval(level: number): number {\n return SPEED_CURVE[Math.min(level - 1, SPEED_CURVE.length - 1)];\n}\n\n// ─── 7-Bag Randomiser ─────────────────────────────────────────────────────────\n\nclass Bag {\n private items: string[] = [];\n\n private refill(): void {\n this.items = Object.keys(SHAPES);\n for (let i = this.items.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [this.items[i], this.items[j]] = [this.items[j], this.items[i]];\n }\n }\n\n draw(): string {\n if (this.items.length === 0) this.refill();\n return this.items.pop()!;\n }\n}\n\n// ─── Main Game Class ──────────────────────────────────────────────────────────\n\nclass TetrisGame {\n // Canvases\n private readonly boardCanvas: HTMLCanvasElement;\n private readonly boardCtx: CanvasRenderingContext2D;\n private readonly nextCanvas: HTMLCanvasElement;\n private readonly nextCtx: CanvasRenderingContext2D;\n private readonly holdCanvas: HTMLCanvasElement;\n private readonly holdCtx: CanvasRenderingContext2D;\n\n // Game state\n private bag!: Bag;\n private board!: Board;\n private current!: Piece;\n private next!: Piece;\n private held: Piece | null = null;\n private canHold!: boolean;\n private score!: number;\n private lines!: number;\n private level!: number;\n private highScore: number;\n private gameOver!: boolean;\n private paused!: boolean;\n\n // Timing\n private lastTime = 0;\n private dropCounter!: number;\n private lockTimer!: number; // counts toward lock delay\n private onFloor!: boolean; // true when piece is resting on something\n\n // Line-clear flash\n private flashTimer = 0;\n private flashRows: number[] = [];\n private pendingClr = 0;\n private pendingScr = 0;\n\n // Input\n private keysDown = new Set<string>();\n private dasTimer = 0;\n private arrTimer = 0;\n\n // ── Constructor ─────────────────────────────────────────────────────────────\n\n constructor() {\n const get = <T extends HTMLElement>(id: string) =>\n document.getElementById(id) as T;\n\n this.boardCanvas = get<HTMLCanvasElement>('board');\n this.boardCtx = this.boardCanvas.getContext('2d')!;\n this.nextCanvas = get<HTMLCanvasElement>('next');\n this.nextCtx = this.nextCanvas.getContext('2d')!;\n this.holdCanvas = get<HTMLCanvasElement>('hold');\n this.holdCtx = this.holdCanvas.getContext('2d')!;\n\n this.boardCanvas.width = COLS * CELL;\n this.boardCanvas.height = ROWS * CELL;\n this.nextCanvas.width = 4 * CELL;\n this.nextCanvas.height = 4 * CELL;\n this.holdCanvas.width = 4 * CELL;\n this.holdCanvas.height = 4 * CELL;\n\n this.highScore = parseInt(localStorage.getItem('tetris-hs') ?? '0', 10);\n\n this.setupInput();\n this.init();\n requestAnimationFrame(t => this.loop(t));\n }\n\n // ── Initialisation ──────────────────────────────────────────────────────────\n\n private init(): void {\n this.bag = new Bag();\n this.board = createBoard();\n this.current = makePiece(this.bag.draw());\n this.next = makePiece(this.bag.draw());\n this.held = null;\n this.canHold = true;\n this.score = 0;\n this.lines = 0;\n this.level = 1;\n this.gameOver = false;\n this.paused = false;\n this.dropCounter = 0;\n this.lockTimer = 0;\n this.onFloor = false;\n this.flashTimer = 0;\n this.flashRows = [];\n this.pendingClr = 0;\n this.pendingScr = 0;\n this.updateHUD();\n }\n\n // ── Input ───────────────────────────────────────────────────────────────────\n\n private setupInput(): void {\n // Prevent page scroll for game keys\n window.addEventListener('keydown', e => {\n if (['ArrowUp','ArrowDown','ArrowLeft','ArrowRight','Space'].includes(e.code))\n e.preventDefault();\n\n if (!e.repeat) {\n this.keysDown.add(e.code);\n this.onFirstPress(e.code);\n }\n }, { passive: false });\n\n window.addEventListener('keyup', e => {\n this.keysDown.delete(e.code);\n if (e.code === 'ArrowLeft' || e.code === 'ArrowRight') {\n this.dasTimer = 0;\n this.arrTimer = 0;\n }\n });\n }\n\n private onFirstPress(code: string): void {\n if (code === 'KeyP') {\n if (!this.gameOver) this.paused = !this.paused;\n return;\n }\n\n if (this.gameOver) {\n if (code === 'Enter' || code === 'KeyR') this.init();\n return;\n }\n\n if (this.paused || this.flashTimer > 0) return;\n\n switch (code) {\n case 'ArrowLeft':\n this.move(-1);\n this.dasTimer = 0;\n this.arrTimer = 0;\n break;\n case 'ArrowRight':\n this.move(1);\n this.dasTimer = 0;\n this.arrTimer = 0;\n break;\n case 'ArrowDown': this.softDrop(); break;\n case 'ArrowUp': this.rotate(1); break;\n case 'KeyZ': this.rotate(-1); break;\n case 'KeyX': this.rotate(1); break;\n case 'Space': this.hardDrop(); break;\n case 'ShiftLeft':\n case 'ShiftRight':\n case 'KeyC': this.holdPiece(); break;\n }\n }\n\n // ── Actions ─────────────────────────────────────────────────────────────────\n\n private move(dx: number): boolean {\n if (fits(this.board, this.current, dx)) {\n this.current.x += dx;\n if (this.onFloor) this.lockTimer = 0; // reset lock delay on successful move\n return true;\n }\n return false;\n }\n\n private softDrop(): void {\n if (fits(this.board, this.current, 0, 1)) {\n this.current.y++;\n this.score++;\n this.dropCounter = 0;\n this.lockTimer = 0;\n this.updateHUD();\n }\n }\n\n private hardDrop(): void {\n let n = 0;\n while (fits(this.board, this.current, 0, 1)) {\n this.current.y++;\n n++;\n }\n this.score += n * 2;\n this.lock();\n }\n\n private rotate(dir: 1 | -1): void {\n const rotated = dir === 1 ? rotateCW(this.current.shape) : rotateCCW(this.current.shape);\n // Wall-kick offsets: try center, then nudge left/right, then further\n const kicks = [0, -1, 1, -2, 2];\n for (const kick of kicks) {\n if (fits(this.board, this.current, kick, 0, rotated)) {\n this.current.shape = rotated;\n this.current.x += kick;\n if (this.onFloor) this.lockTimer = 0; // reset lock delay on successful rotate\n return;\n }\n }\n }\n\n private holdPiece(): void {\n if (!this.canHold) return;\n const savedType = this.current.type;\n if (this.held) {\n this.current = makePiece(this.held.type);\n } else {\n this.current = this.next;\n this.next = makePiece(this.bag.draw());\n }\n this.held = makePiece(savedType);\n this.canHold = false;\n this.onFloor = false;\n this.lockTimer = 0;\n this.dropCounter = 0;\n }\n\n private lock(): void {\n this.board = stamp(this.board, this.current);\n this.canHold = true;\n this.onFloor = false;\n\n const fr = fullRows(this.board);\n if (fr.length > 0) {\n this.flashRows = fr;\n this.flashTimer = FLASH_MS;\n this.pendingClr = fr.length;\n this.pendingScr = lineScore(fr.length, this.level);\n } else {\n this.spawnNext();\n }\n }\n\n private spawnNext(): void {\n this.current = this.next;\n this.next = makePiece(this.bag.draw());\n this.dropCounter = 0;\n this.lockTimer = 0;\n this.onFloor = false;\n\n if (!fits(this.board, this.current)) {\n this.gameOver = true;\n if (this.score > this.highScore) {\n this.highScore = this.score;\n localStorage.setItem('tetris-hs', String(this.highScore));\n }\n }\n this.updateHUD();\n }\n\n // ── HUD ─────────────────────────────────────────────────────────────────────\n\n private updateHUD(): void {\n const el = (id: string) => document.getElementById(id)!;\n el('score').textContent = this.score.toLocaleString();\n el('lines').textContent = String(this.lines);\n el('level').textContent = String(this.level);\n el('high-score').textContent = Math.max(this.highScore, this.score).toLocaleString();\n }\n\n // ── Rendering ────────────────────────────────────────────────────────────────\n\n private ghostOffset(): number {\n let dy = 0;\n while (fits(this.board, this.current, 0, dy + 1)) dy++;\n return dy;\n }\n\n private drawBlock(\n ctx: CanvasRenderingContext2D,\n x: number,\n y: number,\n color: string,\n alpha = 1,\n ): void {\n const px = x * CELL;\n const py = y * CELL;\n const m = 1; // margin between cells\n const s = CELL - m * 2;\n\n ctx.globalAlpha = alpha;\n\n // Base fill\n ctx.fillStyle = color;\n ctx.fillRect(px + m, py + m, s, s);\n\n // Gradient sheen\n const g = ctx.createLinearGradient(px, py, px, py + CELL);\n g.addColorStop(0, 'rgba(255,255,255,0.38)');\n g.addColorStop(0.40, 'rgba(255,255,255,0.04)');\n g.addColorStop(1, 'rgba(0,0,0,0.22)');\n ctx.fillStyle = g;\n ctx.fillRect(px + m, py + m, s, s);\n\n // Top-left highlight bevel\n ctx.fillStyle = 'rgba(255,255,255,0.55)';\n ctx.fillRect(px + m, py + m, s, 3); // top edge\n ctx.fillRect(px + m, py + m, 3, s); // left edge\n\n // Bottom-right shadow bevel\n ctx.fillStyle = 'rgba(0,0,0,0.35)';\n ctx.fillRect(px + m, py + CELL - m - 3, s, 3); // bottom edge\n ctx.fillRect(px + CELL - m - 3, py + m, 3, s); // right edge\n\n ctx.globalAlpha = 1;\n }\n\n private renderBoard(): void {\n const ctx = this.boardCtx;\n const W = this.boardCanvas.width;\n const H = this.boardCanvas.height;\n\n // Background\n ctx.fillStyle = '#0d1117';\n ctx.fillRect(0, 0, W, H);\n\n // Subtle grid lines\n ctx.strokeStyle = 'rgba(255,255,255,0.04)';\n ctx.lineWidth = 1;\n for (let c = 0; c <= COLS; c++) {\n ctx.beginPath(); ctx.moveTo(c * CELL, 0); ctx.lineTo(c * CELL, H); ctx.stroke();\n }\n for (let r = 0; r <= ROWS; r++) {\n ctx.beginPath(); ctx.moveTo(0, r * CELL); ctx.lineTo(W, r * CELL); ctx.stroke();\n }\n\n // Placed board cells (with flash effect on clearing rows)\n for (let r = 0; r < ROWS; r++) {\n for (let c = 0; c < COLS; c++) {\n const cell = this.board[r][c];\n if (!cell) continue;\n\n if (this.flashRows.includes(r)) {\n // Alternate white / colour every ~35ms\n const t = this.flashTimer / FLASH_MS;\n const flash = Math.floor(t * 8) % 2 === 0;\n this.drawBlock(ctx, c, r, flash ? '#ffffff' : cell);\n } else {\n this.drawBlock(ctx, c, r, cell);\n }\n }\n }\n\n // Ghost & active piece — hidden during flash animation\n if (this.flashTimer <= 0 && !this.gameOver) {\n const gDy = this.ghostOffset();\n\n // Ghost\n if (gDy > 0) {\n this.current.shape.forEach((row, r) => {\n row.forEach((cell, c) => {\n const gy = this.current.y + r + gDy;\n if (cell && gy >= 0) {\n this.drawBlock(ctx, this.current.x + c, gy, this.current.color, 0.18);\n }\n });\n });\n }\n\n // Active piece\n this.current.shape.forEach((row, r) => {\n row.forEach((cell, c) => {\n const py = this.current.y + r;\n if (cell && py >= 0) {\n this.drawBlock(ctx, this.current.x + c, py, this.current.color);\n }\n });\n });\n }\n\n // ── Overlays ──────────────────────────────────────────────────────────────\n\n if (this.paused) {\n ctx.fillStyle = 'rgba(0,0,0,0.72)';\n ctx.fillRect(0, 0, W, H);\n this.cText(ctx, 'PAUSED', W/2, H/2 - 22, '#ffffff', 'bold 34px monospace');\n this.cText(ctx, 'P — resume', W/2, H/2 + 18, '#778899', '15px monospace');\n }\n\n if (this.gameOver) {\n ctx.fillStyle = 'rgba(0,0,0,0.80)';\n ctx.fillRect(0, 0, W, H);\n this.cText(ctx, 'GAME OVER', W/2, H/2 - 46, '#ff4040', 'bold 36px monospace');\n this.cText(ctx, `Score: ${this.score.toLocaleString()}`, W/2, H/2 - 2, '#e0e0e0', '20px monospace');\n this.cText(ctx, 'Enter — play again', W/2, H/2 + 38, '#778899', '14px monospace');\n }\n }\n\n private cText(\n ctx: CanvasRenderingContext2D,\n text: string,\n x: number, y: number,\n color: string, font: string,\n ): void {\n ctx.save();\n ctx.fillStyle = color;\n ctx.font = font;\n ctx.textAlign = 'center';\n ctx.textBaseline = 'middle';\n ctx.fillText(text, x, y);\n ctx.restore();\n }\n\n private renderPreview(\n ctx: CanvasRenderingContext2D,\n canvas: HTMLCanvasElement,\n piece: Piece | null,\n dimmed = false,\n ): void {\n ctx.fillStyle = '#0d1117';\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n if (!piece) return;\n\n const s = piece.shape;\n const ox = Math.floor((4 - s[0].length) / 2);\n const oy = Math.floor((4 - s.length) / 2);\n s.forEach((row, r) => {\n row.forEach((cell, c) => {\n if (cell) this.drawBlock(ctx, ox + c, oy + r, piece.color, dimmed ? 0.38 : 1);\n });\n });\n }\n\n // ── Game Loop ────────────────────────────────────────────────────────────────\n\n private loop(time: number): void {\n const dt = Math.min(time - (this.lastTime || time), 100); // cap at 100ms\n this.lastTime = time;\n\n if (!this.paused && !this.gameOver) {\n // ── Flash timer (line-clear animation) ──────────────────────────────────\n if (this.flashTimer > 0) {\n this.flashTimer -= dt;\n if (this.flashTimer <= 0) {\n this.flashTimer = 0;\n this.board = removeRows(this.board, this.flashRows);\n this.flashRows = [];\n this.lines += this.pendingClr;\n this.score += this.pendingScr;\n this.level = Math.floor(this.lines / 10) + 1;\n this.pendingClr = 0;\n this.pendingScr = 0;\n this.spawnNext();\n }\n } else {\n // ── DAS / ARR horizontal movement ─────────────────────────────────────\n const leftDown = this.keysDown.has('ArrowLeft');\n const rightDown = this.keysDown.has('ArrowRight');\n\n if (leftDown || rightDown) {\n this.dasTimer += dt;\n if (this.dasTimer > DAS) {\n this.arrTimer += dt;\n if (this.arrTimer >= ARR) {\n const steps = Math.floor(this.arrTimer / ARR);\n this.arrTimer -= steps * ARR;\n for (let i = 0; i < steps; i++) {\n if (leftDown && !rightDown) this.move(-1);\n if (rightDown && !leftDown) this.move(1);\n }\n }\n }\n }\n\n // ── Gravity ───────────────────────────────────────────────────────────\n const softDropping = this.keysDown.has('ArrowDown');\n const interval = softDropping ? 50 : dropInterval(this.level);\n\n this.dropCounter += dt;\n if (this.dropCounter >= interval) {\n this.dropCounter = 0;\n if (fits(this.board, this.current, 0, 1)) {\n this.current.y++;\n this.onFloor = false;\n if (softDropping) {\n this.score++;\n this.updateHUD();\n }\n } else {\n // Piece is resting on something — start lock delay\n this.onFloor = true;\n this.lockTimer += dt + interval; // add the elapsed time\n if (this.lockTimer >= LOCK_DELAY) {\n this.lock();\n }\n }\n }\n\n // Accumulate lock delay even between gravity ticks\n if (this.onFloor && !fits(this.board, this.current, 0, 1)) {\n this.lockTimer += dt;\n if (this.lockTimer >= LOCK_DELAY) {\n this.lock();\n }\n }\n }\n }\n\n // ── Render ────────────────────────────────────────────────────────────────\n this.renderBoard();\n this.renderPreview(this.nextCtx, this.nextCanvas, this.next);\n this.renderPreview(this.holdCtx, this.holdCanvas, this.held, !this.canHold);\n\n requestAnimationFrame(t => this.loop(t));\n }\n}\n\n// ─── Bootstrap ───────────────────────────────────────────────────────────────\n\nwindow.addEventListener('load', () => new TetrisGame());\nTSEOF","description":"Write the full TypeScript game source"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":200,"cache_read_input_tokens":43059,"cache_creation":{"ephemeral_5m_input_tokens":200,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"6f61405a-4cf7-46b8-a599-b960ba2798cf"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01BYK2dqwigThaH7X7T89oak","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"4e7aefea-9cb0-4f5d-820c-fdb0e77db051","timestamp":"2026-04-04T21:08:57.078Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01EJufkx8RK3mq3aNHf1Khqu","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01HfZZ3RmJoAMMM2QAbzCnC6","name":"Bash","input":{"command":"cat > /tmp/loop-bench-iuvnueix/index.html << 'HTMLEOF'\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Tetris</title>\n <style>\n /* ── Reset ──────────────────────────────────────────────────────────── */\n *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\n body {\n background: #060a12;\n color: #d0d8f0;\n font-family: 'Courier New', Courier, monospace;\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: center;\n min-height: 100vh;\n overflow: hidden;\n user-select: none;\n }\n\n /* ── Title ──────────────────────────────────────────────────────────── */\n .game-title {\n font-size: 28px;\n font-weight: bold;\n letter-spacing: 10px;\n margin-bottom: 18px;\n background: linear-gradient(120deg, #00c8ff 0%, #c040f0 55%, #ff8800 100%);\n -webkit-background-clip: text;\n -webkit-text-fill-color: transparent;\n background-clip: text;\n text-shadow: none;\n }\n\n /* ── Layout ─────────────────────────────────────────────────────────── */\n .game-wrapper {\n display: flex;\n gap: 14px;\n align-items: flex-start;\n }\n\n .panel {\n width: 138px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n }\n\n /* ── Card ───────────────────────────────────────────────────────────── */\n .card {\n background: #0d1420;\n border: 1px solid #1e2a40;\n border-radius: 8px;\n padding: 10px 14px 12px;\n }\n\n .card-label {\n font-size: 9px;\n letter-spacing: 3px;\n text-transform: uppercase;\n color: #445566;\n margin-bottom: 5px;\n }\n\n .card-value {\n font-size: 26px;\n font-weight: bold;\n color: #c8d8f8;\n line-height: 1;\n }\n\n .card-value.md {\n font-size: 18px;\n }\n\n /* ── Board canvas ───────────────────────────────────────────────────── */\n #board {\n display: block;\n border: 2px solid #1e2a40;\n border-radius: 4px;\n box-shadow:\n 0 0 0 1px #060a12,\n 0 0 50px rgba(0, 160, 255, 0.09),\n 0 8px 40px rgba(0, 0, 0, 0.7);\n }\n\n /* ── Preview canvases ───────────────────────────────────────────────── */\n #next, #hold {\n display: block;\n border-radius: 4px;\n }\n\n /* ── Controls list ──────────────────────────────────────────────────── */\n .controls {\n font-size: 11px;\n line-height: 1.95;\n color: #3a4a5a;\n }\n\n .controls kbd {\n display: inline-block;\n min-width: 22px;\n text-align: center;\n color: #7090b0;\n background: #111c2a;\n border: 1px solid #1e2a40;\n border-radius: 3px;\n padding: 0 3px;\n font-family: inherit;\n font-size: 10px;\n }\n\n .ctrl-row {\n display: flex;\n align-items: center;\n gap: 5px;\n }\n\n .ctrl-desc {\n color: #4a5a6a;\n }\n\n /* ── Divider ─────────────────────────────────────────────────────────── */\n .divider {\n border: none;\n border-top: 1px solid #1a2030;\n margin: 2px 0;\n }\n </style>\n</head>\n<body>\n\n <div class=\"game-title\">TETRIS</div>\n\n <div class=\"game-wrapper\">\n\n <!-- ── Left Panel ─────────────────────────────────────────────────── -->\n <div class=\"panel\">\n\n <div class=\"card\">\n <div class=\"card-label\">Hold <kbd>C</kbd></div>\n <canvas id=\"hold\"></canvas>\n </div>\n\n <div class=\"card\">\n <div class=\"card-label\">Level</div>\n <div class=\"card-value\" id=\"level\">1</div>\n </div>\n\n <div class=\"card\">\n <div class=\"card-label\">Lines</div>\n <div class=\"card-value\" id=\"lines\">0</div>\n </div>\n\n </div>\n\n <!-- ── Board ──────────────────────────────────────────────────────── -->\n <canvas id=\"board\"></canvas>\n\n <!-- ── Right Panel ────────────────────────────────────────────────── -->\n <div class=\"panel\">\n\n <div class=\"card\">\n <div class=\"card-label\">Score</div>\n <div class=\"card-value md\" id=\"score\">0</div>\n </div>\n\n <div class=\"card\">\n <div class=\"card-label\">Best</div>\n <div class=\"card-value md\" id=\"high-score\">0</div>\n </div>\n\n <div class=\"card\">\n <div class=\"card-label\">Next</div>\n <canvas id=\"next\"></canvas>\n </div>\n\n <div class=\"card controls\">\n <div class=\"ctrl-row\"><kbd>←</kbd><kbd>→</kbd><span class=\"ctrl-desc\">Move</span></div>\n <div class=\"ctrl-row\"><kbd>↑</kbd><kbd>X</kbd><span class=\"ctrl-desc\">Rotate CW</span></div>\n <div class=\"ctrl-row\"><kbd>Z</kbd><span class=\"ctrl-desc\">Rotate CCW</span></div>\n <div class=\"ctrl-row\"><kbd>↓</kbd><span class=\"ctrl-desc\">Soft drop</span></div>\n <div class=\"ctrl-row\"><kbd>Spc</kbd><span class=\"ctrl-desc\">Hard drop</span></div>\n <div class=\"ctrl-row\"><kbd>C</kbd><kbd>⇧</kbd><span class=\"ctrl-desc\">Hold</span></div>\n <hr class=\"divider\" />\n <div class=\"ctrl-row\"><kbd>P</kbd><span class=\"ctrl-desc\">Pause</span></div>\n <div class=\"ctrl-row\"><kbd>R</kbd><span class=\"ctrl-desc\">Restart</span></div>\n </div>\n\n </div>\n\n </div>\n\n <script src=\"dist/tetris.js\"></script>\n</body>\n</html>\nHTMLEOF","description":"Write the HTML file with layout and styles"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":7238,"cache_read_input_tokens":43259,"cache_creation":{"ephemeral_5m_input_tokens":7238,"ephemeral_1h_input_tokens":0},"output_tokens":36,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"79d3c040-bd22-4ab2-ad63-3e8056526052"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01HfZZ3RmJoAMMM2QAbzCnC6","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"ffe37def-9555-4eac-af85-8304a8b761c9","timestamp":"2026-04-04T21:09:21.618Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01DF45uLbVDtWPvk4ETaAE62","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01Rk6Cai7UPTgUr5MyhThrgX","name":"Bash","input":{"command":"node_modules/.bin/tsc 2>&1","description":"Compile TypeScript to JavaScript"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":1997,"cache_read_input_tokens":50497,"cache_creation":{"ephemeral_5m_input_tokens":1997,"ephemeral_1h_input_tokens":0},"output_tokens":37,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"61f1bf46-2007-4aa7-9e09-927ba51e8639"} +{"type":"user","message":{"role":"user","content":[{"type":"tool_result","content":"Exit code 1\ntsconfig.json(4,15): error TS5107: Option 'module=None' is deprecated and will stop functioning in TypeScript 7.0. Specify compilerOption '\"ignoreDeprecations\": \"6.0\"' to silence this error.\ntsconfig.json(6,5): error TS5011: The common source directory of 'tsconfig.json' is './src'. The 'rootDir' setting must be explicitly set to this or another path to adjust your output's file layout.\n Visit https://aka.ms/ts6 for migration information.","is_error":true,"tool_use_id":"toolu_01Rk6Cai7UPTgUr5MyhThrgX"}]},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"5390fbc9-f1b6-4804-bbd3-014c87f9f5a5","timestamp":"2026-04-04T21:09:24.255Z","tool_use_result":"Error: Exit code 1\ntsconfig.json(4,15): error TS5107: Option 'module=None' is deprecated and will stop functioning in TypeScript 7.0. Specify compilerOption '\"ignoreDeprecations\": \"6.0\"' to silence this error.\ntsconfig.json(6,5): error TS5011: The common source directory of 'tsconfig.json' is './src'. The 'rootDir' setting must be explicitly set to this or another path to adjust your output's file layout.\n Visit https://aka.ms/ts6 for migration information."} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_014WYwuQtfaiTiCZukR8G5JR","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01DF8zTCKYgSKVD94PqBhP5T","name":"Edit","input":{"replace_all":false,"file_path":"/tmp/loop-bench-iuvnueix/tsconfig.json","old_string":"{\n \"compilerOptions\": {\n \"target\": \"ES2017\",\n \"module\": \"none\",\n \"strict\": true,\n \"outDir\": \"dist\",\n \"noEmitOnError\": true,\n \"lib\": [\"ES2017\", \"DOM\"]\n },\n \"include\": [\"src/**/*.ts\"]\n}","new_string":"{\n \"compilerOptions\": {\n \"target\": \"ES2017\",\n \"module\": \"none\",\n \"ignoreDeprecations\": \"6.0\",\n \"strict\": true,\n \"rootDir\": \"src\",\n \"outDir\": \"dist\",\n \"noEmitOnError\": true,\n \"lib\": [\"ES2017\", \"DOM\"]\n },\n \"include\": [\"src/**/*.ts\"]\n}"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":240,"cache_read_input_tokens":52494,"cache_creation":{"ephemeral_5m_input_tokens":240,"ephemeral_1h_input_tokens":0},"output_tokens":37,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"47c31997-f0f3-4bc9-9435-ab855e1f9d20"} +{"type":"user","message":{"role":"user","content":[{"type":"tool_result","content":"<tool_use_error>File has not been read yet. Read it first before writing to it.</tool_use_error>","is_error":true,"tool_use_id":"toolu_01DF8zTCKYgSKVD94PqBhP5T"}]},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"1fdc03ca-5ec3-4e47-a004-07d9678af0b7","timestamp":"2026-04-04T21:09:28.392Z","tool_use_result":"Error: File has not been read yet. Read it first before writing to it."} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01RMmJrMT3HQnfCHBDFnRAJd","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01Ueq7nXW2eRTqfJDhAkrvha","name":"Read","input":{"file_path":"/tmp/loop-bench-iuvnueix/tsconfig.json"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":357,"cache_read_input_tokens":52734,"cache_creation":{"ephemeral_5m_input_tokens":357,"ephemeral_1h_input_tokens":0},"output_tokens":60,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"21cf2b9f-211c-4c67-a913-e7d696f7a238"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01Ueq7nXW2eRTqfJDhAkrvha","type":"tool_result","content":"1\t{\n2\t \"compilerOptions\": {\n3\t \"target\": \"ES2017\",\n4\t \"module\": \"none\",\n5\t \"strict\": true,\n6\t \"outDir\": \"dist\",\n7\t \"noEmitOnError\": true,\n8\t \"lib\": [\"ES2017\", \"DOM\"]\n9\t },\n10\t \"include\": [\"src/**/*.ts\"]\n11\t}\n12\t"}]},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"d2232953-9d9a-458d-922d-c380b52c5cec","timestamp":"2026-04-04T21:09:29.884Z","tool_use_result":{"type":"text","file":{"filePath":"/tmp/loop-bench-iuvnueix/tsconfig.json","content":"{\n \"compilerOptions\": {\n \"target\": \"ES2017\",\n \"module\": \"none\",\n \"strict\": true,\n \"outDir\": \"dist\",\n \"noEmitOnError\": true,\n \"lib\": [\"ES2017\", \"DOM\"]\n },\n \"include\": [\"src/**/*.ts\"]\n}\n","numLines":12,"startLine":1,"totalLines":12}}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_015j5KKekW1gVu9o1MiCCAoW","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01CJoXFw8dibGeSctyFJxtsJ","name":"Edit","input":{"replace_all":false,"file_path":"/tmp/loop-bench-iuvnueix/tsconfig.json","old_string":"{\n \"compilerOptions\": {\n \"target\": \"ES2017\",\n \"module\": \"none\",\n \"strict\": true,\n \"outDir\": \"dist\",\n \"noEmitOnError\": true,\n \"lib\": [\"ES2017\", \"DOM\"]\n },\n \"include\": [\"src/**/*.ts\"]\n}","new_string":"{\n \"compilerOptions\": {\n \"target\": \"ES2017\",\n \"module\": \"none\",\n \"ignoreDeprecations\": \"6.0\",\n \"strict\": true,\n \"rootDir\": \"src\",\n \"outDir\": \"dist\",\n \"noEmitOnError\": true,\n \"lib\": [\"ES2017\", \"DOM\"]\n },\n \"include\": [\"src/**/*.ts\"]\n}"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":189,"cache_read_input_tokens":53091,"cache_creation":{"ephemeral_5m_input_tokens":189,"ephemeral_1h_input_tokens":0},"output_tokens":63,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"77298877-e56b-4ccd-9399-6dc36d80c328"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01CJoXFw8dibGeSctyFJxtsJ","type":"tool_result","content":"The file /tmp/loop-bench-iuvnueix/tsconfig.json has been updated successfully."}]},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"12fb7ebc-34ec-4d62-9fe9-26335a14fdc6","timestamp":"2026-04-04T21:09:32.960Z","tool_use_result":{"filePath":"/tmp/loop-bench-iuvnueix/tsconfig.json","oldString":"{\n \"compilerOptions\": {\n \"target\": \"ES2017\",\n \"module\": \"none\",\n \"strict\": true,\n \"outDir\": \"dist\",\n \"noEmitOnError\": true,\n \"lib\": [\"ES2017\", \"DOM\"]\n },\n \"include\": [\"src/**/*.ts\"]\n}","newString":"{\n \"compilerOptions\": {\n \"target\": \"ES2017\",\n \"module\": \"none\",\n \"ignoreDeprecations\": \"6.0\",\n \"strict\": true,\n \"rootDir\": \"src\",\n \"outDir\": \"dist\",\n \"noEmitOnError\": true,\n \"lib\": [\"ES2017\", \"DOM\"]\n },\n \"include\": [\"src/**/*.ts\"]\n}","originalFile":"{\n \"compilerOptions\": {\n \"target\": \"ES2017\",\n \"module\": \"none\",\n \"strict\": true,\n \"outDir\": \"dist\",\n \"noEmitOnError\": true,\n \"lib\": [\"ES2017\", \"DOM\"]\n },\n \"include\": [\"src/**/*.ts\"]\n}\n","structuredPatch":[{"oldStart":2,"oldLines":7,"newStart":2,"newLines":9,"lines":[" \"compilerOptions\": {"," \"target\": \"ES2017\","," \"module\": \"none\",","+ \"ignoreDeprecations\": \"6.0\","," \"strict\": true,","+ \"rootDir\": \"src\","," \"outDir\": \"dist\","," \"noEmitOnError\": true,"," \"lib\": [\"ES2017\", \"DOM\"]"]}],"userModified":false,"replaceAll":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01QqeAX7f7jcwVJd1SaCXPt5","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01HTqS9bsYNuQ8pmV1SeG4x4","name":"Bash","input":{"command":"node_modules/.bin/tsc 2>&1","description":"Compile TypeScript again with fixed config"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":346,"cache_read_input_tokens":53280,"cache_creation":{"ephemeral_5m_input_tokens":346,"ephemeral_1h_input_tokens":0},"output_tokens":52,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"34a58421-83a4-4538-8c26-7ec7d0eed088"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01HTqS9bsYNuQ8pmV1SeG4x4","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"578cdf09-774a-45bb-a58d-45a92d22bf87","timestamp":"2026-04-04T21:09:35.974Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_012VomGRDHjf9LGVardv5WSC","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01Er54NLSB3DMZoyiRnpVucy","name":"Bash","input":{"command":"ls -lh /tmp/loop-bench-iuvnueix/dist/","description":"Verify compiled output exists"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":105,"cache_read_input_tokens":53626,"cache_creation":{"ephemeral_5m_input_tokens":105,"ephemeral_1h_input_tokens":0},"output_tokens":53,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"bdfe6bfc-06c8-4dbe-8454-d9e5cc4fb2ae"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01Er54NLSB3DMZoyiRnpVucy","type":"tool_result","content":"total 24K\n-rw-r--r-- 1 root root 23K Apr 4 23:09 tetris.js","is_error":false}]},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"2d48efc3-43ce-4ca4-aabe-cde03a2493d5","timestamp":"2026-04-04T21:09:37.682Z","tool_use_result":{"stdout":"total 24K\n-rw-r--r-- 1 root root 23K Apr 4 23:09 tetris.js","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01GfiH53sri6u8PSycXVwZW9","type":"message","role":"assistant","content":[{"type":"text","text":"Compiled successfully. Let me verify the output looks correct and check a few key sections:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":241,"cache_read_input_tokens":53626,"cache_creation":{"ephemeral_5m_input_tokens":241,"ephemeral_1h_input_tokens":0},"output_tokens":2,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"26213d80-3660-442f-87df-529a1f30fb0e"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01GfiH53sri6u8PSycXVwZW9","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01BmTJTeAHoFAWhnhoyCtxQ9","name":"Read","input":{"file_path":"/tmp/loop-bench-iuvnueix/dist/tetris.js","limit":30},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":241,"cache_read_input_tokens":53626,"cache_creation":{"ephemeral_5m_input_tokens":241,"ephemeral_1h_input_tokens":0},"output_tokens":2,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"b8b35052-a20e-41c2-a923-0b42f19f845d"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01BmTJTeAHoFAWhnhoyCtxQ9","type":"tool_result","content":"1\t\"use strict\";\n2\t// ─── Constants ────────────────────────────────────────────────────────────────\n3\tconst COLS = 10;\n4\tconst ROWS = 20;\n5\tconst CELL = 32; // px per grid cell\n6\tconst DAS = 170; // Delayed Auto Shift (ms)\n7\tconst ARR = 50; // Auto Repeat Rate (ms)\n8\tconst FLASH_MS = 280; // Line-clear flash duration (ms)\n9\tconst LOCK_DELAY = 500; // Lock delay after piece hits floor (ms)\n10\t// ─── Piece Definitions ────────────────────────────────────────────────────────\n11\tconst COLORS = {\n12\t I: '#00c8ff',\n13\t O: '#ffd700',\n14\t T: '#c040f0',\n15\t S: '#10e060',\n16\t Z: '#ff3030',\n17\t J: '#3878ff',\n18\t L: '#ff8800',\n19\t};\n20\t// All pieces use square bounding boxes (2×2, 3×3, or 4×4)\n21\tconst SHAPES = {\n22\t I: [[0, 0, 0, 0],\n23\t [1, 1, 1, 1],\n24\t [0, 0, 0, 0],\n25\t [0, 0, 0, 0]],\n26\t O: [[1, 1],\n27\t [1, 1]],\n28\t T: [[0, 1, 0],\n29\t [1, 1, 1],\n30\t [0, 0, 0]],"}]},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"f06f8fe6-15f8-4485-b375-6f66dfffc023","timestamp":"2026-04-04T21:09:39.815Z","tool_use_result":{"type":"text","file":{"filePath":"/tmp/loop-bench-iuvnueix/dist/tetris.js","content":"\"use strict\";\n// ─── Constants ────────────────────────────────────────────────────────────────\nconst COLS = 10;\nconst ROWS = 20;\nconst CELL = 32; // px per grid cell\nconst DAS = 170; // Delayed Auto Shift (ms)\nconst ARR = 50; // Auto Repeat Rate (ms)\nconst FLASH_MS = 280; // Line-clear flash duration (ms)\nconst LOCK_DELAY = 500; // Lock delay after piece hits floor (ms)\n// ─── Piece Definitions ────────────────────────────────────────────────────────\nconst COLORS = {\n I: '#00c8ff',\n O: '#ffd700',\n T: '#c040f0',\n S: '#10e060',\n Z: '#ff3030',\n J: '#3878ff',\n L: '#ff8800',\n};\n// All pieces use square bounding boxes (2×2, 3×3, or 4×4)\nconst SHAPES = {\n I: [[0, 0, 0, 0],\n [1, 1, 1, 1],\n [0, 0, 0, 0],\n [0, 0, 0, 0]],\n O: [[1, 1],\n [1, 1]],\n T: [[0, 1, 0],\n [1, 1, 1],\n [0, 0, 0]],","numLines":30,"startLine":1,"totalLines":575}}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01KtjqTNWtVW743fGYxCRc3p","type":"message","role":"assistant","content":[{"type":"text","text":"Good. Let me also do a quick sanity check on the lock logic in the TS source to make sure the lock timer accumulation isn't double-counting:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":550,"cache_read_input_tokens":53867,"cache_creation":{"ephemeral_5m_input_tokens":550,"ephemeral_1h_input_tokens":0},"output_tokens":2,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"73792056-0c8f-42a5-80ec-40d9f8556141"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01KtjqTNWtVW743fGYxCRc3p","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_011jFriyJ1mZwfLUNLGhjtj9","name":"Read","input":{"file_path":"/tmp/loop-bench-iuvnueix/src/tetris.ts","offset":280,"limit":80},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":550,"cache_read_input_tokens":53867,"cache_creation":{"ephemeral_5m_input_tokens":550,"ephemeral_1h_input_tokens":0},"output_tokens":2,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"808b0a87-703f-419e-a744-bb5eadd526ea"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_011jFriyJ1mZwfLUNLGhjtj9","type":"tool_result","content":"280\t\n281\t private onFirstPress(code: string): void {\n282\t if (code === 'KeyP') {\n283\t if (!this.gameOver) this.paused = !this.paused;\n284\t return;\n285\t }\n286\t\n287\t if (this.gameOver) {\n288\t if (code === 'Enter' || code === 'KeyR') this.init();\n289\t return;\n290\t }\n291\t\n292\t if (this.paused || this.flashTimer > 0) return;\n293\t\n294\t switch (code) {\n295\t case 'ArrowLeft':\n296\t this.move(-1);\n297\t this.dasTimer = 0;\n298\t this.arrTimer = 0;\n299\t break;\n300\t case 'ArrowRight':\n301\t this.move(1);\n302\t this.dasTimer = 0;\n303\t this.arrTimer = 0;\n304\t break;\n305\t case 'ArrowDown': this.softDrop(); break;\n306\t case 'ArrowUp': this.rotate(1); break;\n307\t case 'KeyZ': this.rotate(-1); break;\n308\t case 'KeyX': this.rotate(1); break;\n309\t case 'Space': this.hardDrop(); break;\n310\t case 'ShiftLeft':\n311\t case 'ShiftRight':\n312\t case 'KeyC': this.holdPiece(); break;\n313\t }\n314\t }\n315\t\n316\t // ── Actions ─────────────────────────────────────────────────────────────────\n317\t\n318\t private move(dx: number): boolean {\n319\t if (fits(this.board, this.current, dx)) {\n320\t this.current.x += dx;\n321\t if (this.onFloor) this.lockTimer = 0; // reset lock delay on successful move\n322\t return true;\n323\t }\n324\t return false;\n325\t }\n326\t\n327\t private softDrop(): void {\n328\t if (fits(this.board, this.current, 0, 1)) {\n329\t this.current.y++;\n330\t this.score++;\n331\t this.dropCounter = 0;\n332\t this.lockTimer = 0;\n333\t this.updateHUD();\n334\t }\n335\t }\n336\t\n337\t private hardDrop(): void {\n338\t let n = 0;\n339\t while (fits(this.board, this.current, 0, 1)) {\n340\t this.current.y++;\n341\t n++;\n342\t }\n343\t this.score += n * 2;\n344\t this.lock();\n345\t }\n346\t\n347\t private rotate(dir: 1 | -1): void {\n348\t const rotated = dir === 1 ? rotateCW(this.current.shape) : rotateCCW(this.current.shape);\n349\t // Wall-kick offsets: try center, then nudge left/right, then further\n350\t const kicks = [0, -1, 1, -2, 2];\n351\t for (const kick of kicks) {\n352\t if (fits(this.board, this.current, kick, 0, rotated)) {\n353\t this.current.shape = rotated;\n354\t this.current.x += kick;\n355\t if (this.onFloor) this.lockTimer = 0; // reset lock delay on successful rotate\n356\t return;\n357\t }\n358\t }\n359\t }"}]},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"495926f4-adbb-4ba3-84ba-1e59fe163bbd","timestamp":"2026-04-04T21:09:42.435Z","tool_use_result":{"type":"text","file":{"filePath":"/tmp/loop-bench-iuvnueix/src/tetris.ts","content":"\n private onFirstPress(code: string): void {\n if (code === 'KeyP') {\n if (!this.gameOver) this.paused = !this.paused;\n return;\n }\n\n if (this.gameOver) {\n if (code === 'Enter' || code === 'KeyR') this.init();\n return;\n }\n\n if (this.paused || this.flashTimer > 0) return;\n\n switch (code) {\n case 'ArrowLeft':\n this.move(-1);\n this.dasTimer = 0;\n this.arrTimer = 0;\n break;\n case 'ArrowRight':\n this.move(1);\n this.dasTimer = 0;\n this.arrTimer = 0;\n break;\n case 'ArrowDown': this.softDrop(); break;\n case 'ArrowUp': this.rotate(1); break;\n case 'KeyZ': this.rotate(-1); break;\n case 'KeyX': this.rotate(1); break;\n case 'Space': this.hardDrop(); break;\n case 'ShiftLeft':\n case 'ShiftRight':\n case 'KeyC': this.holdPiece(); break;\n }\n }\n\n // ── Actions ─────────────────────────────────────────────────────────────────\n\n private move(dx: number): boolean {\n if (fits(this.board, this.current, dx)) {\n this.current.x += dx;\n if (this.onFloor) this.lockTimer = 0; // reset lock delay on successful move\n return true;\n }\n return false;\n }\n\n private softDrop(): void {\n if (fits(this.board, this.current, 0, 1)) {\n this.current.y++;\n this.score++;\n this.dropCounter = 0;\n this.lockTimer = 0;\n this.updateHUD();\n }\n }\n\n private hardDrop(): void {\n let n = 0;\n while (fits(this.board, this.current, 0, 1)) {\n this.current.y++;\n n++;\n }\n this.score += n * 2;\n this.lock();\n }\n\n private rotate(dir: 1 | -1): void {\n const rotated = dir === 1 ? rotateCW(this.current.shape) : rotateCCW(this.current.shape);\n // Wall-kick offsets: try center, then nudge left/right, then further\n const kicks = [0, -1, 1, -2, 2];\n for (const kick of kicks) {\n if (fits(this.board, this.current, kick, 0, rotated)) {\n this.current.shape = rotated;\n this.current.x += kick;\n if (this.onFloor) this.lockTimer = 0; // reset lock delay on successful rotate\n return;\n }\n }\n }","numLines":80,"startLine":280,"totalLines":670}}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01G2LY8Lv9GQfyXc6jRNg8vy","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01FtREB1izNVqvccqGPY24f5","name":"Read","input":{"file_path":"/tmp/loop-bench-iuvnueix/src/tetris.ts","offset":460,"limit":80},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":1094,"cache_read_input_tokens":54417,"cache_creation":{"ephemeral_5m_input_tokens":1094,"ephemeral_1h_input_tokens":0},"output_tokens":36,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"ef04ca44-e0a0-4d37-ab8f-0855f08a5194"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01FtREB1izNVqvccqGPY24f5","type":"tool_result","content":"460\t ctx.fillStyle = 'rgba(0,0,0,0.35)';\n461\t ctx.fillRect(px + m, py + CELL - m - 3, s, 3); // bottom edge\n462\t ctx.fillRect(px + CELL - m - 3, py + m, 3, s); // right edge\n463\t\n464\t ctx.globalAlpha = 1;\n465\t }\n466\t\n467\t private renderBoard(): void {\n468\t const ctx = this.boardCtx;\n469\t const W = this.boardCanvas.width;\n470\t const H = this.boardCanvas.height;\n471\t\n472\t // Background\n473\t ctx.fillStyle = '#0d1117';\n474\t ctx.fillRect(0, 0, W, H);\n475\t\n476\t // Subtle grid lines\n477\t ctx.strokeStyle = 'rgba(255,255,255,0.04)';\n478\t ctx.lineWidth = 1;\n479\t for (let c = 0; c <= COLS; c++) {\n480\t ctx.beginPath(); ctx.moveTo(c * CELL, 0); ctx.lineTo(c * CELL, H); ctx.stroke();\n481\t }\n482\t for (let r = 0; r <= ROWS; r++) {\n483\t ctx.beginPath(); ctx.moveTo(0, r * CELL); ctx.lineTo(W, r * CELL); ctx.stroke();\n484\t }\n485\t\n486\t // Placed board cells (with flash effect on clearing rows)\n487\t for (let r = 0; r < ROWS; r++) {\n488\t for (let c = 0; c < COLS; c++) {\n489\t const cell = this.board[r][c];\n490\t if (!cell) continue;\n491\t\n492\t if (this.flashRows.includes(r)) {\n493\t // Alternate white / colour every ~35ms\n494\t const t = this.flashTimer / FLASH_MS;\n495\t const flash = Math.floor(t * 8) % 2 === 0;\n496\t this.drawBlock(ctx, c, r, flash ? '#ffffff' : cell);\n497\t } else {\n498\t this.drawBlock(ctx, c, r, cell);\n499\t }\n500\t }\n501\t }\n502\t\n503\t // Ghost & active piece — hidden during flash animation\n504\t if (this.flashTimer <= 0 && !this.gameOver) {\n505\t const gDy = this.ghostOffset();\n506\t\n507\t // Ghost\n508\t if (gDy > 0) {\n509\t this.current.shape.forEach((row, r) => {\n510\t row.forEach((cell, c) => {\n511\t const gy = this.current.y + r + gDy;\n512\t if (cell && gy >= 0) {\n513\t this.drawBlock(ctx, this.current.x + c, gy, this.current.color, 0.18);\n514\t }\n515\t });\n516\t });\n517\t }\n518\t\n519\t // Active piece\n520\t this.current.shape.forEach((row, r) => {\n521\t row.forEach((cell, c) => {\n522\t const py = this.current.y + r;\n523\t if (cell && py >= 0) {\n524\t this.drawBlock(ctx, this.current.x + c, py, this.current.color);\n525\t }\n526\t });\n527\t });\n528\t }\n529\t\n530\t // ── Overlays ──────────────────────────────────────────────────────────────\n531\t\n532\t if (this.paused) {\n533\t ctx.fillStyle = 'rgba(0,0,0,0.72)';\n534\t ctx.fillRect(0, 0, W, H);\n535\t this.cText(ctx, 'PAUSED', W/2, H/2 - 22, '#ffffff', 'bold 34px monospace');\n536\t this.cText(ctx, 'P — resume', W/2, H/2 + 18, '#778899', '15px monospace');\n537\t }\n538\t\n539\t if (this.gameOver) {"}]},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"f3552792-6ea3-41f3-b770-a7769f1d5ea6","timestamp":"2026-04-04T21:09:44.420Z","tool_use_result":{"type":"text","file":{"filePath":"/tmp/loop-bench-iuvnueix/src/tetris.ts","content":" ctx.fillStyle = 'rgba(0,0,0,0.35)';\n ctx.fillRect(px + m, py + CELL - m - 3, s, 3); // bottom edge\n ctx.fillRect(px + CELL - m - 3, py + m, 3, s); // right edge\n\n ctx.globalAlpha = 1;\n }\n\n private renderBoard(): void {\n const ctx = this.boardCtx;\n const W = this.boardCanvas.width;\n const H = this.boardCanvas.height;\n\n // Background\n ctx.fillStyle = '#0d1117';\n ctx.fillRect(0, 0, W, H);\n\n // Subtle grid lines\n ctx.strokeStyle = 'rgba(255,255,255,0.04)';\n ctx.lineWidth = 1;\n for (let c = 0; c <= COLS; c++) {\n ctx.beginPath(); ctx.moveTo(c * CELL, 0); ctx.lineTo(c * CELL, H); ctx.stroke();\n }\n for (let r = 0; r <= ROWS; r++) {\n ctx.beginPath(); ctx.moveTo(0, r * CELL); ctx.lineTo(W, r * CELL); ctx.stroke();\n }\n\n // Placed board cells (with flash effect on clearing rows)\n for (let r = 0; r < ROWS; r++) {\n for (let c = 0; c < COLS; c++) {\n const cell = this.board[r][c];\n if (!cell) continue;\n\n if (this.flashRows.includes(r)) {\n // Alternate white / colour every ~35ms\n const t = this.flashTimer / FLASH_MS;\n const flash = Math.floor(t * 8) % 2 === 0;\n this.drawBlock(ctx, c, r, flash ? '#ffffff' : cell);\n } else {\n this.drawBlock(ctx, c, r, cell);\n }\n }\n }\n\n // Ghost & active piece — hidden during flash animation\n if (this.flashTimer <= 0 && !this.gameOver) {\n const gDy = this.ghostOffset();\n\n // Ghost\n if (gDy > 0) {\n this.current.shape.forEach((row, r) => {\n row.forEach((cell, c) => {\n const gy = this.current.y + r + gDy;\n if (cell && gy >= 0) {\n this.drawBlock(ctx, this.current.x + c, gy, this.current.color, 0.18);\n }\n });\n });\n }\n\n // Active piece\n this.current.shape.forEach((row, r) => {\n row.forEach((cell, c) => {\n const py = this.current.y + r;\n if (cell && py >= 0) {\n this.drawBlock(ctx, this.current.x + c, py, this.current.color);\n }\n });\n });\n }\n\n // ── Overlays ──────────────────────────────────────────────────────────────\n\n if (this.paused) {\n ctx.fillStyle = 'rgba(0,0,0,0.72)';\n ctx.fillRect(0, 0, W, H);\n this.cText(ctx, 'PAUSED', W/2, H/2 - 22, '#ffffff', 'bold 34px monospace');\n this.cText(ctx, 'P — resume', W/2, H/2 + 18, '#778899', '15px monospace');\n }\n\n if (this.gameOver) {","numLines":80,"startLine":460,"totalLines":670}}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01GN13bqkXWERxV59U2CW3ki","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_011GBGgjyN72ZRf7RZ35mjxM","name":"Read","input":{"file_path":"/tmp/loop-bench-iuvnueix/src/tetris.ts","offset":570,"limit":100},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":2314,"cache_read_input_tokens":54417,"cache_creation":{"ephemeral_5m_input_tokens":2314,"ephemeral_1h_input_tokens":0},"output_tokens":36,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"5592d57a-c80e-44a8-913b-5f919e39c0e4"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_011GBGgjyN72ZRf7RZ35mjxM","type":"tool_result","content":"570\t ctx.fillRect(0, 0, canvas.width, canvas.height);\n571\t if (!piece) return;\n572\t\n573\t const s = piece.shape;\n574\t const ox = Math.floor((4 - s[0].length) / 2);\n575\t const oy = Math.floor((4 - s.length) / 2);\n576\t s.forEach((row, r) => {\n577\t row.forEach((cell, c) => {\n578\t if (cell) this.drawBlock(ctx, ox + c, oy + r, piece.color, dimmed ? 0.38 : 1);\n579\t });\n580\t });\n581\t }\n582\t\n583\t // ── Game Loop ────────────────────────────────────────────────────────────────\n584\t\n585\t private loop(time: number): void {\n586\t const dt = Math.min(time - (this.lastTime || time), 100); // cap at 100ms\n587\t this.lastTime = time;\n588\t\n589\t if (!this.paused && !this.gameOver) {\n590\t // ── Flash timer (line-clear animation) ──────────────────────────────────\n591\t if (this.flashTimer > 0) {\n592\t this.flashTimer -= dt;\n593\t if (this.flashTimer <= 0) {\n594\t this.flashTimer = 0;\n595\t this.board = removeRows(this.board, this.flashRows);\n596\t this.flashRows = [];\n597\t this.lines += this.pendingClr;\n598\t this.score += this.pendingScr;\n599\t this.level = Math.floor(this.lines / 10) + 1;\n600\t this.pendingClr = 0;\n601\t this.pendingScr = 0;\n602\t this.spawnNext();\n603\t }\n604\t } else {\n605\t // ── DAS / ARR horizontal movement ─────────────────────────────────────\n606\t const leftDown = this.keysDown.has('ArrowLeft');\n607\t const rightDown = this.keysDown.has('ArrowRight');\n608\t\n609\t if (leftDown || rightDown) {\n610\t this.dasTimer += dt;\n611\t if (this.dasTimer > DAS) {\n612\t this.arrTimer += dt;\n613\t if (this.arrTimer >= ARR) {\n614\t const steps = Math.floor(this.arrTimer / ARR);\n615\t this.arrTimer -= steps * ARR;\n616\t for (let i = 0; i < steps; i++) {\n617\t if (leftDown && !rightDown) this.move(-1);\n618\t if (rightDown && !leftDown) this.move(1);\n619\t }\n620\t }\n621\t }\n622\t }\n623\t\n624\t // ── Gravity ───────────────────────────────────────────────────────────\n625\t const softDropping = this.keysDown.has('ArrowDown');\n626\t const interval = softDropping ? 50 : dropInterval(this.level);\n627\t\n628\t this.dropCounter += dt;\n629\t if (this.dropCounter >= interval) {\n630\t this.dropCounter = 0;\n631\t if (fits(this.board, this.current, 0, 1)) {\n632\t this.current.y++;\n633\t this.onFloor = false;\n634\t if (softDropping) {\n635\t this.score++;\n636\t this.updateHUD();\n637\t }\n638\t } else {\n639\t // Piece is resting on something — start lock delay\n640\t this.onFloor = true;\n641\t this.lockTimer += dt + interval; // add the elapsed time\n642\t if (this.lockTimer >= LOCK_DELAY) {\n643\t this.lock();\n644\t }\n645\t }\n646\t }\n647\t\n648\t // Accumulate lock delay even between gravity ticks\n649\t if (this.onFloor && !fits(this.board, this.current, 0, 1)) {\n650\t this.lockTimer += dt;\n651\t if (this.lockTimer >= LOCK_DELAY) {\n652\t this.lock();\n653\t }\n654\t }\n655\t }\n656\t }\n657\t\n658\t // ── Render ────────────────────────────────────────────────────────────────\n659\t this.renderBoard();\n660\t this.renderPreview(this.nextCtx, this.nextCanvas, this.next);\n661\t this.renderPreview(this.holdCtx, this.holdCanvas, this.held, !this.canHold);\n662\t\n663\t requestAnimationFrame(t => this.loop(t));\n664\t }\n665\t}\n666\t\n667\t// ─── Bootstrap ───────────────────────────────────────────────────────────────\n668\t\n669\twindow.addEventListener('load', () => new TetrisGame());"}]},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"e6827b7b-d900-4c0d-b236-c54db0566e46","timestamp":"2026-04-04T21:09:46.786Z","tool_use_result":{"type":"text","file":{"filePath":"/tmp/loop-bench-iuvnueix/src/tetris.ts","content":" ctx.fillRect(0, 0, canvas.width, canvas.height);\n if (!piece) return;\n\n const s = piece.shape;\n const ox = Math.floor((4 - s[0].length) / 2);\n const oy = Math.floor((4 - s.length) / 2);\n s.forEach((row, r) => {\n row.forEach((cell, c) => {\n if (cell) this.drawBlock(ctx, ox + c, oy + r, piece.color, dimmed ? 0.38 : 1);\n });\n });\n }\n\n // ── Game Loop ────────────────────────────────────────────────────────────────\n\n private loop(time: number): void {\n const dt = Math.min(time - (this.lastTime || time), 100); // cap at 100ms\n this.lastTime = time;\n\n if (!this.paused && !this.gameOver) {\n // ── Flash timer (line-clear animation) ──────────────────────────────────\n if (this.flashTimer > 0) {\n this.flashTimer -= dt;\n if (this.flashTimer <= 0) {\n this.flashTimer = 0;\n this.board = removeRows(this.board, this.flashRows);\n this.flashRows = [];\n this.lines += this.pendingClr;\n this.score += this.pendingScr;\n this.level = Math.floor(this.lines / 10) + 1;\n this.pendingClr = 0;\n this.pendingScr = 0;\n this.spawnNext();\n }\n } else {\n // ── DAS / ARR horizontal movement ─────────────────────────────────────\n const leftDown = this.keysDown.has('ArrowLeft');\n const rightDown = this.keysDown.has('ArrowRight');\n\n if (leftDown || rightDown) {\n this.dasTimer += dt;\n if (this.dasTimer > DAS) {\n this.arrTimer += dt;\n if (this.arrTimer >= ARR) {\n const steps = Math.floor(this.arrTimer / ARR);\n this.arrTimer -= steps * ARR;\n for (let i = 0; i < steps; i++) {\n if (leftDown && !rightDown) this.move(-1);\n if (rightDown && !leftDown) this.move(1);\n }\n }\n }\n }\n\n // ── Gravity ───────────────────────────────────────────────────────────\n const softDropping = this.keysDown.has('ArrowDown');\n const interval = softDropping ? 50 : dropInterval(this.level);\n\n this.dropCounter += dt;\n if (this.dropCounter >= interval) {\n this.dropCounter = 0;\n if (fits(this.board, this.current, 0, 1)) {\n this.current.y++;\n this.onFloor = false;\n if (softDropping) {\n this.score++;\n this.updateHUD();\n }\n } else {\n // Piece is resting on something — start lock delay\n this.onFloor = true;\n this.lockTimer += dt + interval; // add the elapsed time\n if (this.lockTimer >= LOCK_DELAY) {\n this.lock();\n }\n }\n }\n\n // Accumulate lock delay even between gravity ticks\n if (this.onFloor && !fits(this.board, this.current, 0, 1)) {\n this.lockTimer += dt;\n if (this.lockTimer >= LOCK_DELAY) {\n this.lock();\n }\n }\n }\n }\n\n // ── Render ────────────────────────────────────────────────────────────────\n this.renderBoard();\n this.renderPreview(this.nextCtx, this.nextCanvas, this.next);\n this.renderPreview(this.holdCtx, this.holdCanvas, this.held, !this.canHold);\n\n requestAnimationFrame(t => this.loop(t));\n }\n}\n\n// ─── Bootstrap ───────────────────────────────────────────────────────────────\n\nwindow.addEventListener('load', () => new TetrisGame());","numLines":100,"startLine":570,"totalLines":670}}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01KKZb6AuzQzQW369zXXNzw1","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"I see a potential issue with the lock delay logic. In the gravity tick section, when the piece can't move down, I have:\n\n```typescript\nthis.onFloor = true;\nthis.lockTimer += dt + interval; // add the elapsed time\nif (this.lockTimer >= LOCK_DELAY) {\n this.lock();\n}\n```\n\nAnd then outside the gravity tick check:\n\n```typescript\n// Accumulate lock delay even between gravity ticks\nif (this.onFloor && !fits(this.board, this.current, 0, 1)) {\n this.lockTimer += dt;\n if (this.lockTimer >= LOCK_DELAY) {\n this.lock();\n }\n}\n```\n\nI'm double-counting the timer accumulation—the first block runs when a gravity tick fires and adds `dt + interval`, while the second block runs every frame and adds `dt` again, so on gravity tick frames I'm adding `dt` twice.\n\nLet me trace through this more carefully. When a gravity tick fires and the piece can't move down, the first block adds `dt + interval` to lockTimer, then immediately after, the second block adds another `dt`. That means lockTimer gets `2*dt + interval` in a single frame, which is definitely wrong.\n\nThe fix is to only accumulate lock time in one place. I'll remove the lock timer logic from the gravity tick block and let the second block handle it exclusively—that way lockTimer only gets `dt` added each frame, which is the correct behavior. I need to trace through the lock logic more carefully. The issue is whether `onFloor` gets reset during the lock process—I'm checking the actual code to see if `lock()` sets it to false directly, or if that only happens in `spawnNext()`. Looking at the implementation, `lock()` does set `onFloor = false` immediately, which prevents the second block from triggering again in the same frame even if there are full rows to clear. I'm tracing through the frame where gravity fires and the piece hits bottom to verify there's no double-counting: the `onFloor` flag gets set to true when the piece can't move down, then `lockTimer` accumulates, and once it exceeds the lock delay threshold, `lock()` executes and resets `onFloor` back to false. If the lock doesn't fire yet, the condition continues to check whether `onFloor` is still true.\n\nI see the issue now—in that first frame when the piece hits the floor, `lockTimer` gets incremented twice: once with `dt + interval` in the gravity tick, then again with just `dt` in the second block. The fix is to remove the accumulation from the gravity tick and let the second block handle all the timer updates consistently.\n\nUpdating the code...","signature":"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"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":1427,"cache_read_input_tokens":56731,"cache_creation":{"ephemeral_5m_input_tokens":1427,"ephemeral_1h_input_tokens":0},"output_tokens":8,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"ada40126-4588-4758-be04-7e0abb175a53"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01KKZb6AuzQzQW369zXXNzw1","type":"message","role":"assistant","content":[{"type":"text","text":"I notice the lock-delay logic has a double-counting bug — when a gravity tick fires and the piece hits the floor, `lockTimer` gets `dt` added twice in the same frame. Let me fix that, and also tighten a few edge cases:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":1427,"cache_read_input_tokens":56731,"cache_creation":{"ephemeral_5m_input_tokens":1427,"ephemeral_1h_input_tokens":0},"output_tokens":8,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"94f38c7a-13c2-49a0-a655-50bab8e59064"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01KKZb6AuzQzQW369zXXNzw1","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01PCju5FkTtrhdi1t4ynqm7a","name":"Read","input":{"file_path":"/tmp/loop-bench-iuvnueix/src/tetris.ts","offset":623,"limit":35},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":1427,"cache_read_input_tokens":56731,"cache_creation":{"ephemeral_5m_input_tokens":1427,"ephemeral_1h_input_tokens":0},"output_tokens":8,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"ad3e22a6-fde1-4292-b95d-3cb2271e5b41"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01PCju5FkTtrhdi1t4ynqm7a","type":"tool_result","content":"623\t\n624\t // ── Gravity ───────────────────────────────────────────────────────────\n625\t const softDropping = this.keysDown.has('ArrowDown');\n626\t const interval = softDropping ? 50 : dropInterval(this.level);\n627\t\n628\t this.dropCounter += dt;\n629\t if (this.dropCounter >= interval) {\n630\t this.dropCounter = 0;\n631\t if (fits(this.board, this.current, 0, 1)) {\n632\t this.current.y++;\n633\t this.onFloor = false;\n634\t if (softDropping) {\n635\t this.score++;\n636\t this.updateHUD();\n637\t }\n638\t } else {\n639\t // Piece is resting on something — start lock delay\n640\t this.onFloor = true;\n641\t this.lockTimer += dt + interval; // add the elapsed time\n642\t if (this.lockTimer >= LOCK_DELAY) {\n643\t this.lock();\n644\t }\n645\t }\n646\t }\n647\t\n648\t // Accumulate lock delay even between gravity ticks\n649\t if (this.onFloor && !fits(this.board, this.current, 0, 1)) {\n650\t this.lockTimer += dt;\n651\t if (this.lockTimer >= LOCK_DELAY) {\n652\t this.lock();\n653\t }\n654\t }\n655\t }\n656\t }\n657\t"}]},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"eaca3786-9331-4c4c-baae-b258ff2d2069","timestamp":"2026-04-04T21:10:16.037Z","tool_use_result":{"type":"text","file":{"filePath":"/tmp/loop-bench-iuvnueix/src/tetris.ts","content":"\n // ── Gravity ───────────────────────────────────────────────────────────\n const softDropping = this.keysDown.has('ArrowDown');\n const interval = softDropping ? 50 : dropInterval(this.level);\n\n this.dropCounter += dt;\n if (this.dropCounter >= interval) {\n this.dropCounter = 0;\n if (fits(this.board, this.current, 0, 1)) {\n this.current.y++;\n this.onFloor = false;\n if (softDropping) {\n this.score++;\n this.updateHUD();\n }\n } else {\n // Piece is resting on something — start lock delay\n this.onFloor = true;\n this.lockTimer += dt + interval; // add the elapsed time\n if (this.lockTimer >= LOCK_DELAY) {\n this.lock();\n }\n }\n }\n\n // Accumulate lock delay even between gravity ticks\n if (this.onFloor && !fits(this.board, this.current, 0, 1)) {\n this.lockTimer += dt;\n if (this.lockTimer >= LOCK_DELAY) {\n this.lock();\n }\n }\n }\n }\n","numLines":35,"startLine":623,"totalLines":670}}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01Jd1GCDrv6w6BHMtsWZb41G","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01Pib1GNR5FNCfyFbE23HSiT","name":"Edit","input":{"replace_all":false,"file_path":"/tmp/loop-bench-iuvnueix/src/tetris.ts","old_string":" // ── Gravity ───────────────────────────────────────────────────────────\n const softDropping = this.keysDown.has('ArrowDown');\n const interval = softDropping ? 50 : dropInterval(this.level);\n\n this.dropCounter += dt;\n if (this.dropCounter >= interval) {\n this.dropCounter = 0;\n if (fits(this.board, this.current, 0, 1)) {\n this.current.y++;\n this.onFloor = false;\n if (softDropping) {\n this.score++;\n this.updateHUD();\n }\n } else {\n // Piece is resting on something — start lock delay\n this.onFloor = true;\n this.lockTimer += dt + interval; // add the elapsed time\n if (this.lockTimer >= LOCK_DELAY) {\n this.lock();\n }\n }\n }\n\n // Accumulate lock delay even between gravity ticks\n if (this.onFloor && !fits(this.board, this.current, 0, 1)) {\n this.lockTimer += dt;\n if (this.lockTimer >= LOCK_DELAY) {\n this.lock();\n }\n }","new_string":" // ── Gravity ───────────────────────────────────────────────────────────\n const softDropping = this.keysDown.has('ArrowDown');\n const interval = softDropping ? 50 : dropInterval(this.level);\n\n this.dropCounter += dt;\n if (this.dropCounter >= interval) {\n this.dropCounter = 0;\n if (fits(this.board, this.current, 0, 1)) {\n this.current.y++;\n this.onFloor = false;\n this.lockTimer = 0;\n if (softDropping) {\n this.score++;\n this.updateHUD();\n }\n } else {\n // Piece can't fall — mark as on floor; lock delay handled below\n this.onFloor = true;\n }\n }\n\n // Accumulate lock delay every frame while piece rests on something\n if (this.onFloor && !fits(this.board, this.current, 0, 1)) {\n this.lockTimer += dt;\n if (this.lockTimer >= LOCK_DELAY) {\n this.lock();\n }\n }"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":2281,"cache_read_input_tokens":58158,"cache_creation":{"ephemeral_5m_input_tokens":2281,"ephemeral_1h_input_tokens":0},"output_tokens":61,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"b42573b6-2e59-46a6-8722-364bb365b85c"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01Pib1GNR5FNCfyFbE23HSiT","type":"tool_result","content":"The file /tmp/loop-bench-iuvnueix/src/tetris.ts has been updated successfully."}]},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"f988133d-f36a-45b4-8b51-f2f67226431d","timestamp":"2026-04-04T21:10:23.223Z","tool_use_result":{"filePath":"/tmp/loop-bench-iuvnueix/src/tetris.ts","oldString":" // ── Gravity ───────────────────────────────────────────────────────────\n const softDropping = this.keysDown.has('ArrowDown');\n const interval = softDropping ? 50 : dropInterval(this.level);\n\n this.dropCounter += dt;\n if (this.dropCounter >= interval) {\n this.dropCounter = 0;\n if (fits(this.board, this.current, 0, 1)) {\n this.current.y++;\n this.onFloor = false;\n if (softDropping) {\n this.score++;\n this.updateHUD();\n }\n } else {\n // Piece is resting on something — start lock delay\n this.onFloor = true;\n this.lockTimer += dt + interval; // add the elapsed time\n if (this.lockTimer >= LOCK_DELAY) {\n this.lock();\n }\n }\n }\n\n // Accumulate lock delay even between gravity ticks\n if (this.onFloor && !fits(this.board, this.current, 0, 1)) {\n this.lockTimer += dt;\n if (this.lockTimer >= LOCK_DELAY) {\n this.lock();\n }\n }","newString":" // ── Gravity ───────────────────────────────────────────────────────────\n const softDropping = this.keysDown.has('ArrowDown');\n const interval = softDropping ? 50 : dropInterval(this.level);\n\n this.dropCounter += dt;\n if (this.dropCounter >= interval) {\n this.dropCounter = 0;\n if (fits(this.board, this.current, 0, 1)) {\n this.current.y++;\n this.onFloor = false;\n this.lockTimer = 0;\n if (softDropping) {\n this.score++;\n this.updateHUD();\n }\n } else {\n // Piece can't fall — mark as on floor; lock delay handled below\n this.onFloor = true;\n }\n }\n\n // Accumulate lock delay every frame while piece rests on something\n if (this.onFloor && !fits(this.board, this.current, 0, 1)) {\n this.lockTimer += dt;\n if (this.lockTimer >= LOCK_DELAY) {\n this.lock();\n }\n }","originalFile":"// ─── Constants ────────────────────────────────────────────────────────────────\n\nconst COLS = 10;\nconst ROWS = 20;\nconst CELL = 32; // px per grid cell\nconst DAS = 170; // Delayed Auto Shift (ms)\nconst ARR = 50; // Auto Repeat Rate (ms)\nconst FLASH_MS = 280; // Line-clear flash duration (ms)\nconst LOCK_DELAY = 500; // Lock delay after piece hits floor (ms)\n\n// ─── Piece Definitions ────────────────────────────────────────────────────────\n\nconst COLORS: Record<string, string> = {\n I: '#00c8ff',\n O: '#ffd700',\n T: '#c040f0',\n S: '#10e060',\n Z: '#ff3030',\n J: '#3878ff',\n L: '#ff8800',\n};\n\n// All pieces use square bounding boxes (2×2, 3×3, or 4×4)\nconst SHAPES: Record<string, number[][]> = {\n I: [[0,0,0,0],\n [1,1,1,1],\n [0,0,0,0],\n [0,0,0,0]],\n O: [[1,1],\n [1,1]],\n T: [[0,1,0],\n [1,1,1],\n [0,0,0]],\n S: [[0,1,1],\n [1,1,0],\n [0,0,0]],\n Z: [[1,1,0],\n [0,1,1],\n [0,0,0]],\n J: [[1,0,0],\n [1,1,1],\n [0,0,0]],\n L: [[0,0,1],\n [1,1,1],\n [0,0,0]],\n};\n\n// Standard speed curve (ms per gravity drop) for levels 1-13\nconst SPEED_CURVE = [800,717,633,550,467,383,300,217,133,100,83,67,50];\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ntype Cell = string | null;\ntype Board = Cell[][];\n\ninterface Piece {\n type: string;\n shape: number[][];\n color: string;\n x: number;\n y: number;\n}\n\n// ─── Utility Functions ────────────────────────────────────────────────────────\n\nfunction createBoard(): Board {\n return Array.from({ length: ROWS }, () => new Array<Cell>(COLS).fill(null));\n}\n\n/** Rotate matrix 90° clockwise */\nfunction rotateCW(m: number[][]): number[][] {\n return m[0].map((_, i) => m.map(row => row[i]).reverse());\n}\n\n/** Rotate matrix 90° counter-clockwise */\nfunction rotateCCW(m: number[][]): number[][] {\n return m[0].map((_, i) => m.map(row => row[row.length - 1 - i])).reverse();\n}\n\n/** Spawn a new piece centered at the top */\nfunction makePiece(type: string): Piece {\n const shape = SHAPES[type].map(r => [...r]);\n return {\n type,\n shape,\n color: COLORS[type],\n x: Math.floor((COLS - shape[0].length) / 2),\n y: 0,\n };\n}\n\n/** Returns true if the piece (optionally with offsets / alternate shape) fits on the board */\nfunction fits(board: Board, piece: Piece, dx = 0, dy = 0, shape = piece.shape): boolean {\n for (let r = 0; r < shape.length; r++) {\n for (let c = 0; c < shape[r].length; c++) {\n if (!shape[r][c]) continue;\n const nx = piece.x + c + dx;\n const ny = piece.y + r + dy;\n if (nx < 0 || nx >= COLS || ny >= ROWS) return false;\n if (ny >= 0 && board[ny][nx]) return false;\n }\n }\n return true;\n}\n\n/** Place the piece onto the board, returning a new board */\nfunction stamp(board: Board, piece: Piece): Board {\n const b = board.map(r => [...r]);\n piece.shape.forEach((row, r) => {\n row.forEach((cell, c) => {\n if (cell) {\n const ny = piece.y + r;\n const nx = piece.x + c;\n if (ny >= 0 && ny < ROWS && nx >= 0 && nx < COLS) b[ny][nx] = piece.color;\n }\n });\n });\n return b;\n}\n\n/** Return indices of completely filled rows */\nfunction fullRows(board: Board): number[] {\n return board.reduce<number[]>((acc, row, i) => {\n if (row.every(c => c !== null)) acc.push(i);\n return acc;\n }, []);\n}\n\n/** Remove the specified rows and prepend empty rows at the top */\nfunction removeRows(board: Board, rows: number[]): Board {\n const kept = board.filter((_, i) => !rows.includes(i));\n const empties = Array.from({ length: rows.length }, () => new Array<Cell>(COLS).fill(null));\n return [...empties, ...kept];\n}\n\nconst SCORE_TABLE = [0, 100, 300, 500, 800];\n\nfunction lineScore(n: number, level: number): number {\n return (SCORE_TABLE[n] ?? 0) * level;\n}\n\nfunction dropInterval(level: number): number {\n return SPEED_CURVE[Math.min(level - 1, SPEED_CURVE.length - 1)];\n}\n\n// ─── 7-Bag Randomiser ─────────────────────────────────────────────────────────\n\nclass Bag {\n private items: string[] = [];\n\n private refill(): void {\n this.items = Object.keys(SHAPES);\n for (let i = this.items.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [this.items[i], this.items[j]] = [this.items[j], this.items[i]];\n }\n }\n\n draw(): string {\n if (this.items.length === 0) this.refill();\n return this.items.pop()!;\n }\n}\n\n// ─── Main Game Class ──────────────────────────────────────────────────────────\n\nclass TetrisGame {\n // Canvases\n private readonly boardCanvas: HTMLCanvasElement;\n private readonly boardCtx: CanvasRenderingContext2D;\n private readonly nextCanvas: HTMLCanvasElement;\n private readonly nextCtx: CanvasRenderingContext2D;\n private readonly holdCanvas: HTMLCanvasElement;\n private readonly holdCtx: CanvasRenderingContext2D;\n\n // Game state\n private bag!: Bag;\n private board!: Board;\n private current!: Piece;\n private next!: Piece;\n private held: Piece | null = null;\n private canHold!: boolean;\n private score!: number;\n private lines!: number;\n private level!: number;\n private highScore: number;\n private gameOver!: boolean;\n private paused!: boolean;\n\n // Timing\n private lastTime = 0;\n private dropCounter!: number;\n private lockTimer!: number; // counts toward lock delay\n private onFloor!: boolean; // true when piece is resting on something\n\n // Line-clear flash\n private flashTimer = 0;\n private flashRows: number[] = [];\n private pendingClr = 0;\n private pendingScr = 0;\n\n // Input\n private keysDown = new Set<string>();\n private dasTimer = 0;\n private arrTimer = 0;\n\n // ── Constructor ─────────────────────────────────────────────────────────────\n\n constructor() {\n const get = <T extends HTMLElement>(id: string) =>\n document.getElementById(id) as T;\n\n this.boardCanvas = get<HTMLCanvasElement>('board');\n this.boardCtx = this.boardCanvas.getContext('2d')!;\n this.nextCanvas = get<HTMLCanvasElement>('next');\n this.nextCtx = this.nextCanvas.getContext('2d')!;\n this.holdCanvas = get<HTMLCanvasElement>('hold');\n this.holdCtx = this.holdCanvas.getContext('2d')!;\n\n this.boardCanvas.width = COLS * CELL;\n this.boardCanvas.height = ROWS * CELL;\n this.nextCanvas.width = 4 * CELL;\n this.nextCanvas.height = 4 * CELL;\n this.holdCanvas.width = 4 * CELL;\n this.holdCanvas.height = 4 * CELL;\n\n this.highScore = parseInt(localStorage.getItem('tetris-hs') ?? '0', 10);\n\n this.setupInput();\n this.init();\n requestAnimationFrame(t => this.loop(t));\n }\n\n // ── Initialisation ──────────────────────────────────────────────────────────\n\n private init(): void {\n this.bag = new Bag();\n this.board = createBoard();\n this.current = makePiece(this.bag.draw());\n this.next = makePiece(this.bag.draw());\n this.held = null;\n this.canHold = true;\n this.score = 0;\n this.lines = 0;\n this.level = 1;\n this.gameOver = false;\n this.paused = false;\n this.dropCounter = 0;\n this.lockTimer = 0;\n this.onFloor = false;\n this.flashTimer = 0;\n this.flashRows = [];\n this.pendingClr = 0;\n this.pendingScr = 0;\n this.updateHUD();\n }\n\n // ── Input ───────────────────────────────────────────────────────────────────\n\n private setupInput(): void {\n // Prevent page scroll for game keys\n window.addEventListener('keydown', e => {\n if (['ArrowUp','ArrowDown','ArrowLeft','ArrowRight','Space'].includes(e.code))\n e.preventDefault();\n\n if (!e.repeat) {\n this.keysDown.add(e.code);\n this.onFirstPress(e.code);\n }\n }, { passive: false });\n\n window.addEventListener('keyup', e => {\n this.keysDown.delete(e.code);\n if (e.code === 'ArrowLeft' || e.code === 'ArrowRight') {\n this.dasTimer = 0;\n this.arrTimer = 0;\n }\n });\n }\n\n private onFirstPress(code: string): void {\n if (code === 'KeyP') {\n if (!this.gameOver) this.paused = !this.paused;\n return;\n }\n\n if (this.gameOver) {\n if (code === 'Enter' || code === 'KeyR') this.init();\n return;\n }\n\n if (this.paused || this.flashTimer > 0) return;\n\n switch (code) {\n case 'ArrowLeft':\n this.move(-1);\n this.dasTimer = 0;\n this.arrTimer = 0;\n break;\n case 'ArrowRight':\n this.move(1);\n this.dasTimer = 0;\n this.arrTimer = 0;\n break;\n case 'ArrowDown': this.softDrop(); break;\n case 'ArrowUp': this.rotate(1); break;\n case 'KeyZ': this.rotate(-1); break;\n case 'KeyX': this.rotate(1); break;\n case 'Space': this.hardDrop(); break;\n case 'ShiftLeft':\n case 'ShiftRight':\n case 'KeyC': this.holdPiece(); break;\n }\n }\n\n // ── Actions ─────────────────────────────────────────────────────────────────\n\n private move(dx: number): boolean {\n if (fits(this.board, this.current, dx)) {\n this.current.x += dx;\n if (this.onFloor) this.lockTimer = 0; // reset lock delay on successful move\n return true;\n }\n return false;\n }\n\n private softDrop(): void {\n if (fits(this.board, this.current, 0, 1)) {\n this.current.y++;\n this.score++;\n this.dropCounter = 0;\n this.lockTimer = 0;\n this.updateHUD();\n }\n }\n\n private hardDrop(): void {\n let n = 0;\n while (fits(this.board, this.current, 0, 1)) {\n this.current.y++;\n n++;\n }\n this.score += n * 2;\n this.lock();\n }\n\n private rotate(dir: 1 | -1): void {\n const rotated = dir === 1 ? rotateCW(this.current.shape) : rotateCCW(this.current.shape);\n // Wall-kick offsets: try center, then nudge left/right, then further\n const kicks = [0, -1, 1, -2, 2];\n for (const kick of kicks) {\n if (fits(this.board, this.current, kick, 0, rotated)) {\n this.current.shape = rotated;\n this.current.x += kick;\n if (this.onFloor) this.lockTimer = 0; // reset lock delay on successful rotate\n return;\n }\n }\n }\n\n private holdPiece(): void {\n if (!this.canHold) return;\n const savedType = this.current.type;\n if (this.held) {\n this.current = makePiece(this.held.type);\n } else {\n this.current = this.next;\n this.next = makePiece(this.bag.draw());\n }\n this.held = makePiece(savedType);\n this.canHold = false;\n this.onFloor = false;\n this.lockTimer = 0;\n this.dropCounter = 0;\n }\n\n private lock(): void {\n this.board = stamp(this.board, this.current);\n this.canHold = true;\n this.onFloor = false;\n\n const fr = fullRows(this.board);\n if (fr.length > 0) {\n this.flashRows = fr;\n this.flashTimer = FLASH_MS;\n this.pendingClr = fr.length;\n this.pendingScr = lineScore(fr.length, this.level);\n } else {\n this.spawnNext();\n }\n }\n\n private spawnNext(): void {\n this.current = this.next;\n this.next = makePiece(this.bag.draw());\n this.dropCounter = 0;\n this.lockTimer = 0;\n this.onFloor = false;\n\n if (!fits(this.board, this.current)) {\n this.gameOver = true;\n if (this.score > this.highScore) {\n this.highScore = this.score;\n localStorage.setItem('tetris-hs', String(this.highScore));\n }\n }\n this.updateHUD();\n }\n\n // ── HUD ─────────────────────────────────────────────────────────────────────\n\n private updateHUD(): void {\n const el = (id: string) => document.getElementById(id)!;\n el('score').textContent = this.score.toLocaleString();\n el('lines').textContent = String(this.lines);\n el('level').textContent = String(this.level);\n el('high-score').textContent = Math.max(this.highScore, this.score).toLocaleString();\n }\n\n // ── Rendering ────────────────────────────────────────────────────────────────\n\n private ghostOffset(): number {\n let dy = 0;\n while (fits(this.board, this.current, 0, dy + 1)) dy++;\n return dy;\n }\n\n private drawBlock(\n ctx: CanvasRenderingContext2D,\n x: number,\n y: number,\n color: string,\n alpha = 1,\n ): void {\n const px = x * CELL;\n const py = y * CELL;\n const m = 1; // margin between cells\n const s = CELL - m * 2;\n\n ctx.globalAlpha = alpha;\n\n // Base fill\n ctx.fillStyle = color;\n ctx.fillRect(px + m, py + m, s, s);\n\n // Gradient sheen\n const g = ctx.createLinearGradient(px, py, px, py + CELL);\n g.addColorStop(0, 'rgba(255,255,255,0.38)');\n g.addColorStop(0.40, 'rgba(255,255,255,0.04)');\n g.addColorStop(1, 'rgba(0,0,0,0.22)');\n ctx.fillStyle = g;\n ctx.fillRect(px + m, py + m, s, s);\n\n // Top-left highlight bevel\n ctx.fillStyle = 'rgba(255,255,255,0.55)';\n ctx.fillRect(px + m, py + m, s, 3); // top edge\n ctx.fillRect(px + m, py + m, 3, s); // left edge\n\n // Bottom-right shadow bevel\n ctx.fillStyle = 'rgba(0,0,0,0.35)';\n ctx.fillRect(px + m, py + CELL - m - 3, s, 3); // bottom edge\n ctx.fillRect(px + CELL - m - 3, py + m, 3, s); // right edge\n\n ctx.globalAlpha = 1;\n }\n\n private renderBoard(): void {\n const ctx = this.boardCtx;\n const W = this.boardCanvas.width;\n const H = this.boardCanvas.height;\n\n // Background\n ctx.fillStyle = '#0d1117';\n ctx.fillRect(0, 0, W, H);\n\n // Subtle grid lines\n ctx.strokeStyle = 'rgba(255,255,255,0.04)';\n ctx.lineWidth = 1;\n for (let c = 0; c <= COLS; c++) {\n ctx.beginPath(); ctx.moveTo(c * CELL, 0); ctx.lineTo(c * CELL, H); ctx.stroke();\n }\n for (let r = 0; r <= ROWS; r++) {\n ctx.beginPath(); ctx.moveTo(0, r * CELL); ctx.lineTo(W, r * CELL); ctx.stroke();\n }\n\n // Placed board cells (with flash effect on clearing rows)\n for (let r = 0; r < ROWS; r++) {\n for (let c = 0; c < COLS; c++) {\n const cell = this.board[r][c];\n if (!cell) continue;\n\n if (this.flashRows.includes(r)) {\n // Alternate white / colour every ~35ms\n const t = this.flashTimer / FLASH_MS;\n const flash = Math.floor(t * 8) % 2 === 0;\n this.drawBlock(ctx, c, r, flash ? '#ffffff' : cell);\n } else {\n this.drawBlock(ctx, c, r, cell);\n }\n }\n }\n\n // Ghost & active piece — hidden during flash animation\n if (this.flashTimer <= 0 && !this.gameOver) {\n const gDy = this.ghostOffset();\n\n // Ghost\n if (gDy > 0) {\n this.current.shape.forEach((row, r) => {\n row.forEach((cell, c) => {\n const gy = this.current.y + r + gDy;\n if (cell && gy >= 0) {\n this.drawBlock(ctx, this.current.x + c, gy, this.current.color, 0.18);\n }\n });\n });\n }\n\n // Active piece\n this.current.shape.forEach((row, r) => {\n row.forEach((cell, c) => {\n const py = this.current.y + r;\n if (cell && py >= 0) {\n this.drawBlock(ctx, this.current.x + c, py, this.current.color);\n }\n });\n });\n }\n\n // ── Overlays ──────────────────────────────────────────────────────────────\n\n if (this.paused) {\n ctx.fillStyle = 'rgba(0,0,0,0.72)';\n ctx.fillRect(0, 0, W, H);\n this.cText(ctx, 'PAUSED', W/2, H/2 - 22, '#ffffff', 'bold 34px monospace');\n this.cText(ctx, 'P — resume', W/2, H/2 + 18, '#778899', '15px monospace');\n }\n\n if (this.gameOver) {\n ctx.fillStyle = 'rgba(0,0,0,0.80)';\n ctx.fillRect(0, 0, W, H);\n this.cText(ctx, 'GAME OVER', W/2, H/2 - 46, '#ff4040', 'bold 36px monospace');\n this.cText(ctx, `Score: ${this.score.toLocaleString()}`, W/2, H/2 - 2, '#e0e0e0', '20px monospace');\n this.cText(ctx, 'Enter — play again', W/2, H/2 + 38, '#778899', '14px monospace');\n }\n }\n\n private cText(\n ctx: CanvasRenderingContext2D,\n text: string,\n x: number, y: number,\n color: string, font: string,\n ): void {\n ctx.save();\n ctx.fillStyle = color;\n ctx.font = font;\n ctx.textAlign = 'center';\n ctx.textBaseline = 'middle';\n ctx.fillText(text, x, y);\n ctx.restore();\n }\n\n private renderPreview(\n ctx: CanvasRenderingContext2D,\n canvas: HTMLCanvasElement,\n piece: Piece | null,\n dimmed = false,\n ): void {\n ctx.fillStyle = '#0d1117';\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n if (!piece) return;\n\n const s = piece.shape;\n const ox = Math.floor((4 - s[0].length) / 2);\n const oy = Math.floor((4 - s.length) / 2);\n s.forEach((row, r) => {\n row.forEach((cell, c) => {\n if (cell) this.drawBlock(ctx, ox + c, oy + r, piece.color, dimmed ? 0.38 : 1);\n });\n });\n }\n\n // ── Game Loop ────────────────────────────────────────────────────────────────\n\n private loop(time: number): void {\n const dt = Math.min(time - (this.lastTime || time), 100); // cap at 100ms\n this.lastTime = time;\n\n if (!this.paused && !this.gameOver) {\n // ── Flash timer (line-clear animation) ──────────────────────────────────\n if (this.flashTimer > 0) {\n this.flashTimer -= dt;\n if (this.flashTimer <= 0) {\n this.flashTimer = 0;\n this.board = removeRows(this.board, this.flashRows);\n this.flashRows = [];\n this.lines += this.pendingClr;\n this.score += this.pendingScr;\n this.level = Math.floor(this.lines / 10) + 1;\n this.pendingClr = 0;\n this.pendingScr = 0;\n this.spawnNext();\n }\n } else {\n // ── DAS / ARR horizontal movement ─────────────────────────────────────\n const leftDown = this.keysDown.has('ArrowLeft');\n const rightDown = this.keysDown.has('ArrowRight');\n\n if (leftDown || rightDown) {\n this.dasTimer += dt;\n if (this.dasTimer > DAS) {\n this.arrTimer += dt;\n if (this.arrTimer >= ARR) {\n const steps = Math.floor(this.arrTimer / ARR);\n this.arrTimer -= steps * ARR;\n for (let i = 0; i < steps; i++) {\n if (leftDown && !rightDown) this.move(-1);\n if (rightDown && !leftDown) this.move(1);\n }\n }\n }\n }\n\n // ── Gravity ───────────────────────────────────────────────────────────\n const softDropping = this.keysDown.has('ArrowDown');\n const interval = softDropping ? 50 : dropInterval(this.level);\n\n this.dropCounter += dt;\n if (this.dropCounter >= interval) {\n this.dropCounter = 0;\n if (fits(this.board, this.current, 0, 1)) {\n this.current.y++;\n this.onFloor = false;\n if (softDropping) {\n this.score++;\n this.updateHUD();\n }\n } else {\n // Piece is resting on something — start lock delay\n this.onFloor = true;\n this.lockTimer += dt + interval; // add the elapsed time\n if (this.lockTimer >= LOCK_DELAY) {\n this.lock();\n }\n }\n }\n\n // Accumulate lock delay even between gravity ticks\n if (this.onFloor && !fits(this.board, this.current, 0, 1)) {\n this.lockTimer += dt;\n if (this.lockTimer >= LOCK_DELAY) {\n this.lock();\n }\n }\n }\n }\n\n // ── Render ────────────────────────────────────────────────────────────────\n this.renderBoard();\n this.renderPreview(this.nextCtx, this.nextCanvas, this.next);\n this.renderPreview(this.holdCtx, this.holdCanvas, this.held, !this.canHold);\n\n requestAnimationFrame(t => this.loop(t));\n }\n}\n\n// ─── Bootstrap ───────────────────────────────────────────────────────────────\n\nwindow.addEventListener('load', () => new TetrisGame());\n","structuredPatch":[{"oldStart":631,"oldLines":21,"newStart":631,"newLines":18,"lines":[" if (fits(this.board, this.current, 0, 1)) {"," this.current.y++;"," this.onFloor = false;","+ this.lockTimer = 0;"," if (softDropping) {"," this.score++;"," this.updateHUD();"," }"," } else {","- // Piece is resting on something — start lock delay","+ // Piece can't fall — mark as on floor; lock delay handled below"," this.onFloor = true;","- this.lockTimer += dt + interval; // add the elapsed time","- if (this.lockTimer >= LOCK_DELAY) {","- this.lock();","- }"," }"," }"," ","- // Accumulate lock delay even between gravity ticks","+ // Accumulate lock delay every frame while piece rests on something"," if (this.onFloor && !fits(this.board, this.current, 0, 1)) {"," this.lockTimer += dt;"," if (this.lockTimer >= LOCK_DELAY) {"]}],"userModified":false,"replaceAll":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_013m8GnfeVW1pqguSV4fL8fH","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01PigyPteDeRs7AZdJYdJqj8","name":"Bash","input":{"command":"node_modules/.bin/tsc 2>&1","description":"Recompile after lock-delay fix"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":763,"cache_read_input_tokens":60439,"cache_creation":{"ephemeral_5m_input_tokens":763,"ephemeral_1h_input_tokens":0},"output_tokens":47,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"1034750c-9f4f-4049-9b9a-cd3a84adc60e"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01PigyPteDeRs7AZdJYdJqj8","type":"tool_result","content":"(Bash completed with no output)","is_error":false}]},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"5045e958-5090-429a-bf6e-eea2b6d603e4","timestamp":"2026-04-04T21:10:25.862Z","tool_use_result":{"stdout":"","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_014ekHNEkWfhTdzc7UerZJbV","type":"message","role":"assistant","content":[{"type":"text","text":"Clean compile. Let me do a final review of the complete source and HTML to make sure everything looks right:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":106,"cache_read_input_tokens":61202,"cache_creation":{"ephemeral_5m_input_tokens":106,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"62466616-966f-4a96-9209-59108c1d4631"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_014ekHNEkWfhTdzc7UerZJbV","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01MMc3mKbeLZCJe9kWVEghpt","name":"Read","input":{"file_path":"/tmp/loop-bench-iuvnueix/src/tetris.ts","limit":100},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":106,"cache_read_input_tokens":61202,"cache_creation":{"ephemeral_5m_input_tokens":106,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"3dc5b999-b027-45df-a283-d0b63f0e1d4f"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01MMc3mKbeLZCJe9kWVEghpt","type":"tool_result","content":"1\t// ─── Constants ────────────────────────────────────────────────────────────────\n2\t\n3\tconst COLS = 10;\n4\tconst ROWS = 20;\n5\tconst CELL = 32; // px per grid cell\n6\tconst DAS = 170; // Delayed Auto Shift (ms)\n7\tconst ARR = 50; // Auto Repeat Rate (ms)\n8\tconst FLASH_MS = 280; // Line-clear flash duration (ms)\n9\tconst LOCK_DELAY = 500; // Lock delay after piece hits floor (ms)\n10\t\n11\t// ─── Piece Definitions ────────────────────────────────────────────────────────\n12\t\n13\tconst COLORS: Record<string, string> = {\n14\t I: '#00c8ff',\n15\t O: '#ffd700',\n16\t T: '#c040f0',\n17\t S: '#10e060',\n18\t Z: '#ff3030',\n19\t J: '#3878ff',\n20\t L: '#ff8800',\n21\t};\n22\t\n23\t// All pieces use square bounding boxes (2×2, 3×3, or 4×4)\n24\tconst SHAPES: Record<string, number[][]> = {\n25\t I: [[0,0,0,0],\n26\t [1,1,1,1],\n27\t [0,0,0,0],\n28\t [0,0,0,0]],\n29\t O: [[1,1],\n30\t [1,1]],\n31\t T: [[0,1,0],\n32\t [1,1,1],\n33\t [0,0,0]],\n34\t S: [[0,1,1],\n35\t [1,1,0],\n36\t [0,0,0]],\n37\t Z: [[1,1,0],\n38\t [0,1,1],\n39\t [0,0,0]],\n40\t J: [[1,0,0],\n41\t [1,1,1],\n42\t [0,0,0]],\n43\t L: [[0,0,1],\n44\t [1,1,1],\n45\t [0,0,0]],\n46\t};\n47\t\n48\t// Standard speed curve (ms per gravity drop) for levels 1-13\n49\tconst SPEED_CURVE = [800,717,633,550,467,383,300,217,133,100,83,67,50];\n50\t\n51\t// ─── Types ────────────────────────────────────────────────────────────────────\n52\t\n53\ttype Cell = string | null;\n54\ttype Board = Cell[][];\n55\t\n56\tinterface Piece {\n57\t type: string;\n58\t shape: number[][];\n59\t color: string;\n60\t x: number;\n61\t y: number;\n62\t}\n63\t\n64\t// ─── Utility Functions ────────────────────────────────────────────────────────\n65\t\n66\tfunction createBoard(): Board {\n67\t return Array.from({ length: ROWS }, () => new Array<Cell>(COLS).fill(null));\n68\t}\n69\t\n70\t/** Rotate matrix 90° clockwise */\n71\tfunction rotateCW(m: number[][]): number[][] {\n72\t return m[0].map((_, i) => m.map(row => row[i]).reverse());\n73\t}\n74\t\n75\t/** Rotate matrix 90° counter-clockwise */\n76\tfunction rotateCCW(m: number[][]): number[][] {\n77\t return m[0].map((_, i) => m.map(row => row[row.length - 1 - i])).reverse();\n78\t}\n79\t\n80\t/** Spawn a new piece centered at the top */\n81\tfunction makePiece(type: string): Piece {\n82\t const shape = SHAPES[type].map(r => [...r]);\n83\t return {\n84\t type,\n85\t shape,\n86\t color: COLORS[type],\n87\t x: Math.floor((COLS - shape[0].length) / 2),\n88\t y: 0,\n89\t };\n90\t}\n91\t\n92\t/** Returns true if the piece (optionally with offsets / alternate shape) fits on the board */\n93\tfunction fits(board: Board, piece: Piece, dx = 0, dy = 0, shape = piece.shape): boolean {\n94\t for (let r = 0; r < shape.length; r++) {\n95\t for (let c = 0; c < shape[r].length; c++) {\n96\t if (!shape[r][c]) continue;\n97\t const nx = piece.x + c + dx;\n98\t const ny = piece.y + r + dy;\n99\t if (nx < 0 || nx >= COLS || ny >= ROWS) return false;\n100\t if (ny >= 0 && board[ny][nx]) return false;"}]},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"b5b1f48c-ea9b-4c07-a82e-8a1aec510d00","timestamp":"2026-04-04T21:10:28.170Z","tool_use_result":{"type":"text","file":{"filePath":"/tmp/loop-bench-iuvnueix/src/tetris.ts","content":"// ─── Constants ────────────────────────────────────────────────────────────────\n\nconst COLS = 10;\nconst ROWS = 20;\nconst CELL = 32; // px per grid cell\nconst DAS = 170; // Delayed Auto Shift (ms)\nconst ARR = 50; // Auto Repeat Rate (ms)\nconst FLASH_MS = 280; // Line-clear flash duration (ms)\nconst LOCK_DELAY = 500; // Lock delay after piece hits floor (ms)\n\n// ─── Piece Definitions ────────────────────────────────────────────────────────\n\nconst COLORS: Record<string, string> = {\n I: '#00c8ff',\n O: '#ffd700',\n T: '#c040f0',\n S: '#10e060',\n Z: '#ff3030',\n J: '#3878ff',\n L: '#ff8800',\n};\n\n// All pieces use square bounding boxes (2×2, 3×3, or 4×4)\nconst SHAPES: Record<string, number[][]> = {\n I: [[0,0,0,0],\n [1,1,1,1],\n [0,0,0,0],\n [0,0,0,0]],\n O: [[1,1],\n [1,1]],\n T: [[0,1,0],\n [1,1,1],\n [0,0,0]],\n S: [[0,1,1],\n [1,1,0],\n [0,0,0]],\n Z: [[1,1,0],\n [0,1,1],\n [0,0,0]],\n J: [[1,0,0],\n [1,1,1],\n [0,0,0]],\n L: [[0,0,1],\n [1,1,1],\n [0,0,0]],\n};\n\n// Standard speed curve (ms per gravity drop) for levels 1-13\nconst SPEED_CURVE = [800,717,633,550,467,383,300,217,133,100,83,67,50];\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ntype Cell = string | null;\ntype Board = Cell[][];\n\ninterface Piece {\n type: string;\n shape: number[][];\n color: string;\n x: number;\n y: number;\n}\n\n// ─── Utility Functions ────────────────────────────────────────────────────────\n\nfunction createBoard(): Board {\n return Array.from({ length: ROWS }, () => new Array<Cell>(COLS).fill(null));\n}\n\n/** Rotate matrix 90° clockwise */\nfunction rotateCW(m: number[][]): number[][] {\n return m[0].map((_, i) => m.map(row => row[i]).reverse());\n}\n\n/** Rotate matrix 90° counter-clockwise */\nfunction rotateCCW(m: number[][]): number[][] {\n return m[0].map((_, i) => m.map(row => row[row.length - 1 - i])).reverse();\n}\n\n/** Spawn a new piece centered at the top */\nfunction makePiece(type: string): Piece {\n const shape = SHAPES[type].map(r => [...r]);\n return {\n type,\n shape,\n color: COLORS[type],\n x: Math.floor((COLS - shape[0].length) / 2),\n y: 0,\n };\n}\n\n/** Returns true if the piece (optionally with offsets / alternate shape) fits on the board */\nfunction fits(board: Board, piece: Piece, dx = 0, dy = 0, shape = piece.shape): boolean {\n for (let r = 0; r < shape.length; r++) {\n for (let c = 0; c < shape[r].length; c++) {\n if (!shape[r][c]) continue;\n const nx = piece.x + c + dx;\n const ny = piece.y + r + dy;\n if (nx < 0 || nx >= COLS || ny >= ROWS) return false;\n if (ny >= 0 && board[ny][nx]) return false;","numLines":100,"startLine":1,"totalLines":667}}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01CuB9WWbAcyw5WBh8W8ZwEb","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01T4u3y6kn8toT2iRDX2EMjq","name":"Read","input":{"file_path":"/tmp/loop-bench-iuvnueix/src/tetris.ts","offset":100,"limit":180},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":1411,"cache_read_input_tokens":61308,"cache_creation":{"ephemeral_5m_input_tokens":1411,"ephemeral_1h_input_tokens":0},"output_tokens":36,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"47722487-e305-4a44-839f-06b94a0c4ade"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01T4u3y6kn8toT2iRDX2EMjq","type":"tool_result","content":"100\t if (ny >= 0 && board[ny][nx]) return false;\n101\t }\n102\t }\n103\t return true;\n104\t}\n105\t\n106\t/** Place the piece onto the board, returning a new board */\n107\tfunction stamp(board: Board, piece: Piece): Board {\n108\t const b = board.map(r => [...r]);\n109\t piece.shape.forEach((row, r) => {\n110\t row.forEach((cell, c) => {\n111\t if (cell) {\n112\t const ny = piece.y + r;\n113\t const nx = piece.x + c;\n114\t if (ny >= 0 && ny < ROWS && nx >= 0 && nx < COLS) b[ny][nx] = piece.color;\n115\t }\n116\t });\n117\t });\n118\t return b;\n119\t}\n120\t\n121\t/** Return indices of completely filled rows */\n122\tfunction fullRows(board: Board): number[] {\n123\t return board.reduce<number[]>((acc, row, i) => {\n124\t if (row.every(c => c !== null)) acc.push(i);\n125\t return acc;\n126\t }, []);\n127\t}\n128\t\n129\t/** Remove the specified rows and prepend empty rows at the top */\n130\tfunction removeRows(board: Board, rows: number[]): Board {\n131\t const kept = board.filter((_, i) => !rows.includes(i));\n132\t const empties = Array.from({ length: rows.length }, () => new Array<Cell>(COLS).fill(null));\n133\t return [...empties, ...kept];\n134\t}\n135\t\n136\tconst SCORE_TABLE = [0, 100, 300, 500, 800];\n137\t\n138\tfunction lineScore(n: number, level: number): number {\n139\t return (SCORE_TABLE[n] ?? 0) * level;\n140\t}\n141\t\n142\tfunction dropInterval(level: number): number {\n143\t return SPEED_CURVE[Math.min(level - 1, SPEED_CURVE.length - 1)];\n144\t}\n145\t\n146\t// ─── 7-Bag Randomiser ─────────────────────────────────────────────────────────\n147\t\n148\tclass Bag {\n149\t private items: string[] = [];\n150\t\n151\t private refill(): void {\n152\t this.items = Object.keys(SHAPES);\n153\t for (let i = this.items.length - 1; i > 0; i--) {\n154\t const j = Math.floor(Math.random() * (i + 1));\n155\t [this.items[i], this.items[j]] = [this.items[j], this.items[i]];\n156\t }\n157\t }\n158\t\n159\t draw(): string {\n160\t if (this.items.length === 0) this.refill();\n161\t return this.items.pop()!;\n162\t }\n163\t}\n164\t\n165\t// ─── Main Game Class ──────────────────────────────────────────────────────────\n166\t\n167\tclass TetrisGame {\n168\t // Canvases\n169\t private readonly boardCanvas: HTMLCanvasElement;\n170\t private readonly boardCtx: CanvasRenderingContext2D;\n171\t private readonly nextCanvas: HTMLCanvasElement;\n172\t private readonly nextCtx: CanvasRenderingContext2D;\n173\t private readonly holdCanvas: HTMLCanvasElement;\n174\t private readonly holdCtx: CanvasRenderingContext2D;\n175\t\n176\t // Game state\n177\t private bag!: Bag;\n178\t private board!: Board;\n179\t private current!: Piece;\n180\t private next!: Piece;\n181\t private held: Piece | null = null;\n182\t private canHold!: boolean;\n183\t private score!: number;\n184\t private lines!: number;\n185\t private level!: number;\n186\t private highScore: number;\n187\t private gameOver!: boolean;\n188\t private paused!: boolean;\n189\t\n190\t // Timing\n191\t private lastTime = 0;\n192\t private dropCounter!: number;\n193\t private lockTimer!: number; // counts toward lock delay\n194\t private onFloor!: boolean; // true when piece is resting on something\n195\t\n196\t // Line-clear flash\n197\t private flashTimer = 0;\n198\t private flashRows: number[] = [];\n199\t private pendingClr = 0;\n200\t private pendingScr = 0;\n201\t\n202\t // Input\n203\t private keysDown = new Set<string>();\n204\t private dasTimer = 0;\n205\t private arrTimer = 0;\n206\t\n207\t // ── Constructor ─────────────────────────────────────────────────────────────\n208\t\n209\t constructor() {\n210\t const get = <T extends HTMLElement>(id: string) =>\n211\t document.getElementById(id) as T;\n212\t\n213\t this.boardCanvas = get<HTMLCanvasElement>('board');\n214\t this.boardCtx = this.boardCanvas.getContext('2d')!;\n215\t this.nextCanvas = get<HTMLCanvasElement>('next');\n216\t this.nextCtx = this.nextCanvas.getContext('2d')!;\n217\t this.holdCanvas = get<HTMLCanvasElement>('hold');\n218\t this.holdCtx = this.holdCanvas.getContext('2d')!;\n219\t\n220\t this.boardCanvas.width = COLS * CELL;\n221\t this.boardCanvas.height = ROWS * CELL;\n222\t this.nextCanvas.width = 4 * CELL;\n223\t this.nextCanvas.height = 4 * CELL;\n224\t this.holdCanvas.width = 4 * CELL;\n225\t this.holdCanvas.height = 4 * CELL;\n226\t\n227\t this.highScore = parseInt(localStorage.getItem('tetris-hs') ?? '0', 10);\n228\t\n229\t this.setupInput();\n230\t this.init();\n231\t requestAnimationFrame(t => this.loop(t));\n232\t }\n233\t\n234\t // ── Initialisation ──────────────────────────────────────────────────────────\n235\t\n236\t private init(): void {\n237\t this.bag = new Bag();\n238\t this.board = createBoard();\n239\t this.current = makePiece(this.bag.draw());\n240\t this.next = makePiece(this.bag.draw());\n241\t this.held = null;\n242\t this.canHold = true;\n243\t this.score = 0;\n244\t this.lines = 0;\n245\t this.level = 1;\n246\t this.gameOver = false;\n247\t this.paused = false;\n248\t this.dropCounter = 0;\n249\t this.lockTimer = 0;\n250\t this.onFloor = false;\n251\t this.flashTimer = 0;\n252\t this.flashRows = [];\n253\t this.pendingClr = 0;\n254\t this.pendingScr = 0;\n255\t this.updateHUD();\n256\t }\n257\t\n258\t // ── Input ───────────────────────────────────────────────────────────────────\n259\t\n260\t private setupInput(): void {\n261\t // Prevent page scroll for game keys\n262\t window.addEventListener('keydown', e => {\n263\t if (['ArrowUp','ArrowDown','ArrowLeft','ArrowRight','Space'].includes(e.code))\n264\t e.preventDefault();\n265\t\n266\t if (!e.repeat) {\n267\t this.keysDown.add(e.code);\n268\t this.onFirstPress(e.code);\n269\t }\n270\t }, { passive: false });\n271\t\n272\t window.addEventListener('keyup', e => {\n273\t this.keysDown.delete(e.code);\n274\t if (e.code === 'ArrowLeft' || e.code === 'ArrowRight') {\n275\t this.dasTimer = 0;\n276\t this.arrTimer = 0;\n277\t }\n278\t });\n279\t }"}]},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"7f69c229-a934-41aa-8c26-bb9cf79287ae","timestamp":"2026-04-04T21:10:31.464Z","tool_use_result":{"type":"text","file":{"filePath":"/tmp/loop-bench-iuvnueix/src/tetris.ts","content":" if (ny >= 0 && board[ny][nx]) return false;\n }\n }\n return true;\n}\n\n/** Place the piece onto the board, returning a new board */\nfunction stamp(board: Board, piece: Piece): Board {\n const b = board.map(r => [...r]);\n piece.shape.forEach((row, r) => {\n row.forEach((cell, c) => {\n if (cell) {\n const ny = piece.y + r;\n const nx = piece.x + c;\n if (ny >= 0 && ny < ROWS && nx >= 0 && nx < COLS) b[ny][nx] = piece.color;\n }\n });\n });\n return b;\n}\n\n/** Return indices of completely filled rows */\nfunction fullRows(board: Board): number[] {\n return board.reduce<number[]>((acc, row, i) => {\n if (row.every(c => c !== null)) acc.push(i);\n return acc;\n }, []);\n}\n\n/** Remove the specified rows and prepend empty rows at the top */\nfunction removeRows(board: Board, rows: number[]): Board {\n const kept = board.filter((_, i) => !rows.includes(i));\n const empties = Array.from({ length: rows.length }, () => new Array<Cell>(COLS).fill(null));\n return [...empties, ...kept];\n}\n\nconst SCORE_TABLE = [0, 100, 300, 500, 800];\n\nfunction lineScore(n: number, level: number): number {\n return (SCORE_TABLE[n] ?? 0) * level;\n}\n\nfunction dropInterval(level: number): number {\n return SPEED_CURVE[Math.min(level - 1, SPEED_CURVE.length - 1)];\n}\n\n// ─── 7-Bag Randomiser ─────────────────────────────────────────────────────────\n\nclass Bag {\n private items: string[] = [];\n\n private refill(): void {\n this.items = Object.keys(SHAPES);\n for (let i = this.items.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [this.items[i], this.items[j]] = [this.items[j], this.items[i]];\n }\n }\n\n draw(): string {\n if (this.items.length === 0) this.refill();\n return this.items.pop()!;\n }\n}\n\n// ─── Main Game Class ──────────────────────────────────────────────────────────\n\nclass TetrisGame {\n // Canvases\n private readonly boardCanvas: HTMLCanvasElement;\n private readonly boardCtx: CanvasRenderingContext2D;\n private readonly nextCanvas: HTMLCanvasElement;\n private readonly nextCtx: CanvasRenderingContext2D;\n private readonly holdCanvas: HTMLCanvasElement;\n private readonly holdCtx: CanvasRenderingContext2D;\n\n // Game state\n private bag!: Bag;\n private board!: Board;\n private current!: Piece;\n private next!: Piece;\n private held: Piece | null = null;\n private canHold!: boolean;\n private score!: number;\n private lines!: number;\n private level!: number;\n private highScore: number;\n private gameOver!: boolean;\n private paused!: boolean;\n\n // Timing\n private lastTime = 0;\n private dropCounter!: number;\n private lockTimer!: number; // counts toward lock delay\n private onFloor!: boolean; // true when piece is resting on something\n\n // Line-clear flash\n private flashTimer = 0;\n private flashRows: number[] = [];\n private pendingClr = 0;\n private pendingScr = 0;\n\n // Input\n private keysDown = new Set<string>();\n private dasTimer = 0;\n private arrTimer = 0;\n\n // ── Constructor ─────────────────────────────────────────────────────────────\n\n constructor() {\n const get = <T extends HTMLElement>(id: string) =>\n document.getElementById(id) as T;\n\n this.boardCanvas = get<HTMLCanvasElement>('board');\n this.boardCtx = this.boardCanvas.getContext('2d')!;\n this.nextCanvas = get<HTMLCanvasElement>('next');\n this.nextCtx = this.nextCanvas.getContext('2d')!;\n this.holdCanvas = get<HTMLCanvasElement>('hold');\n this.holdCtx = this.holdCanvas.getContext('2d')!;\n\n this.boardCanvas.width = COLS * CELL;\n this.boardCanvas.height = ROWS * CELL;\n this.nextCanvas.width = 4 * CELL;\n this.nextCanvas.height = 4 * CELL;\n this.holdCanvas.width = 4 * CELL;\n this.holdCanvas.height = 4 * CELL;\n\n this.highScore = parseInt(localStorage.getItem('tetris-hs') ?? '0', 10);\n\n this.setupInput();\n this.init();\n requestAnimationFrame(t => this.loop(t));\n }\n\n // ── Initialisation ──────────────────────────────────────────────────────────\n\n private init(): void {\n this.bag = new Bag();\n this.board = createBoard();\n this.current = makePiece(this.bag.draw());\n this.next = makePiece(this.bag.draw());\n this.held = null;\n this.canHold = true;\n this.score = 0;\n this.lines = 0;\n this.level = 1;\n this.gameOver = false;\n this.paused = false;\n this.dropCounter = 0;\n this.lockTimer = 0;\n this.onFloor = false;\n this.flashTimer = 0;\n this.flashRows = [];\n this.pendingClr = 0;\n this.pendingScr = 0;\n this.updateHUD();\n }\n\n // ── Input ───────────────────────────────────────────────────────────────────\n\n private setupInput(): void {\n // Prevent page scroll for game keys\n window.addEventListener('keydown', e => {\n if (['ArrowUp','ArrowDown','ArrowLeft','ArrowRight','Space'].includes(e.code))\n e.preventDefault();\n\n if (!e.repeat) {\n this.keysDown.add(e.code);\n this.onFirstPress(e.code);\n }\n }, { passive: false });\n\n window.addEventListener('keyup', e => {\n this.keysDown.delete(e.code);\n if (e.code === 'ArrowLeft' || e.code === 'ArrowRight') {\n this.dasTimer = 0;\n this.arrTimer = 0;\n }\n });\n }","numLines":180,"startLine":100,"totalLines":667}}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01RuyDTprwhKgVJgyfXimZr2","type":"message","role":"assistant","content":[{"type":"thinking","thinking":"The code looks good. Let me also check a subtle issue with the `keysDown` set. In `setupInput`, I add to `keysDown` only when `!e.repeat`. But what if a key is held, the browser fires repeat events (which we ignore), then the keyup fires - we remove from `keysDown`. But what if the keyup for a previously tracked key fires after we did `!e.repeat` add? That should be fine since we only care about the final state.\n\nWait, there's actually an issue: when `e.repeat` is true, we DON'T add to `keysDown`. But for left/right, we need `keysDown` to reflect the currently-held state for DAS/ARR. Let me check:\n\n\n- On the first ArrowLeft press, `e.repeat` is false so we add it to `keysDown` and trigger the initial move\n- Subsequent repeats have `e.repeat` true so nothing changes in `keysDown` (it's already there)\n- When ArrowLeft releases, we remove it from `keysDown` and reset the timers\n\nSo `keysDown` correctly tracks which keys are physically held, and the game loop checking `keysDown.has('ArrowLeft')` works as intended. This is actually correct!\n\nThere's one edge case though: if the page loses focus while holding a key, the keyup event might not fire, leaving that key stuck in `keysDown`. I should add a blur handler to clear all keys when focus is lost—it's a simple one-liner that prevents stuck inputs. Let me add that.","signature":"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"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":2340,"cache_read_input_tokens":62719,"cache_creation":{"ephemeral_5m_input_tokens":2340,"ephemeral_1h_input_tokens":0},"output_tokens":8,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"20007ba7-dc68-4bb6-a0a1-564e512d77b0"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01RuyDTprwhKgVJgyfXimZr2","type":"message","role":"assistant","content":[{"type":"text","text":"Let me also add a `blur` handler to prevent stuck keys when the window loses focus, and verify the final file structure:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":2340,"cache_read_input_tokens":62719,"cache_creation":{"ephemeral_5m_input_tokens":2340,"ephemeral_1h_input_tokens":0},"output_tokens":8,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"c7f274e2-2b9e-4687-878e-8b7e02a0b187"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01RuyDTprwhKgVJgyfXimZr2","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01R7fsxLs3SPYbJjc17hqi9r","name":"Edit","input":{"replace_all":false,"file_path":"/tmp/loop-bench-iuvnueix/src/tetris.ts","old_string":" window.addEventListener('keyup', e => {\n this.keysDown.delete(e.code);\n if (e.code === 'ArrowLeft' || e.code === 'ArrowRight') {\n this.dasTimer = 0;\n this.arrTimer = 0;\n }\n });\n }","new_string":" window.addEventListener('keyup', e => {\n this.keysDown.delete(e.code);\n if (e.code === 'ArrowLeft' || e.code === 'ArrowRight') {\n this.dasTimer = 0;\n this.arrTimer = 0;\n }\n });\n\n // Clear all held keys when window loses focus (prevents stuck movement)\n window.addEventListener('blur', () => {\n this.keysDown.clear();\n this.dasTimer = 0;\n this.arrTimer = 0;\n });\n }"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":2340,"cache_read_input_tokens":62719,"cache_creation":{"ephemeral_5m_input_tokens":2340,"ephemeral_1h_input_tokens":0},"output_tokens":8,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"87ab83bb-16bd-4925-a0e1-c8070a5147cb"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01R7fsxLs3SPYbJjc17hqi9r","type":"tool_result","content":"The file /tmp/loop-bench-iuvnueix/src/tetris.ts has been updated successfully."}]},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"b5a90eaf-0507-48ff-a819-d99bb5185721","timestamp":"2026-04-04T21:10:48.094Z","tool_use_result":{"filePath":"/tmp/loop-bench-iuvnueix/src/tetris.ts","oldString":" window.addEventListener('keyup', e => {\n this.keysDown.delete(e.code);\n if (e.code === 'ArrowLeft' || e.code === 'ArrowRight') {\n this.dasTimer = 0;\n this.arrTimer = 0;\n }\n });\n }","newString":" window.addEventListener('keyup', e => {\n this.keysDown.delete(e.code);\n if (e.code === 'ArrowLeft' || e.code === 'ArrowRight') {\n this.dasTimer = 0;\n this.arrTimer = 0;\n }\n });\n\n // Clear all held keys when window loses focus (prevents stuck movement)\n window.addEventListener('blur', () => {\n this.keysDown.clear();\n this.dasTimer = 0;\n this.arrTimer = 0;\n });\n }","originalFile":"// ─── Constants ────────────────────────────────────────────────────────────────\n\nconst COLS = 10;\nconst ROWS = 20;\nconst CELL = 32; // px per grid cell\nconst DAS = 170; // Delayed Auto Shift (ms)\nconst ARR = 50; // Auto Repeat Rate (ms)\nconst FLASH_MS = 280; // Line-clear flash duration (ms)\nconst LOCK_DELAY = 500; // Lock delay after piece hits floor (ms)\n\n// ─── Piece Definitions ────────────────────────────────────────────────────────\n\nconst COLORS: Record<string, string> = {\n I: '#00c8ff',\n O: '#ffd700',\n T: '#c040f0',\n S: '#10e060',\n Z: '#ff3030',\n J: '#3878ff',\n L: '#ff8800',\n};\n\n// All pieces use square bounding boxes (2×2, 3×3, or 4×4)\nconst SHAPES: Record<string, number[][]> = {\n I: [[0,0,0,0],\n [1,1,1,1],\n [0,0,0,0],\n [0,0,0,0]],\n O: [[1,1],\n [1,1]],\n T: [[0,1,0],\n [1,1,1],\n [0,0,0]],\n S: [[0,1,1],\n [1,1,0],\n [0,0,0]],\n Z: [[1,1,0],\n [0,1,1],\n [0,0,0]],\n J: [[1,0,0],\n [1,1,1],\n [0,0,0]],\n L: [[0,0,1],\n [1,1,1],\n [0,0,0]],\n};\n\n// Standard speed curve (ms per gravity drop) for levels 1-13\nconst SPEED_CURVE = [800,717,633,550,467,383,300,217,133,100,83,67,50];\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\ntype Cell = string | null;\ntype Board = Cell[][];\n\ninterface Piece {\n type: string;\n shape: number[][];\n color: string;\n x: number;\n y: number;\n}\n\n// ─── Utility Functions ────────────────────────────────────────────────────────\n\nfunction createBoard(): Board {\n return Array.from({ length: ROWS }, () => new Array<Cell>(COLS).fill(null));\n}\n\n/** Rotate matrix 90° clockwise */\nfunction rotateCW(m: number[][]): number[][] {\n return m[0].map((_, i) => m.map(row => row[i]).reverse());\n}\n\n/** Rotate matrix 90° counter-clockwise */\nfunction rotateCCW(m: number[][]): number[][] {\n return m[0].map((_, i) => m.map(row => row[row.length - 1 - i])).reverse();\n}\n\n/** Spawn a new piece centered at the top */\nfunction makePiece(type: string): Piece {\n const shape = SHAPES[type].map(r => [...r]);\n return {\n type,\n shape,\n color: COLORS[type],\n x: Math.floor((COLS - shape[0].length) / 2),\n y: 0,\n };\n}\n\n/** Returns true if the piece (optionally with offsets / alternate shape) fits on the board */\nfunction fits(board: Board, piece: Piece, dx = 0, dy = 0, shape = piece.shape): boolean {\n for (let r = 0; r < shape.length; r++) {\n for (let c = 0; c < shape[r].length; c++) {\n if (!shape[r][c]) continue;\n const nx = piece.x + c + dx;\n const ny = piece.y + r + dy;\n if (nx < 0 || nx >= COLS || ny >= ROWS) return false;\n if (ny >= 0 && board[ny][nx]) return false;\n }\n }\n return true;\n}\n\n/** Place the piece onto the board, returning a new board */\nfunction stamp(board: Board, piece: Piece): Board {\n const b = board.map(r => [...r]);\n piece.shape.forEach((row, r) => {\n row.forEach((cell, c) => {\n if (cell) {\n const ny = piece.y + r;\n const nx = piece.x + c;\n if (ny >= 0 && ny < ROWS && nx >= 0 && nx < COLS) b[ny][nx] = piece.color;\n }\n });\n });\n return b;\n}\n\n/** Return indices of completely filled rows */\nfunction fullRows(board: Board): number[] {\n return board.reduce<number[]>((acc, row, i) => {\n if (row.every(c => c !== null)) acc.push(i);\n return acc;\n }, []);\n}\n\n/** Remove the specified rows and prepend empty rows at the top */\nfunction removeRows(board: Board, rows: number[]): Board {\n const kept = board.filter((_, i) => !rows.includes(i));\n const empties = Array.from({ length: rows.length }, () => new Array<Cell>(COLS).fill(null));\n return [...empties, ...kept];\n}\n\nconst SCORE_TABLE = [0, 100, 300, 500, 800];\n\nfunction lineScore(n: number, level: number): number {\n return (SCORE_TABLE[n] ?? 0) * level;\n}\n\nfunction dropInterval(level: number): number {\n return SPEED_CURVE[Math.min(level - 1, SPEED_CURVE.length - 1)];\n}\n\n// ─── 7-Bag Randomiser ─────────────────────────────────────────────────────────\n\nclass Bag {\n private items: string[] = [];\n\n private refill(): void {\n this.items = Object.keys(SHAPES);\n for (let i = this.items.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [this.items[i], this.items[j]] = [this.items[j], this.items[i]];\n }\n }\n\n draw(): string {\n if (this.items.length === 0) this.refill();\n return this.items.pop()!;\n }\n}\n\n// ─── Main Game Class ──────────────────────────────────────────────────────────\n\nclass TetrisGame {\n // Canvases\n private readonly boardCanvas: HTMLCanvasElement;\n private readonly boardCtx: CanvasRenderingContext2D;\n private readonly nextCanvas: HTMLCanvasElement;\n private readonly nextCtx: CanvasRenderingContext2D;\n private readonly holdCanvas: HTMLCanvasElement;\n private readonly holdCtx: CanvasRenderingContext2D;\n\n // Game state\n private bag!: Bag;\n private board!: Board;\n private current!: Piece;\n private next!: Piece;\n private held: Piece | null = null;\n private canHold!: boolean;\n private score!: number;\n private lines!: number;\n private level!: number;\n private highScore: number;\n private gameOver!: boolean;\n private paused!: boolean;\n\n // Timing\n private lastTime = 0;\n private dropCounter!: number;\n private lockTimer!: number; // counts toward lock delay\n private onFloor!: boolean; // true when piece is resting on something\n\n // Line-clear flash\n private flashTimer = 0;\n private flashRows: number[] = [];\n private pendingClr = 0;\n private pendingScr = 0;\n\n // Input\n private keysDown = new Set<string>();\n private dasTimer = 0;\n private arrTimer = 0;\n\n // ── Constructor ─────────────────────────────────────────────────────────────\n\n constructor() {\n const get = <T extends HTMLElement>(id: string) =>\n document.getElementById(id) as T;\n\n this.boardCanvas = get<HTMLCanvasElement>('board');\n this.boardCtx = this.boardCanvas.getContext('2d')!;\n this.nextCanvas = get<HTMLCanvasElement>('next');\n this.nextCtx = this.nextCanvas.getContext('2d')!;\n this.holdCanvas = get<HTMLCanvasElement>('hold');\n this.holdCtx = this.holdCanvas.getContext('2d')!;\n\n this.boardCanvas.width = COLS * CELL;\n this.boardCanvas.height = ROWS * CELL;\n this.nextCanvas.width = 4 * CELL;\n this.nextCanvas.height = 4 * CELL;\n this.holdCanvas.width = 4 * CELL;\n this.holdCanvas.height = 4 * CELL;\n\n this.highScore = parseInt(localStorage.getItem('tetris-hs') ?? '0', 10);\n\n this.setupInput();\n this.init();\n requestAnimationFrame(t => this.loop(t));\n }\n\n // ── Initialisation ──────────────────────────────────────────────────────────\n\n private init(): void {\n this.bag = new Bag();\n this.board = createBoard();\n this.current = makePiece(this.bag.draw());\n this.next = makePiece(this.bag.draw());\n this.held = null;\n this.canHold = true;\n this.score = 0;\n this.lines = 0;\n this.level = 1;\n this.gameOver = false;\n this.paused = false;\n this.dropCounter = 0;\n this.lockTimer = 0;\n this.onFloor = false;\n this.flashTimer = 0;\n this.flashRows = [];\n this.pendingClr = 0;\n this.pendingScr = 0;\n this.updateHUD();\n }\n\n // ── Input ───────────────────────────────────────────────────────────────────\n\n private setupInput(): void {\n // Prevent page scroll for game keys\n window.addEventListener('keydown', e => {\n if (['ArrowUp','ArrowDown','ArrowLeft','ArrowRight','Space'].includes(e.code))\n e.preventDefault();\n\n if (!e.repeat) {\n this.keysDown.add(e.code);\n this.onFirstPress(e.code);\n }\n }, { passive: false });\n\n window.addEventListener('keyup', e => {\n this.keysDown.delete(e.code);\n if (e.code === 'ArrowLeft' || e.code === 'ArrowRight') {\n this.dasTimer = 0;\n this.arrTimer = 0;\n }\n });\n }\n\n private onFirstPress(code: string): void {\n if (code === 'KeyP') {\n if (!this.gameOver) this.paused = !this.paused;\n return;\n }\n\n if (this.gameOver) {\n if (code === 'Enter' || code === 'KeyR') this.init();\n return;\n }\n\n if (this.paused || this.flashTimer > 0) return;\n\n switch (code) {\n case 'ArrowLeft':\n this.move(-1);\n this.dasTimer = 0;\n this.arrTimer = 0;\n break;\n case 'ArrowRight':\n this.move(1);\n this.dasTimer = 0;\n this.arrTimer = 0;\n break;\n case 'ArrowDown': this.softDrop(); break;\n case 'ArrowUp': this.rotate(1); break;\n case 'KeyZ': this.rotate(-1); break;\n case 'KeyX': this.rotate(1); break;\n case 'Space': this.hardDrop(); break;\n case 'ShiftLeft':\n case 'ShiftRight':\n case 'KeyC': this.holdPiece(); break;\n }\n }\n\n // ── Actions ─────────────────────────────────────────────────────────────────\n\n private move(dx: number): boolean {\n if (fits(this.board, this.current, dx)) {\n this.current.x += dx;\n if (this.onFloor) this.lockTimer = 0; // reset lock delay on successful move\n return true;\n }\n return false;\n }\n\n private softDrop(): void {\n if (fits(this.board, this.current, 0, 1)) {\n this.current.y++;\n this.score++;\n this.dropCounter = 0;\n this.lockTimer = 0;\n this.updateHUD();\n }\n }\n\n private hardDrop(): void {\n let n = 0;\n while (fits(this.board, this.current, 0, 1)) {\n this.current.y++;\n n++;\n }\n this.score += n * 2;\n this.lock();\n }\n\n private rotate(dir: 1 | -1): void {\n const rotated = dir === 1 ? rotateCW(this.current.shape) : rotateCCW(this.current.shape);\n // Wall-kick offsets: try center, then nudge left/right, then further\n const kicks = [0, -1, 1, -2, 2];\n for (const kick of kicks) {\n if (fits(this.board, this.current, kick, 0, rotated)) {\n this.current.shape = rotated;\n this.current.x += kick;\n if (this.onFloor) this.lockTimer = 0; // reset lock delay on successful rotate\n return;\n }\n }\n }\n\n private holdPiece(): void {\n if (!this.canHold) return;\n const savedType = this.current.type;\n if (this.held) {\n this.current = makePiece(this.held.type);\n } else {\n this.current = this.next;\n this.next = makePiece(this.bag.draw());\n }\n this.held = makePiece(savedType);\n this.canHold = false;\n this.onFloor = false;\n this.lockTimer = 0;\n this.dropCounter = 0;\n }\n\n private lock(): void {\n this.board = stamp(this.board, this.current);\n this.canHold = true;\n this.onFloor = false;\n\n const fr = fullRows(this.board);\n if (fr.length > 0) {\n this.flashRows = fr;\n this.flashTimer = FLASH_MS;\n this.pendingClr = fr.length;\n this.pendingScr = lineScore(fr.length, this.level);\n } else {\n this.spawnNext();\n }\n }\n\n private spawnNext(): void {\n this.current = this.next;\n this.next = makePiece(this.bag.draw());\n this.dropCounter = 0;\n this.lockTimer = 0;\n this.onFloor = false;\n\n if (!fits(this.board, this.current)) {\n this.gameOver = true;\n if (this.score > this.highScore) {\n this.highScore = this.score;\n localStorage.setItem('tetris-hs', String(this.highScore));\n }\n }\n this.updateHUD();\n }\n\n // ── HUD ─────────────────────────────────────────────────────────────────────\n\n private updateHUD(): void {\n const el = (id: string) => document.getElementById(id)!;\n el('score').textContent = this.score.toLocaleString();\n el('lines').textContent = String(this.lines);\n el('level').textContent = String(this.level);\n el('high-score').textContent = Math.max(this.highScore, this.score).toLocaleString();\n }\n\n // ── Rendering ────────────────────────────────────────────────────────────────\n\n private ghostOffset(): number {\n let dy = 0;\n while (fits(this.board, this.current, 0, dy + 1)) dy++;\n return dy;\n }\n\n private drawBlock(\n ctx: CanvasRenderingContext2D,\n x: number,\n y: number,\n color: string,\n alpha = 1,\n ): void {\n const px = x * CELL;\n const py = y * CELL;\n const m = 1; // margin between cells\n const s = CELL - m * 2;\n\n ctx.globalAlpha = alpha;\n\n // Base fill\n ctx.fillStyle = color;\n ctx.fillRect(px + m, py + m, s, s);\n\n // Gradient sheen\n const g = ctx.createLinearGradient(px, py, px, py + CELL);\n g.addColorStop(0, 'rgba(255,255,255,0.38)');\n g.addColorStop(0.40, 'rgba(255,255,255,0.04)');\n g.addColorStop(1, 'rgba(0,0,0,0.22)');\n ctx.fillStyle = g;\n ctx.fillRect(px + m, py + m, s, s);\n\n // Top-left highlight bevel\n ctx.fillStyle = 'rgba(255,255,255,0.55)';\n ctx.fillRect(px + m, py + m, s, 3); // top edge\n ctx.fillRect(px + m, py + m, 3, s); // left edge\n\n // Bottom-right shadow bevel\n ctx.fillStyle = 'rgba(0,0,0,0.35)';\n ctx.fillRect(px + m, py + CELL - m - 3, s, 3); // bottom edge\n ctx.fillRect(px + CELL - m - 3, py + m, 3, s); // right edge\n\n ctx.globalAlpha = 1;\n }\n\n private renderBoard(): void {\n const ctx = this.boardCtx;\n const W = this.boardCanvas.width;\n const H = this.boardCanvas.height;\n\n // Background\n ctx.fillStyle = '#0d1117';\n ctx.fillRect(0, 0, W, H);\n\n // Subtle grid lines\n ctx.strokeStyle = 'rgba(255,255,255,0.04)';\n ctx.lineWidth = 1;\n for (let c = 0; c <= COLS; c++) {\n ctx.beginPath(); ctx.moveTo(c * CELL, 0); ctx.lineTo(c * CELL, H); ctx.stroke();\n }\n for (let r = 0; r <= ROWS; r++) {\n ctx.beginPath(); ctx.moveTo(0, r * CELL); ctx.lineTo(W, r * CELL); ctx.stroke();\n }\n\n // Placed board cells (with flash effect on clearing rows)\n for (let r = 0; r < ROWS; r++) {\n for (let c = 0; c < COLS; c++) {\n const cell = this.board[r][c];\n if (!cell) continue;\n\n if (this.flashRows.includes(r)) {\n // Alternate white / colour every ~35ms\n const t = this.flashTimer / FLASH_MS;\n const flash = Math.floor(t * 8) % 2 === 0;\n this.drawBlock(ctx, c, r, flash ? '#ffffff' : cell);\n } else {\n this.drawBlock(ctx, c, r, cell);\n }\n }\n }\n\n // Ghost & active piece — hidden during flash animation\n if (this.flashTimer <= 0 && !this.gameOver) {\n const gDy = this.ghostOffset();\n\n // Ghost\n if (gDy > 0) {\n this.current.shape.forEach((row, r) => {\n row.forEach((cell, c) => {\n const gy = this.current.y + r + gDy;\n if (cell && gy >= 0) {\n this.drawBlock(ctx, this.current.x + c, gy, this.current.color, 0.18);\n }\n });\n });\n }\n\n // Active piece\n this.current.shape.forEach((row, r) => {\n row.forEach((cell, c) => {\n const py = this.current.y + r;\n if (cell && py >= 0) {\n this.drawBlock(ctx, this.current.x + c, py, this.current.color);\n }\n });\n });\n }\n\n // ── Overlays ──────────────────────────────────────────────────────────────\n\n if (this.paused) {\n ctx.fillStyle = 'rgba(0,0,0,0.72)';\n ctx.fillRect(0, 0, W, H);\n this.cText(ctx, 'PAUSED', W/2, H/2 - 22, '#ffffff', 'bold 34px monospace');\n this.cText(ctx, 'P — resume', W/2, H/2 + 18, '#778899', '15px monospace');\n }\n\n if (this.gameOver) {\n ctx.fillStyle = 'rgba(0,0,0,0.80)';\n ctx.fillRect(0, 0, W, H);\n this.cText(ctx, 'GAME OVER', W/2, H/2 - 46, '#ff4040', 'bold 36px monospace');\n this.cText(ctx, `Score: ${this.score.toLocaleString()}`, W/2, H/2 - 2, '#e0e0e0', '20px monospace');\n this.cText(ctx, 'Enter — play again', W/2, H/2 + 38, '#778899', '14px monospace');\n }\n }\n\n private cText(\n ctx: CanvasRenderingContext2D,\n text: string,\n x: number, y: number,\n color: string, font: string,\n ): void {\n ctx.save();\n ctx.fillStyle = color;\n ctx.font = font;\n ctx.textAlign = 'center';\n ctx.textBaseline = 'middle';\n ctx.fillText(text, x, y);\n ctx.restore();\n }\n\n private renderPreview(\n ctx: CanvasRenderingContext2D,\n canvas: HTMLCanvasElement,\n piece: Piece | null,\n dimmed = false,\n ): void {\n ctx.fillStyle = '#0d1117';\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n if (!piece) return;\n\n const s = piece.shape;\n const ox = Math.floor((4 - s[0].length) / 2);\n const oy = Math.floor((4 - s.length) / 2);\n s.forEach((row, r) => {\n row.forEach((cell, c) => {\n if (cell) this.drawBlock(ctx, ox + c, oy + r, piece.color, dimmed ? 0.38 : 1);\n });\n });\n }\n\n // ── Game Loop ────────────────────────────────────────────────────────────────\n\n private loop(time: number): void {\n const dt = Math.min(time - (this.lastTime || time), 100); // cap at 100ms\n this.lastTime = time;\n\n if (!this.paused && !this.gameOver) {\n // ── Flash timer (line-clear animation) ──────────────────────────────────\n if (this.flashTimer > 0) {\n this.flashTimer -= dt;\n if (this.flashTimer <= 0) {\n this.flashTimer = 0;\n this.board = removeRows(this.board, this.flashRows);\n this.flashRows = [];\n this.lines += this.pendingClr;\n this.score += this.pendingScr;\n this.level = Math.floor(this.lines / 10) + 1;\n this.pendingClr = 0;\n this.pendingScr = 0;\n this.spawnNext();\n }\n } else {\n // ── DAS / ARR horizontal movement ─────────────────────────────────────\n const leftDown = this.keysDown.has('ArrowLeft');\n const rightDown = this.keysDown.has('ArrowRight');\n\n if (leftDown || rightDown) {\n this.dasTimer += dt;\n if (this.dasTimer > DAS) {\n this.arrTimer += dt;\n if (this.arrTimer >= ARR) {\n const steps = Math.floor(this.arrTimer / ARR);\n this.arrTimer -= steps * ARR;\n for (let i = 0; i < steps; i++) {\n if (leftDown && !rightDown) this.move(-1);\n if (rightDown && !leftDown) this.move(1);\n }\n }\n }\n }\n\n // ── Gravity ───────────────────────────────────────────────────────────\n const softDropping = this.keysDown.has('ArrowDown');\n const interval = softDropping ? 50 : dropInterval(this.level);\n\n this.dropCounter += dt;\n if (this.dropCounter >= interval) {\n this.dropCounter = 0;\n if (fits(this.board, this.current, 0, 1)) {\n this.current.y++;\n this.onFloor = false;\n this.lockTimer = 0;\n if (softDropping) {\n this.score++;\n this.updateHUD();\n }\n } else {\n // Piece can't fall — mark as on floor; lock delay handled below\n this.onFloor = true;\n }\n }\n\n // Accumulate lock delay every frame while piece rests on something\n if (this.onFloor && !fits(this.board, this.current, 0, 1)) {\n this.lockTimer += dt;\n if (this.lockTimer >= LOCK_DELAY) {\n this.lock();\n }\n }\n }\n }\n\n // ── Render ────────────────────────────────────────────────────────────────\n this.renderBoard();\n this.renderPreview(this.nextCtx, this.nextCanvas, this.next);\n this.renderPreview(this.holdCtx, this.holdCanvas, this.held, !this.canHold);\n\n requestAnimationFrame(t => this.loop(t));\n }\n}\n\n// ─── Bootstrap ───────────────────────────────────────────────────────────────\n\nwindow.addEventListener('load', () => new TetrisGame());\n","structuredPatch":[{"oldStart":276,"oldLines":6,"newStart":276,"newLines":13,"lines":[" this.arrTimer = 0;"," }"," });","+","+ // Clear all held keys when window loses focus (prevents stuck movement)","+ window.addEventListener('blur', () => {","+ this.keysDown.clear();","+ this.dasTimer = 0;","+ this.arrTimer = 0;","+ });"," }"," "," private onFirstPress(code: string): void {"]}],"userModified":false,"replaceAll":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01DnBEgzTaQ7rgmdcmQLoog7","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01EcCjksftwBjuNwcMtTU6BJ","name":"Bash","input":{"command":"node_modules/.bin/tsc 2>&1 && echo \"✓ Compiled successfully\" && ls -lh /tmp/loop-bench-iuvnueix/dist/","description":"Final compile and verify output"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":962,"cache_read_input_tokens":65059,"cache_creation":{"ephemeral_5m_input_tokens":962,"ephemeral_1h_input_tokens":0},"output_tokens":43,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"fc7d07dd-c570-4cdd-92da-8f8b7875840b"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01EcCjksftwBjuNwcMtTU6BJ","type":"tool_result","content":"✓ Compiled successfully\ntotal 24K\n-rw-r--r-- 1 root root 23K Apr 4 23:10 tetris.js","is_error":false}]},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"39475b4b-88d8-498f-9b26-6e3062b2d16d","timestamp":"2026-04-04T21:10:51.083Z","tool_use_result":{"stdout":"✓ Compiled successfully\ntotal 24K\n-rw-r--r-- 1 root root 23K Apr 4 23:10 tetris.js","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01M1vXvuNWhd4Pcavn5YPGwt","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01RWaWAthBSvc7G1VmHgQYpu","name":"Bash","input":{"command":"ls /tmp/loop-bench-iuvnueix/","description":"Show final project structure"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":167,"cache_read_input_tokens":66021,"cache_creation":{"ephemeral_5m_input_tokens":167,"ephemeral_1h_input_tokens":0},"output_tokens":49,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"d07c7b7d-1684-4591-a518-452d090103fb"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01RWaWAthBSvc7G1VmHgQYpu","type":"tool_result","content":"dist\nindex.html\nnode_modules\npackage.json\npackage-lock.json\nsrc\ntsconfig.json","is_error":false}]},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"9b87b6d4-280f-42b2-9ef8-3a61a5993409","timestamp":"2026-04-04T21:10:52.857Z","tool_use_result":{"stdout":"dist\nindex.html\nnode_modules\npackage.json\npackage-lock.json\nsrc\ntsconfig.json","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_017vCaQxvaK7CM6mCKgXssb4","type":"message","role":"assistant","content":[{"type":"text","text":"Let me do one final read of the complete TS source to verify everything is coherent end-to-end:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":290,"cache_read_input_tokens":66021,"cache_creation":{"ephemeral_5m_input_tokens":290,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"c380eefe-ae7e-406d-8fff-a53501da2c7f"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_017vCaQxvaK7CM6mCKgXssb4","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01UpzqYHSHbhzizdsodqCzJj","name":"Read","input":{"file_path":"/tmp/loop-bench-iuvnueix/src/tetris.ts","offset":360,"limit":120},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":290,"cache_read_input_tokens":66021,"cache_creation":{"ephemeral_5m_input_tokens":290,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"f92d400e-2250-4fe0-b6ce-7f607ec44bdf"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01UpzqYHSHbhzizdsodqCzJj","type":"tool_result","content":"360\t this.current.shape = rotated;\n361\t this.current.x += kick;\n362\t if (this.onFloor) this.lockTimer = 0; // reset lock delay on successful rotate\n363\t return;\n364\t }\n365\t }\n366\t }\n367\t\n368\t private holdPiece(): void {\n369\t if (!this.canHold) return;\n370\t const savedType = this.current.type;\n371\t if (this.held) {\n372\t this.current = makePiece(this.held.type);\n373\t } else {\n374\t this.current = this.next;\n375\t this.next = makePiece(this.bag.draw());\n376\t }\n377\t this.held = makePiece(savedType);\n378\t this.canHold = false;\n379\t this.onFloor = false;\n380\t this.lockTimer = 0;\n381\t this.dropCounter = 0;\n382\t }\n383\t\n384\t private lock(): void {\n385\t this.board = stamp(this.board, this.current);\n386\t this.canHold = true;\n387\t this.onFloor = false;\n388\t\n389\t const fr = fullRows(this.board);\n390\t if (fr.length > 0) {\n391\t this.flashRows = fr;\n392\t this.flashTimer = FLASH_MS;\n393\t this.pendingClr = fr.length;\n394\t this.pendingScr = lineScore(fr.length, this.level);\n395\t } else {\n396\t this.spawnNext();\n397\t }\n398\t }\n399\t\n400\t private spawnNext(): void {\n401\t this.current = this.next;\n402\t this.next = makePiece(this.bag.draw());\n403\t this.dropCounter = 0;\n404\t this.lockTimer = 0;\n405\t this.onFloor = false;\n406\t\n407\t if (!fits(this.board, this.current)) {\n408\t this.gameOver = true;\n409\t if (this.score > this.highScore) {\n410\t this.highScore = this.score;\n411\t localStorage.setItem('tetris-hs', String(this.highScore));\n412\t }\n413\t }\n414\t this.updateHUD();\n415\t }\n416\t\n417\t // ── HUD ─────────────────────────────────────────────────────────────────────\n418\t\n419\t private updateHUD(): void {\n420\t const el = (id: string) => document.getElementById(id)!;\n421\t el('score').textContent = this.score.toLocaleString();\n422\t el('lines').textContent = String(this.lines);\n423\t el('level').textContent = String(this.level);\n424\t el('high-score').textContent = Math.max(this.highScore, this.score).toLocaleString();\n425\t }\n426\t\n427\t // ── Rendering ────────────────────────────────────────────────────────────────\n428\t\n429\t private ghostOffset(): number {\n430\t let dy = 0;\n431\t while (fits(this.board, this.current, 0, dy + 1)) dy++;\n432\t return dy;\n433\t }\n434\t\n435\t private drawBlock(\n436\t ctx: CanvasRenderingContext2D,\n437\t x: number,\n438\t y: number,\n439\t color: string,\n440\t alpha = 1,\n441\t ): void {\n442\t const px = x * CELL;\n443\t const py = y * CELL;\n444\t const m = 1; // margin between cells\n445\t const s = CELL - m * 2;\n446\t\n447\t ctx.globalAlpha = alpha;\n448\t\n449\t // Base fill\n450\t ctx.fillStyle = color;\n451\t ctx.fillRect(px + m, py + m, s, s);\n452\t\n453\t // Gradient sheen\n454\t const g = ctx.createLinearGradient(px, py, px, py + CELL);\n455\t g.addColorStop(0, 'rgba(255,255,255,0.38)');\n456\t g.addColorStop(0.40, 'rgba(255,255,255,0.04)');\n457\t g.addColorStop(1, 'rgba(0,0,0,0.22)');\n458\t ctx.fillStyle = g;\n459\t ctx.fillRect(px + m, py + m, s, s);\n460\t\n461\t // Top-left highlight bevel\n462\t ctx.fillStyle = 'rgba(255,255,255,0.55)';\n463\t ctx.fillRect(px + m, py + m, s, 3); // top edge\n464\t ctx.fillRect(px + m, py + m, 3, s); // left edge\n465\t\n466\t // Bottom-right shadow bevel\n467\t ctx.fillStyle = 'rgba(0,0,0,0.35)';\n468\t ctx.fillRect(px + m, py + CELL - m - 3, s, 3); // bottom edge\n469\t ctx.fillRect(px + CELL - m - 3, py + m, 3, s); // right edge\n470\t\n471\t ctx.globalAlpha = 1;\n472\t }\n473\t\n474\t private renderBoard(): void {\n475\t const ctx = this.boardCtx;\n476\t const W = this.boardCanvas.width;\n477\t const H = this.boardCanvas.height;\n478\t\n479\t // Background"}]},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"f2e4c8b9-bd7c-4186-bafb-e7bbe8a73175","timestamp":"2026-04-04T21:10:55.421Z","tool_use_result":{"type":"text","file":{"filePath":"/tmp/loop-bench-iuvnueix/src/tetris.ts","content":" this.current.shape = rotated;\n this.current.x += kick;\n if (this.onFloor) this.lockTimer = 0; // reset lock delay on successful rotate\n return;\n }\n }\n }\n\n private holdPiece(): void {\n if (!this.canHold) return;\n const savedType = this.current.type;\n if (this.held) {\n this.current = makePiece(this.held.type);\n } else {\n this.current = this.next;\n this.next = makePiece(this.bag.draw());\n }\n this.held = makePiece(savedType);\n this.canHold = false;\n this.onFloor = false;\n this.lockTimer = 0;\n this.dropCounter = 0;\n }\n\n private lock(): void {\n this.board = stamp(this.board, this.current);\n this.canHold = true;\n this.onFloor = false;\n\n const fr = fullRows(this.board);\n if (fr.length > 0) {\n this.flashRows = fr;\n this.flashTimer = FLASH_MS;\n this.pendingClr = fr.length;\n this.pendingScr = lineScore(fr.length, this.level);\n } else {\n this.spawnNext();\n }\n }\n\n private spawnNext(): void {\n this.current = this.next;\n this.next = makePiece(this.bag.draw());\n this.dropCounter = 0;\n this.lockTimer = 0;\n this.onFloor = false;\n\n if (!fits(this.board, this.current)) {\n this.gameOver = true;\n if (this.score > this.highScore) {\n this.highScore = this.score;\n localStorage.setItem('tetris-hs', String(this.highScore));\n }\n }\n this.updateHUD();\n }\n\n // ── HUD ─────────────────────────────────────────────────────────────────────\n\n private updateHUD(): void {\n const el = (id: string) => document.getElementById(id)!;\n el('score').textContent = this.score.toLocaleString();\n el('lines').textContent = String(this.lines);\n el('level').textContent = String(this.level);\n el('high-score').textContent = Math.max(this.highScore, this.score).toLocaleString();\n }\n\n // ── Rendering ────────────────────────────────────────────────────────────────\n\n private ghostOffset(): number {\n let dy = 0;\n while (fits(this.board, this.current, 0, dy + 1)) dy++;\n return dy;\n }\n\n private drawBlock(\n ctx: CanvasRenderingContext2D,\n x: number,\n y: number,\n color: string,\n alpha = 1,\n ): void {\n const px = x * CELL;\n const py = y * CELL;\n const m = 1; // margin between cells\n const s = CELL - m * 2;\n\n ctx.globalAlpha = alpha;\n\n // Base fill\n ctx.fillStyle = color;\n ctx.fillRect(px + m, py + m, s, s);\n\n // Gradient sheen\n const g = ctx.createLinearGradient(px, py, px, py + CELL);\n g.addColorStop(0, 'rgba(255,255,255,0.38)');\n g.addColorStop(0.40, 'rgba(255,255,255,0.04)');\n g.addColorStop(1, 'rgba(0,0,0,0.22)');\n ctx.fillStyle = g;\n ctx.fillRect(px + m, py + m, s, s);\n\n // Top-left highlight bevel\n ctx.fillStyle = 'rgba(255,255,255,0.55)';\n ctx.fillRect(px + m, py + m, s, 3); // top edge\n ctx.fillRect(px + m, py + m, 3, s); // left edge\n\n // Bottom-right shadow bevel\n ctx.fillStyle = 'rgba(0,0,0,0.35)';\n ctx.fillRect(px + m, py + CELL - m - 3, s, 3); // bottom edge\n ctx.fillRect(px + CELL - m - 3, py + m, 3, s); // right edge\n\n ctx.globalAlpha = 1;\n }\n\n private renderBoard(): void {\n const ctx = this.boardCtx;\n const W = this.boardCanvas.width;\n const H = this.boardCanvas.height;\n\n // Background","numLines":120,"startLine":360,"totalLines":674}}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01WhrQNPKGjRptJZTSaawCZi","type":"message","role":"assistant","content":[{"type":"text","text":"Everything looks solid. Let me now update `package.json` to add a convenient build script, then do a final structure check:"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":1640,"cache_read_input_tokens":66311,"cache_creation":{"ephemeral_5m_input_tokens":1640,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"c36f6c8b-2044-432e-844a-b2109b619ad7"} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01WhrQNPKGjRptJZTSaawCZi","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01Xm2eoxXqmaazyGNPWk9iJS","name":"Edit","input":{"replace_all":false,"file_path":"/tmp/loop-bench-iuvnueix/package.json","old_string":" \"scripts\": {\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },","new_string":" \"scripts\": {\n \"build\": \"tsc\",\n \"watch\": \"tsc --watch\",\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":1640,"cache_read_input_tokens":66311,"cache_creation":{"ephemeral_5m_input_tokens":1640,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"f43d286c-ebe2-4ae5-bdb0-aabe9b2784bb"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01Xm2eoxXqmaazyGNPWk9iJS","type":"tool_result","content":"The file /tmp/loop-bench-iuvnueix/package.json has been updated successfully."}]},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"fee45dff-eca4-4b92-a6ab-76ef37618ff6","timestamp":"2026-04-04T21:10:59.206Z","tool_use_result":{"filePath":"/tmp/loop-bench-iuvnueix/package.json","oldString":" \"scripts\": {\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },","newString":" \"scripts\": {\n \"build\": \"tsc\",\n \"watch\": \"tsc --watch\",\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },","originalFile":"{\n \"name\": \"loop-bench-iuvnueix\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },\n \"keywords\": [],\n \"author\": \"\",\n \"license\": \"ISC\",\n \"type\": \"commonjs\",\n \"devDependencies\": {\n \"@eslint/js\": \"^10.0.1\",\n \"@playwright/test\": \"^1.59.1\",\n \"@types/node\": \"^25.5.2\",\n \"eslint\": \"^10.2.0\",\n \"typescript\": \"^6.0.2\"\n }\n}\n","structuredPatch":[{"oldStart":4,"oldLines":6,"newStart":4,"newLines":8,"lines":[" \"description\": \"\","," \"main\": \"index.js\","," \"scripts\": {","+ \"build\": \"tsc\",","+ \"watch\": \"tsc --watch\","," \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\""," },"," \"keywords\": [],"]}],"userModified":false,"replaceAll":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01QHG7wrsiUe3DspnMTx6zfC","type":"message","role":"assistant","content":[{"type":"tool_use","id":"toolu_01PMZBgwob772Zz41Z6ZTUht","name":"Bash","input":{"command":"echo \"=== Project structure ===\" && find /tmp/loop-bench-iuvnueix -not -path '*/node_modules/*' -not -path '*/.git/*' | sort && echo \"\" && echo \"=== Compiled JS size ===\" && wc -l /tmp/loop-bench-iuvnueix/dist/tetris.js && echo \"\" && echo \"=== TS source lines ===\" && wc -l /tmp/loop-bench-iuvnueix/src/tetris.ts","description":"Show full project structure and file sizes"},"caller":{"type":"direct"}}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":259,"cache_read_input_tokens":67951,"cache_creation":{"ephemeral_5m_input_tokens":259,"ephemeral_1h_input_tokens":0},"output_tokens":49,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"c1ae3ffd-bf3b-465d-bbf4-7335606fa6f8"} +{"type":"user","message":{"role":"user","content":[{"tool_use_id":"toolu_01PMZBgwob772Zz41Z6ZTUht","type":"tool_result","content":"=== Project structure ===\n/tmp/loop-bench-iuvnueix\n/tmp/loop-bench-iuvnueix/dist\n/tmp/loop-bench-iuvnueix/dist/tetris.js\n/tmp/loop-bench-iuvnueix/index.html\n/tmp/loop-bench-iuvnueix/node_modules\n/tmp/loop-bench-iuvnueix/package.json\n/tmp/loop-bench-iuvnueix/package-lock.json\n/tmp/loop-bench-iuvnueix/src\n/tmp/loop-bench-iuvnueix/src/tetris.ts\n/tmp/loop-bench-iuvnueix/tsconfig.json\n\n=== Compiled JS size ===\n577 /tmp/loop-bench-iuvnueix/dist/tetris.js\n\n=== TS source lines ===\n673 /tmp/loop-bench-iuvnueix/src/tetris.ts","is_error":false}]},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"05364b99-e6a5-4999-979b-6e42a0f55972","timestamp":"2026-04-04T21:11:02.348Z","tool_use_result":{"stdout":"=== Project structure ===\n/tmp/loop-bench-iuvnueix\n/tmp/loop-bench-iuvnueix/dist\n/tmp/loop-bench-iuvnueix/dist/tetris.js\n/tmp/loop-bench-iuvnueix/index.html\n/tmp/loop-bench-iuvnueix/node_modules\n/tmp/loop-bench-iuvnueix/package.json\n/tmp/loop-bench-iuvnueix/package-lock.json\n/tmp/loop-bench-iuvnueix/src\n/tmp/loop-bench-iuvnueix/src/tetris.ts\n/tmp/loop-bench-iuvnueix/tsconfig.json\n\n=== Compiled JS size ===\n577 /tmp/loop-bench-iuvnueix/dist/tetris.js\n\n=== TS source lines ===\n673 /tmp/loop-bench-iuvnueix/src/tetris.ts","stderr":"","interrupted":false,"isImage":false,"noOutputExpected":false}} +{"type":"assistant","message":{"model":"claude-sonnet-4-6","id":"msg_01Kk7SHMFqmAXH32jmM4Co5M","type":"message","role":"assistant","content":[{"type":"text","text":"Everything is in order. Here's a complete summary of everything that was built:\n\n---\n\n## Tetris — complete ✅\n\n### Files created\n\n| File | Purpose |\n|---|---|\n| `index.html` | Game page — layout, dark theme CSS, loads compiled JS |\n| `src/tetris.ts` | Full TypeScript source (~673 lines) |\n| `dist/tetris.js` | Compiled, ready-to-run JavaScript |\n| `tsconfig.json` | TypeScript compiler config |\n\n### To play\nOpen `index.html` in any modern browser (or serve from a local web server).\n\nTo rebuild after editing the source:\n```bash\nnpm run build\n```\n\n---\n\n### Features implemented\n\n**Core mechanics**\n- All 7 tetrominoes (I, O, T, S, Z, J, L) with authentic colours\n- **7-bag randomiser** — guarantees a fair distribution; no long droughts\n- **CW & CCW rotation** with wall-kick offsets (tries ±1, ±2 nudges so pieces don't get stuck at walls)\n- **Lock delay** (500 ms) — piece doesn't instantly lock when it touches the floor, giving you time to slide or rotate\n- **Ghost piece** — faint outline shows exactly where the piece will land\n\n**Gameplay**\n- **Soft drop** (↓) — 20× gravity, +1 point per cell\n- **Hard drop** (Space) — instantly slams the piece down, +2 points per cell\n- **Hold** (C / Shift) — swap the active piece to a hold slot; can only use once per piece\n- **Line clearing** with a white-flash animation before rows vanish\n- **Scoring** — 100 / 300 / 500 / 800 × level for 1 / 2 / 3 / 4 lines (standard Guideline values)\n- **Levelling** — every 10 lines cleared advances the level\n- **Speed curve** — 13-step drop interval table (800 ms → 50 ms) matching standard Tetris feel\n- **High score** persisted in `localStorage`\n\n**Controls**\n| Key | Action |\n|---|---|\n| `←` `→` | Move (with DAS + ARR auto-repeat) |\n| `↑` or `X` | Rotate clockwise |\n| `Z` | Rotate counter-clockwise |\n| `↓` | Soft drop |\n| `Space` | Hard drop |\n| `C` / `Shift` | Hold piece |\n| `P` | Pause / resume |\n| `R` / `Enter` | Restart (on game over) |"}],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":1,"cache_creation_input_tokens":444,"cache_read_input_tokens":68210,"cache_creation":{"ephemeral_5m_input_tokens":444,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard","inference_geo":"not_available"},"context_management":null},"parent_tool_use_id":null,"session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","uuid":"e139589e-3e45-4a75-8686-ea8959e9a2a1"} +{"type":"result","subtype":"success","is_error":false,"duration_ms":820517,"duration_api_ms":816433,"num_turns":29,"result":"Everything is in order. Here's a complete summary of everything that was built:\n\n---\n\n## Tetris — complete ✅\n\n### Files created\n\n| File | Purpose |\n|---|---|\n| `index.html` | Game page — layout, dark theme CSS, loads compiled JS |\n| `src/tetris.ts` | Full TypeScript source (~673 lines) |\n| `dist/tetris.js` | Compiled, ready-to-run JavaScript |\n| `tsconfig.json` | TypeScript compiler config |\n\n### To play\nOpen `index.html` in any modern browser (or serve from a local web server).\n\nTo rebuild after editing the source:\n```bash\nnpm run build\n```\n\n---\n\n### Features implemented\n\n**Core mechanics**\n- All 7 tetrominoes (I, O, T, S, Z, J, L) with authentic colours\n- **7-bag randomiser** — guarantees a fair distribution; no long droughts\n- **CW & CCW rotation** with wall-kick offsets (tries ±1, ±2 nudges so pieces don't get stuck at walls)\n- **Lock delay** (500 ms) — piece doesn't instantly lock when it touches the floor, giving you time to slide or rotate\n- **Ghost piece** — faint outline shows exactly where the piece will land\n\n**Gameplay**\n- **Soft drop** (↓) — 20× gravity, +1 point per cell\n- **Hard drop** (Space) — instantly slams the piece down, +2 points per cell\n- **Hold** (C / Shift) — swap the active piece to a hold slot; can only use once per piece\n- **Line clearing** with a white-flash animation before rows vanish\n- **Scoring** — 100 / 300 / 500 / 800 × level for 1 / 2 / 3 / 4 lines (standard Guideline values)\n- **Levelling** — every 10 lines cleared advances the level\n- **Speed curve** — 13-step drop interval table (800 ms → 50 ms) matching standard Tetris feel\n- **High score** persisted in `localStorage`\n\n**Controls**\n| Key | Action |\n|---|---|\n| `←` `→` | Move (with DAS + ARR auto-repeat) |\n| `↑` or `X` | Rotate clockwise |\n| `Z` | Rotate counter-clockwise |\n| `↓` | Soft drop |\n| `Space` | Hard drop |\n| `C` / `Shift` | Hold piece |\n| `P` | Pause / resume |\n| `R` / `Enter` | Restart (on game over) |","stop_reason":"end_turn","session_id":"86d862fe-01d8-4269-b0c7-f5a0e0155537","total_cost_usd":1.78234365,"usage":{"input_tokens":34,"cache_creation_input_tokens":108669,"cache_read_input_tokens":1477443,"output_tokens":62100,"server_tool_use":{"web_search_requests":0,"web_fetch_requests":0},"service_tier":"standard","cache_creation":{"ephemeral_1h_input_tokens":0,"ephemeral_5m_input_tokens":108669},"inference_geo":"","iterations":[],"speed":"standard"},"modelUsage":{"claude-sonnet-4-6":{"inputTokens":34,"outputTokens":62100,"cacheReadInputTokens":1477443,"cacheCreationInputTokens":108669,"webSearchRequests":0,"costUSD":1.78234365,"contextWindow":200000,"maxOutputTokens":32000}},"permission_denials":[],"terminal_reason":"completed","fast_mode_state":"off","uuid":"ca656ed4-b951-43ce-862a-260ed6d3ed39"}

Impressum · Datenschutz