1 Commits

Author SHA1 Message Date
c66bb6a9a8 feat: adiciona atividade Padrões de reconhecimento de padrões
Nova atividade em app/src/atividades/programacao/padroes/, seguindo o padrão arquitetural das demais (BaseGameScene + Blockly + interpretador):

- Cena Phaser (PadroesScene), blocos customizados, API do interpretador, validador e UI com painéis ENTRADA/SAÍDA.
- Fase 1 (teste): loop "enquanto" que verifica se a ENTRADA contém apenas letras de A-Z (caso "ABC123" -> "INVÁLIDO").
- Registro em gameRegistry.js, rota em App.jsx e ajuste do teste do registry (EXPECTED_IDS -> 10 jogos).
2026-06-27 18:30:53 -03:00
16 changed files with 990 additions and 168 deletions

View File

@@ -1 +0,0 @@
{}

View File

@@ -1 +0,0 @@
{}

View File

@@ -83,7 +83,6 @@
"react": "^19.2.0",
"react-confetti": "^6.4.0",
"react-dom": "^19.2.0",
"react-intl": "^10.1.14",
"react-resizable-panels": "^3.0.6",
"react-router-dom": "^7.9.4",
"react-shepherd": "^6.1.9",

269
app/pnpm-lock.yaml generated
View File

@@ -77,9 +77,6 @@ importers:
react-dom:
specifier: ^19.2.0
version: 19.2.0(react@19.2.0)
react-intl:
specifier: ^10.1.14
version: 10.1.14(@types/react@19.2.2)(react@19.2.0)
react-resizable-panels:
specifier: ^3.0.6
version: 3.0.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
@@ -232,10 +229,18 @@ packages:
'@asamuzakjp/nwsapi@2.3.9':
resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==}
'@babel/code-frame@7.27.1':
resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
engines: {node: '>=6.9.0'}
'@babel/code-frame@7.29.0':
resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==}
engines: {node: '>=6.9.0'}
'@babel/compat-data@7.28.4':
resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==}
engines: {node: '>=6.9.0'}
'@babel/compat-data@7.29.3':
resolution: {integrity: sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==}
engines: {node: '>=6.9.0'}
@@ -244,6 +249,10 @@ packages:
resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==}
engines: {node: '>=6.9.0'}
'@babel/generator@7.28.3':
resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==}
engines: {node: '>=6.9.0'}
'@babel/generator@7.29.1':
resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==}
engines: {node: '>=6.9.0'}
@@ -252,6 +261,10 @@ packages:
resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==}
engines: {node: '>=6.9.0'}
'@babel/helper-compilation-targets@7.27.2':
resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==}
engines: {node: '>=6.9.0'}
'@babel/helper-compilation-targets@7.28.6':
resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==}
engines: {node: '>=6.9.0'}
@@ -281,10 +294,20 @@ packages:
resolution: {integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==}
engines: {node: '>=6.9.0'}
'@babel/helper-module-imports@7.27.1':
resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==}
engines: {node: '>=6.9.0'}
'@babel/helper-module-imports@7.28.6':
resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==}
engines: {node: '>=6.9.0'}
'@babel/helper-module-transforms@7.28.3':
resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
'@babel/helper-module-transforms@7.28.6':
resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==}
engines: {node: '>=6.9.0'}
@@ -746,10 +769,18 @@ packages:
resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==}
engines: {node: '>=6.9.0'}
'@babel/template@7.27.2':
resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==}
engines: {node: '>=6.9.0'}
'@babel/template@7.28.6':
resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==}
engines: {node: '>=6.9.0'}
'@babel/traverse@7.28.4':
resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==}
engines: {node: '>=6.9.0'}
'@babel/traverse@7.29.0':
resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==}
engines: {node: '>=6.9.0'}
@@ -1096,18 +1127,6 @@ packages:
'@floating-ui/utils@0.2.10':
resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==}
'@formatjs/fast-memoize@3.1.6':
resolution: {integrity: sha512-H5aexk1Le7T9TPmscacZ+1pR6CTa2n1wq+HDVGXhH8TzUlQQpeXzZs91dRtmFHrbeNbjPFPfQujUqm7MHgVoXQ==}
'@formatjs/icu-messageformat-parser@3.5.12':
resolution: {integrity: sha512-YyzzxVgYJ8DELmmkhn0Yr0rUj0dTJFf9Jp628K3S0ysInBWxLVDOS8i3RP91cCp4DMK4WYb4cVMhWA9i4knSJg==}
'@formatjs/icu-skeleton-parser@2.1.10':
resolution: {integrity: sha512-XuSva+8ZGawk8VnD5VD6UeH8KarQ/Z022zgjHDoHmlNiAewstXuuzXc0Hk5pGFSdG+nNw5bfJKXqj1ZXHn9yUA==}
'@formatjs/intl@4.1.14':
resolution: {integrity: sha512-hl8dgvbV2Te/KPnCud+nuBc+z8qW8fGreiun7ul5LopIHkbP0izylvnhIFDL2dQMeMPdrKCAXhJkYDeoxVuDRw==}
'@fortawesome/fontawesome-free@6.7.2':
resolution: {integrity: sha512-JUOtgFW6k9u4Y+xeIaEiLr3+cjoUPiAuLXoyKOJSia6Duzb7pq+A76P9ZdPDoAoxHdHzq6gE9/jKBGXlZT8FbA==}
engines: {node: '>=6'}
@@ -1158,92 +1177,78 @@ packages:
resolution: {integrity: sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ==}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-arm@1.2.3':
resolution: {integrity: sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA==}
cpu: [arm]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-ppc64@1.2.3':
resolution: {integrity: sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-s390x@1.2.3':
resolution: {integrity: sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w==}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linux-x64@1.2.3':
resolution: {integrity: sha512-3JU7LmR85K6bBiRzSUc/Ff9JBVIFVvq6bomKE0e63UXGeRw2HPVEjoJke1Yx+iU4rL7/7kUjES4dZ/81Qjhyxg==}
cpu: [x64]
os: [linux]
libc: [glibc]
'@img/sharp-libvips-linuxmusl-arm64@1.2.3':
resolution: {integrity: sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw==}
cpu: [arm64]
os: [linux]
libc: [musl]
'@img/sharp-libvips-linuxmusl-x64@1.2.3':
resolution: {integrity: sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g==}
cpu: [x64]
os: [linux]
libc: [musl]
'@img/sharp-linux-arm64@0.34.4':
resolution: {integrity: sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@img/sharp-linux-arm@0.34.4':
resolution: {integrity: sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm]
os: [linux]
libc: [glibc]
'@img/sharp-linux-ppc64@0.34.4':
resolution: {integrity: sha512-F4PDtF4Cy8L8hXA2p3TO6s4aDt93v+LKmpcYFLAVdkkD3hSxZzee0rh6/+94FpAynsuMpLX5h+LRsSG3rIciUQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@img/sharp-linux-s390x@0.34.4':
resolution: {integrity: sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@img/sharp-linux-x64@0.34.4':
resolution: {integrity: sha512-ZfGtcp2xS51iG79c6Vhw9CWqQC8l2Ot8dygxoDoIQPTat/Ov3qAa8qpxSrtAEAJW+UjTXc4yxCjNfxm4h6Xm2A==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
libc: [glibc]
'@img/sharp-linuxmusl-arm64@0.34.4':
resolution: {integrity: sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
libc: [musl]
'@img/sharp-linuxmusl-x64@0.34.4':
resolution: {integrity: sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
libc: [musl]
'@img/sharp-wasm32@0.34.4':
resolution: {integrity: sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA==}
@@ -1457,145 +1462,121 @@ packages:
resolution: {integrity: sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==}
cpu: [arm]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm-gnueabihf@4.60.3':
resolution: {integrity: sha512-DV6fJoxEYWJOvaZIsok7KrYl0tPvga5OZ2yvKHNNYyk/2roMLqQAbGhr78EQ5YhHpnhLKJD3S1WFusAkmUuV5g==}
cpu: [arm]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm-musleabihf@4.52.5':
resolution: {integrity: sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==}
cpu: [arm]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-arm-musleabihf@4.60.3':
resolution: {integrity: sha512-mQKoJAzvuOs6F+TZybQO4GOTSMUu7v0WdxEk24krQ/uUxXoPTtHjuaUuPmFhtBcM4K0ons8nrE3JyhTuCFtT/w==}
cpu: [arm]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-arm64-gnu@4.52.5':
resolution: {integrity: sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm64-gnu@4.60.3':
resolution: {integrity: sha512-Whjj2qoiJ6+OOJMGptTYazaJvjOJm+iKHpXQM1P3LzGjt7Ff++Tp7nH4N8J/BUA7R9IHfDyx4DJIflifwnbmIA==}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm64-musl@4.52.5':
resolution: {integrity: sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==}
cpu: [arm64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-arm64-musl@4.60.3':
resolution: {integrity: sha512-4YTNHKqGng5+yiZt3mg77nmyuCfmNfX4fPmyUapBcIk+BdwSwmCWGXOUxhXbBEkFHtoN5boLj/5NON+u5QC9tg==}
cpu: [arm64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-loong64-gnu@4.52.5':
resolution: {integrity: sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==}
cpu: [loong64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-loong64-gnu@4.60.3':
resolution: {integrity: sha512-SU3kNlhkpI4UqlUc2VXPGK9o886ZsSeGfMAX2ba2b8DKmMXq4AL7KUrkSWVbb7koVqx41Yczx6dx5PNargIrEA==}
cpu: [loong64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-loong64-musl@4.60.3':
resolution: {integrity: sha512-6lDLl5h4TXpB1mTf2rQWnAk/LcXrx9vBfu/DT5TIPhvMhRWaZ5MxkIc8u4lJAmBo6klTe1ywXIUHFjylW505sg==}
cpu: [loong64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-ppc64-gnu@4.52.5':
resolution: {integrity: sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-ppc64-gnu@4.60.3':
resolution: {integrity: sha512-BMo8bOw8evlup/8G+cj5xWtPyp93xPdyoSN16Zy90Q2QZ0ZYRhCt6ZJSwbrRzG9HApFabjwj2p25TUPDWrhzqQ==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-ppc64-musl@4.60.3':
resolution: {integrity: sha512-E0L8X1dZN1/Rph+5VPF6Xj2G7JJvMACVXtamTJIDrVI44Y3K+G8gQaMEAavbqCGTa16InptiVrX6eM6pmJ+7qA==}
cpu: [ppc64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-riscv64-gnu@4.52.5':
resolution: {integrity: sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-riscv64-gnu@4.60.3':
resolution: {integrity: sha512-oZJ/WHaVfHUiRAtmTAeo3DcevNsVvH8mbvodjZy7D5QKvCefO371SiKRpxoDcCxB3PTRTLayWBkvmDQKTcX/sw==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-riscv64-musl@4.52.5':
resolution: {integrity: sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==}
cpu: [riscv64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-riscv64-musl@4.60.3':
resolution: {integrity: sha512-Dhbyh7j9FybM3YaTgaHmVALwA8AkUwTPccyCQ79TG9AJUsMQqgN1DDEZNr4+QUfwiWvLDumW5vdwzoeUF+TNxQ==}
cpu: [riscv64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-s390x-gnu@4.52.5':
resolution: {integrity: sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-s390x-gnu@4.60.3':
resolution: {integrity: sha512-cJd1X5XhHHlltkaypz1UcWLA8AcoIi1aWhsvaWDskD1oz2eKCypnqvTQ8ykMNI0RSmm7NkTdSqSSD7zM0xa6Ig==}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.52.5':
resolution: {integrity: sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==}
cpu: [x64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.60.3':
resolution: {integrity: sha512-DAZDBHQfG2oQuhY7mc6I3/qB4LU2fQCjRvxbDwd/Jdvb9fypP4IJ4qmtu6lNjes6B531AI8cg1aKC2di97bUxA==}
cpu: [x64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-musl@4.52.5':
resolution: {integrity: sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==}
cpu: [x64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-x64-musl@4.60.3':
resolution: {integrity: sha512-cRxsE8c13mZOh3vP+wLDxpQBRrOHDIGOWyDL93Sy0Ga8y515fBcC2pjUfFwUe5T7tqvTvWbCpg1URM/AXdWIXA==}
cpu: [x64]
os: [linux]
libc: [musl]
'@rollup/rollup-openbsd-x64@4.60.3':
resolution: {integrity: sha512-QaWcIgRxqEdQdhJqW4DJctsH6HCmo5vHxY0krHSX4jMtOqfzC+dqDGuHM87bu4H8JBeibWx7jFz+h6/4C8wA5Q==}
@@ -1706,19 +1687,15 @@ packages:
'@tsparticles/basic@3.9.1':
resolution: {integrity: sha512-ijr2dHMx0IQHqhKW3qA8tfwrR2XYbbWYdaJMQuBo2CkwBVIhZ76U+H20Y492j/NXpd1FUnt2aC0l4CEVGVGdeQ==}
deprecated: Please update to version 4.1.1
'@tsparticles/confetti@3.9.1':
resolution: {integrity: sha512-S0Q6iBqQvcCaOzmnddmh41RHeLwzSdkLq8hU3Ryokmb9eqoS9MQdYz2tjUHHdTap+YLPlp64SZ15sC4C9ulFbA==}
deprecated: Please update to version 4.1.1
'@tsparticles/engine@3.9.1':
resolution: {integrity: sha512-DpdgAhWMZ3Eh2gyxik8FXS6BKZ8vyea+Eu5BC4epsahqTGY9V3JGGJcXC6lRJx6cPMAx1A0FaQAojPF3v6rkmQ==}
deprecated: Please update to version 4.1.1
'@tsparticles/move-base@3.9.1':
resolution: {integrity: sha512-X4huBS27d8srpxwOxliWPUt+NtCwY+8q/cx1DvQxyqmTA8VFCGpcHNwtqiN+9JicgzOvSuaORVqUgwlsc7h4pQ==}
deprecated: this package was deprecated in tsparticles v4, replaced by @tsparticles/plugin-move
'@tsparticles/plugin-emitters@3.9.1':
resolution: {integrity: sha512-h7opR8SoFWBmVHceDLJUerLENaPfkJSh2zQYvzmLj2L+V3VLS1QDgty+4QZVeZfqNROmgQw2eLFA5El1E0sqqw==}
@@ -1728,83 +1705,63 @@ packages:
'@tsparticles/plugin-hsl-color@3.9.1':
resolution: {integrity: sha512-jJd1iGgRwX6eeNjc1zUXiJivaqC5UE+SC2A3/NtHwwoQrkfxGWmRHOsVyLnOBRcCPgBp/FpdDe6DIDjCMO715w==}
deprecated: Please update to version 4.1.1
'@tsparticles/plugin-motion@3.9.1':
resolution: {integrity: sha512-I/356NHCiMUgFzWjAHYKO7YvBqKtHSktIPgTRruqlruyrAcwzjkT55ZQ1K5EcJLWETkF1bfG2VpJBRu8ksf9mw==}
deprecated: Please update to version 4.1.1
'@tsparticles/plugin-rgb-color@3.9.1':
resolution: {integrity: sha512-SBxk7f1KBfXeTnnklbE2Hx4jBgh6I6HOtxb+Os1gTp0oaghZOkWcCD2dP4QbUu7fVNCMOcApPoMNC8RTFcy9wQ==}
deprecated: Please update to version 4.1.1
'@tsparticles/shape-cards@3.9.1':
resolution: {integrity: sha512-/tQtGh6xC3UmKU2WO7VM5RoAnsvFvPkXcCJJHAQ6AIyWUKVWBrVuewF0ZbJQlNhWCEW/aqE199LuDAewqYAQ5A==}
deprecated: Please update to version 4.1.1
'@tsparticles/shape-circle@3.9.1':
resolution: {integrity: sha512-DqZFLjbuhVn99WJ+A9ajz9YON72RtCcvubzq6qfjFmtwAK7frvQeb6iDTp6Ze9FUipluxVZWVRG4vWTxi2B+/g==}
deprecated: Please update to version 4.1.1
'@tsparticles/shape-emoji@3.9.1':
resolution: {integrity: sha512-ifvY63usuT+hipgVHb8gelBHSeF6ryPnMxAAEC1RGHhhXfpSRWMtE6ybr+pSsYU52M3G9+TF84v91pSwNrb9ZQ==}
deprecated: Please update to version 4.1.1
'@tsparticles/shape-heart@3.9.1':
resolution: {integrity: sha512-h1aYiBVCUAJ14zyK792EuX0332Hus6OgYy/4dk6PhfgdFTQaHk+FzGJjw+jEB8vpxOYtWeysT9uoofPZeDrqBQ==}
deprecated: Please update to version 4.1.1
'@tsparticles/shape-image@3.9.1':
resolution: {integrity: sha512-fCA5eme8VF3oX8yNVUA0l2SLDKuiZObkijb0z3Ky0qj1HUEVlAuEMhhNDNB9E2iELTrWEix9z7BFMePp2CC7AA==}
deprecated: Please update to version 4.1.1
'@tsparticles/shape-polygon@3.9.1':
resolution: {integrity: sha512-dA77PgZdoLwxnliH6XQM/zF0r4jhT01pw5y7XTeTqws++hg4rTLV9255k6R6eUqKq0FPSW1/WBsBIl7q/MmrqQ==}
deprecated: Please update to version 4.1.1
'@tsparticles/shape-square@3.9.1':
resolution: {integrity: sha512-DKGkDnRyZrAm7T2ipqNezJahSWs6xd9O5LQLe5vjrYm1qGwrFxJiQaAdlb00UNrexz1/SA7bEoIg4XKaFa7qhQ==}
deprecated: Please update to version 4.1.1
'@tsparticles/shape-star@3.9.1':
resolution: {integrity: sha512-kdMJpi8cdeb6vGrZVSxTG0JIjCwIenggqk0EYeKAwtOGZFBgL7eHhF2F6uu1oq8cJAbXPujEoabnLsz6mW8XaA==}
deprecated: Please update to version 4.1.1
'@tsparticles/updater-color@3.9.1':
resolution: {integrity: sha512-XGWdscrgEMA8L5E7exsE0f8/2zHKIqnTrZymcyuFBw2DCB6BIV+5z6qaNStpxrhq3DbIxxhqqcybqeOo7+Alpg==}
deprecated: this package was deprecated in tsparticles v4, replaced by @tsparticles/updater-paint
'@tsparticles/updater-life@3.9.1':
resolution: {integrity: sha512-Oi8aF2RIwMMsjssUkCB6t3PRpENHjdZf6cX92WNfAuqXtQphr3OMAkYFJFWkvyPFK22AVy3p/cFt6KE5zXxwAA==}
deprecated: Please update to version 4.1.1
'@tsparticles/updater-opacity@3.9.1':
resolution: {integrity: sha512-w778LQuRZJ+IoWzeRdrGykPYSSaTeWfBvLZ2XwYEkh/Ss961InOxZKIpcS6i5Kp/Zfw0fS1ZAuqeHwuj///Osw==}
deprecated: Please update to version 4.1.1
'@tsparticles/updater-out-modes@3.9.1':
resolution: {integrity: sha512-cKQEkAwbru+hhKF+GTsfbOvuBbx2DSB25CxOdhtW2wRvDBoCnngNdLw91rs+0Cex4tgEeibkebrIKFDDE6kELg==}
deprecated: Please update to version 4.1.1
'@tsparticles/updater-roll@3.9.1':
resolution: {integrity: sha512-zl4JeM3gUBJ0uttmIsond3lrZ3f3AkItFeS0Lhj/7jiCKfUoRyyOMrcBk8R1AhW7lI+7ko1iBs3jhO0jnxz9vg==}
deprecated: Please update to version 4.1.1
'@tsparticles/updater-rotate@3.9.1':
resolution: {integrity: sha512-9BfKaGfp28JN82MF2qs6Ae/lJr9EColMfMTHqSKljblwbpVDHte4umuwKl3VjbRt87WD9MGtla66NTUYl+WxuQ==}
deprecated: Please update to version 4.1.1
'@tsparticles/updater-size@3.9.1':
resolution: {integrity: sha512-3NSVs0O2ApNKZXfd+y/zNhTXSFeG1Pw4peI8e6z/q5+XLbmue9oiEwoPy/tQLaark3oNj3JU7Q903ZijPyXSzw==}
deprecated: Please update to version 4.1.1
'@tsparticles/updater-tilt@3.9.1':
resolution: {integrity: sha512-PB2yaoyXRmSk4iIVgjtRrzOxXMK9mjeAQHIJGtT4faq46Z8cbIIEFgjTwqrUV8qOrNg/h4sm5NE/s0qsTYjp1Q==}
deprecated: Please update to version 4.1.1
'@tsparticles/updater-wobble@3.9.1':
resolution: {integrity: sha512-c99Ogy9q4QWO+zsDXol0UnpUwZiY2UucFb8ltuDv9AlbGUeprygoub8jhgT5pEDv+GdzWOJGSgq7rfgv9cHBrg==}
deprecated: Please update to version 4.1.1
'@types/aria-query@5.0.4':
resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==}
@@ -2937,7 +2894,6 @@ packages:
glob@10.4.5:
resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
hasBin: true
glob@11.1.0:
@@ -2948,7 +2904,7 @@ packages:
glob@7.2.3:
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
deprecated: Glob versions prior to v9 are no longer supported
global-agent@3.0.0:
resolution: {integrity: sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==}
@@ -3106,9 +3062,6 @@ packages:
resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
engines: {node: '>= 0.4'}
intl-messageformat@11.2.9:
resolution: {integrity: sha512-cGzymZerpDhVXRKjKLgXKda9gI29TU2o88L7gwNMHp3WZVxA/0c5tX52udXbW9JklDApolvMXZG6Dhhdz5eirA==}
is-array-buffer@3.0.5:
resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==}
engines: {node: '>= 0.4'}
@@ -3412,28 +3365,24 @@ packages:
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
libc: [glibc]
lightningcss-linux-arm64-musl@1.30.1:
resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
libc: [musl]
lightningcss-linux-x64-gnu@1.30.1:
resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
libc: [glibc]
lightningcss-linux-x64-musl@1.30.1:
resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
libc: [musl]
lightningcss-win32-arm64-msvc@1.30.1:
resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==}
@@ -3973,12 +3922,6 @@ packages:
peerDependencies:
react: ^19.2.0
react-intl@10.1.14:
resolution: {integrity: sha512-DuL7vAViDCjxyiPNccMkt1PBv22mh+1OBhA8ta4r0fbjnJYhLVFc+3pR71qADZRHytRnKm9JcVDP9jYJW+qO6g==}
peerDependencies:
'@types/react': '>=18.0.0'
react: '>=18.0.0'
react-is@16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
@@ -4793,7 +4736,6 @@ packages:
whatwg-encoding@3.1.1:
resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
engines: {node: '>=18'}
deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation
whatwg-mimetype@4.0.0:
resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
@@ -5025,25 +4967,33 @@ snapshots:
'@asamuzakjp/nwsapi@2.3.9': {}
'@babel/code-frame@7.27.1':
dependencies:
'@babel/helper-validator-identifier': 7.27.1
js-tokens: 4.0.0
picocolors: 1.1.1
'@babel/code-frame@7.29.0':
dependencies:
'@babel/helper-validator-identifier': 7.28.5
js-tokens: 4.0.0
picocolors: 1.1.1
'@babel/compat-data@7.28.4': {}
'@babel/compat-data@7.29.3': {}
'@babel/core@7.28.4':
dependencies:
'@babel/code-frame': 7.29.0
'@babel/generator': 7.29.1
'@babel/helper-compilation-targets': 7.28.6
'@babel/helper-module-transforms': 7.28.6(@babel/core@7.28.4)
'@babel/code-frame': 7.27.1
'@babel/generator': 7.28.3
'@babel/helper-compilation-targets': 7.27.2
'@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4)
'@babel/helpers': 7.28.4
'@babel/parser': 7.29.0
'@babel/template': 7.28.6
'@babel/traverse': 7.29.0
'@babel/types': 7.29.0
'@babel/parser': 7.28.4
'@babel/template': 7.27.2
'@babel/traverse': 7.28.4
'@babel/types': 7.28.4
'@jridgewell/remapping': 2.3.5
convert-source-map: 2.0.0
debug: 4.4.3
@@ -5053,6 +5003,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@babel/generator@7.28.3':
dependencies:
'@babel/parser': 7.28.4
'@babel/types': 7.28.4
'@jridgewell/gen-mapping': 0.3.13
'@jridgewell/trace-mapping': 0.3.31
jsesc: 3.1.0
'@babel/generator@7.29.1':
dependencies:
'@babel/parser': 7.29.0
@@ -5065,11 +5023,19 @@ snapshots:
dependencies:
'@babel/types': 7.29.0
'@babel/helper-compilation-targets@7.27.2':
dependencies:
'@babel/compat-data': 7.28.4
'@babel/helper-validator-option': 7.27.1
browserslist: 4.26.3
lru-cache: 5.1.1
semver: 6.3.1
'@babel/helper-compilation-targets@7.28.6':
dependencies:
'@babel/compat-data': 7.29.3
'@babel/helper-validator-option': 7.27.1
browserslist: 4.28.2
browserslist: 4.26.3
lru-cache: 5.1.1
semver: 6.3.1
@@ -5113,6 +5079,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@babel/helper-module-imports@7.27.1':
dependencies:
'@babel/traverse': 7.28.4
'@babel/types': 7.28.4
transitivePeerDependencies:
- supports-color
'@babel/helper-module-imports@7.28.6':
dependencies:
'@babel/traverse': 7.29.0
@@ -5120,6 +5093,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)':
dependencies:
'@babel/core': 7.28.4
'@babel/helper-module-imports': 7.27.1
'@babel/helper-validator-identifier': 7.27.1
'@babel/traverse': 7.28.4
transitivePeerDependencies:
- supports-color
'@babel/helper-module-transforms@7.28.6(@babel/core@7.28.4)':
dependencies:
'@babel/core': 7.28.4
@@ -5142,7 +5124,7 @@ snapshots:
'@babel/core': 7.28.4
'@babel/helper-annotate-as-pure': 7.27.3
'@babel/helper-wrap-function': 7.28.6
'@babel/traverse': 7.29.0
'@babel/traverse': 7.28.4
transitivePeerDependencies:
- supports-color
@@ -5157,7 +5139,7 @@ snapshots:
'@babel/helper-skip-transparent-expression-wrappers@7.27.1':
dependencies:
'@babel/traverse': 7.29.0
'@babel/traverse': 7.28.4
'@babel/types': 7.29.0
transitivePeerDependencies:
- supports-color
@@ -5180,8 +5162,8 @@ snapshots:
'@babel/helpers@7.28.4':
dependencies:
'@babel/template': 7.28.6
'@babel/types': 7.29.0
'@babel/template': 7.27.2
'@babel/types': 7.28.4
'@babel/parser@7.28.4':
dependencies:
@@ -5382,7 +5364,7 @@ snapshots:
'@babel/core': 7.28.4
'@babel/helper-compilation-targets': 7.28.6
'@babel/helper-plugin-utils': 7.28.6
'@babel/traverse': 7.29.0
'@babel/traverse': 7.28.4
transitivePeerDependencies:
- supports-color
@@ -5409,7 +5391,7 @@ snapshots:
'@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.28.4)':
dependencies:
'@babel/core': 7.28.4
'@babel/helper-module-transforms': 7.28.6(@babel/core@7.28.4)
'@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4)
'@babel/helper-plugin-utils': 7.28.6
transitivePeerDependencies:
- supports-color
@@ -5435,7 +5417,7 @@ snapshots:
'@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.28.4)':
dependencies:
'@babel/core': 7.28.4
'@babel/helper-module-transforms': 7.28.6(@babel/core@7.28.4)
'@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4)
'@babel/helper-plugin-utils': 7.28.6
transitivePeerDependencies:
- supports-color
@@ -5683,12 +5665,30 @@ snapshots:
'@babel/runtime@7.28.4': {}
'@babel/template@7.27.2':
dependencies:
'@babel/code-frame': 7.27.1
'@babel/parser': 7.28.4
'@babel/types': 7.28.4
'@babel/template@7.28.6':
dependencies:
'@babel/code-frame': 7.29.0
'@babel/parser': 7.29.0
'@babel/types': 7.29.0
'@babel/traverse@7.28.4':
dependencies:
'@babel/code-frame': 7.27.1
'@babel/generator': 7.28.3
'@babel/helper-globals': 7.28.0
'@babel/parser': 7.28.4
'@babel/template': 7.27.2
'@babel/types': 7.28.4
debug: 4.4.3
transitivePeerDependencies:
- supports-color
'@babel/traverse@7.29.0':
dependencies:
'@babel/code-frame': 7.29.0
@@ -5865,7 +5865,7 @@ snapshots:
fs-extra: 10.1.0
isbinaryfile: 4.0.10
minimist: 1.2.8
plist: 3.1.1
plist: 3.1.0
transitivePeerDependencies:
- supports-color
@@ -5888,7 +5888,7 @@ snapshots:
dir-compare: 4.2.0
fs-extra: 11.3.4
minimatch: 9.0.5
plist: 3.1.1
plist: 3.1.0
transitivePeerDependencies:
- supports-color
@@ -6045,20 +6045,6 @@ snapshots:
'@floating-ui/utils@0.2.10': {}
'@formatjs/fast-memoize@3.1.6': {}
'@formatjs/icu-messageformat-parser@3.5.12':
dependencies:
'@formatjs/icu-skeleton-parser': 2.1.10
'@formatjs/icu-skeleton-parser@2.1.10': {}
'@formatjs/intl@4.1.14':
dependencies:
'@formatjs/fast-memoize': 3.1.6
'@formatjs/icu-messageformat-parser': 3.5.12
intl-messageformat: 11.2.9
'@fortawesome/fontawesome-free@6.7.2': {}
'@humanfs/core@0.19.1': {}
@@ -6252,7 +6238,7 @@ snapshots:
'@rollup/plugin-babel@6.1.0(@babel/core@7.28.4)(@types/babel__core@7.20.5)(rollup@4.60.3)':
dependencies:
'@babel/core': 7.28.4
'@babel/helper-module-imports': 7.28.6
'@babel/helper-module-imports': 7.27.1
'@rollup/pluginutils': 5.3.0(rollup@4.60.3)
optionalDependencies:
'@types/babel__core': 7.20.5
@@ -6266,7 +6252,7 @@ snapshots:
'@types/resolve': 1.20.2
deepmerge: 4.3.1
is-module: 1.0.0
resolve: 1.22.12
resolve: 1.22.10
optionalDependencies:
rollup: 4.60.3
@@ -6620,24 +6606,24 @@ snapshots:
'@types/babel__core@7.20.5':
dependencies:
'@babel/parser': 7.29.0
'@babel/types': 7.29.0
'@babel/parser': 7.28.4
'@babel/types': 7.28.4
'@types/babel__generator': 7.27.0
'@types/babel__template': 7.4.4
'@types/babel__traverse': 7.28.0
'@types/babel__generator@7.27.0':
dependencies:
'@babel/types': 7.29.0
'@babel/types': 7.28.4
'@types/babel__template@7.4.4':
dependencies:
'@babel/parser': 7.29.0
'@babel/types': 7.29.0
'@babel/parser': 7.28.4
'@babel/types': 7.28.4
'@types/babel__traverse@7.28.0':
dependencies:
'@babel/types': 7.29.0
'@babel/types': 7.28.4
'@types/cacheable-request@6.0.3':
dependencies:
@@ -6821,7 +6807,8 @@ snapshots:
'@xmldom/xmldom@0.8.13': {}
'@xmldom/xmldom@0.9.10': {}
'@xmldom/xmldom@0.9.10':
optional: true
'@zeit/schemas@2.36.0': {}
@@ -8210,11 +8197,6 @@ snapshots:
hasown: 2.0.2
side-channel: 1.1.0
intl-messageformat@11.2.9:
dependencies:
'@formatjs/fast-memoize': 3.1.6
'@formatjs/icu-messageformat-parser': 3.5.12
is-array-buffer@3.0.5:
dependencies:
call-bind: 1.0.9
@@ -8885,6 +8867,7 @@ snapshots:
'@xmldom/xmldom': 0.9.10
base64-js: 1.5.1
xmlbuilder: 15.1.1
optional: true
possible-typed-array-names@1.1.0: {}
@@ -9001,14 +8984,6 @@ snapshots:
react: 19.2.0
scheduler: 0.27.0
react-intl@10.1.14(@types/react@19.2.2)(react@19.2.0):
dependencies:
'@formatjs/icu-messageformat-parser': 3.5.12
'@formatjs/intl': 4.1.14
'@types/react': 19.2.2
intl-messageformat: 11.2.9
react: 19.2.0
react-is@16.13.1: {}
react-is@17.0.2: {}

View File

@@ -6,13 +6,11 @@
import { lazy, Suspense, useEffect } from "react";
import { HashRouter as Router, Routes, Route, useLocation } from "react-router-dom";
import { IntlProvider } from 'react-intl';
import "./App.css";
import HomePage from "./pages/HomePage/HomePage";
import LabPython from "./pages/LabPython/LabPython";
import ScrollToTop from "./components/ScrollToTop";
import { trackPageView } from "./services/plausible";
import { getLocalizedMessages } from "./services/localizedMessages";
const Playground = lazy(() => import("./pages/Playground/Playground"));
const About = lazy(() => import("./pages/About/About"));
@@ -35,6 +33,7 @@ const TurtleGame = lazy(() => import("./atividades/programacao/turtle/TurtleGame
const CriptoGame = lazy(() => import("./atividades/programacao/cripto/CriptoGame"));
const ExemploGame = lazy(() => import("./atividades/programacao/exemplo/ExemploGame"));
const ExemploGame2 = lazy(() => import("./atividades/programacao/exemplo2/ExemploGame2"));
const PadroesGame = lazy(() => import("./atividades/programacao/padroes/PadroesGame"));
const LoadingFallback = () => (
<div
@@ -105,6 +104,7 @@ function AppRoutes() {
<Route path="/atividades/programacao/turtle" element={<TurtleGame />} />
<Route path="/atividades/programacao/exemplo" element={<ExemploGame />} />
<Route path="/atividades/programacao/exemplo2" element={<ExemploGame2 />} />
<Route path="/atividades/programacao/padroes" element={<PadroesGame />} />
</Routes>
{/* Modal overlay routes — rendered on top of the background page */}
@@ -118,15 +118,10 @@ function AppRoutes() {
}
export default function App() {
const locale = 'ht';
const translatedMessages = getLocalizedMessages(locale);
console.log(translatedMessages);
return (
<Router>
<Suspense fallback={<LoadingFallback />}>
<IntlProvider messages={translatedMessages} locale={locale} defaultLocale="pt">
<AppRoutes />
</IntlProvider>
</Suspense>
</Router>
);

View File

@@ -0,0 +1,61 @@
/**
* @fileoverview Componente React de entrada do jogo Padrões.
* @module games.padroes.PadroesGame
*/
import { useEffect, useMemo } from "react";
import GameBase from "../../../components/game/GameBase";
import GameEditor from "../../../components/game/GameEditor";
import BlocklyEditor from "../../../components/game/editors/BlocklyEditor";
import { createGame } from "./game";
import { gameConfig } from "./config/config";
import { generateDynamicToolbox, registerBlocks } from "./blocks/blocks";
import {
GameStateProvider,
useGameState,
} from "../../../contexts/GameStateContext";
/**
* Monta a cena e o editor do jogo Padrões.
* Registra os blocos, configura o toolbox dinâmico e injeta o `gameFactory`.
* @returns {JSX.Element} Conteúdo do jogo (editor + canvas)
*/
function PadroesContent() {
const { setFailureMessage } = useGameState();
// Registra os blocos customizados no Blockly ao montar
useEffect(() => {
registerBlocks();
}, []);
// Memoriza o gerador do toolbox para evitar recriações
const toolboxGenerator = useMemo(
() => (allowedBlocks) => generateDynamicToolbox(allowedBlocks),
[],
);
return (
<GameBase
gameFactory={createGame}
gameConfig={gameConfig}
customFailureHandler={setFailureMessage}
failureHandler={setFailureMessage}
>
<GameEditor>
<BlocklyEditor toolboxGenerator={toolboxGenerator} />
</GameEditor>
</GameBase>
);
}
/**
* Componente de página que provê o contexto de estado do jogo Padrões.
* @returns {JSX.Element} Página completa do jogo
*/
export default function PadroesGame() {
return (
<GameStateProvider gameConfig={gameConfig}>
<PadroesContent />
</GameStateProvider>
);
}

View File

@@ -0,0 +1,249 @@
/**
* @fileoverview Blocos, geradores e toolbox do jogo Padrões.
*
* Blocos customizados expõem: leitura da ENTRADA, definição/leitura de
* SAÍDA, CONTADOR e LETRA, a constante ALFABETO A-Z e as operações de
* string text_charAt / text_indexOf (0-based, alinhadas ao cripto).
*
* @module games.padroes.blocks.blocks
*/
"use strict";
import * as Blockly from "blockly/core";
import "blockly/blocks";
import { javascriptGenerator } from "blockly/javascript";
import { CORES_BLOCKLY } from "@/blockly/blocklyColors";
import {
configurarGerador,
gerarExpressao,
gerarStatementComValor,
} from "@/blockly/generator";
import { gerarToolboxDeEstrutura } from "@/blockly/toolbox";
import {
criarBlocoExpressaoSimples,
criarBlocoStatementComValor,
} from "@/blockly/blockFactory";
const C = CORES_BLOCKLY; // LOGICA:210, LOOPS:120, MATEMATICA:230, TEXTO:160, VARIAVEIS:330
// Estrutura declarativa da toolbox (cada categoria filtra por allowedBlocks).
const ESTRUTURA_TOOLBOX = [
{
nome: "Entrada/Saída",
blocos: ["obter_entrada", "definir_saida", "obter_saida"],
},
{
nome: "Variáveis",
blocos: [
"definir_contador",
"obter_contador",
"definir_letra",
"obter_letra",
],
},
{ nome: "Repetição", blocos: ["controls_whileUntil"] },
{
nome: "Lógica",
blocos: ["controls_if", "logic_compare", "logic_operation"],
},
{
nome: "Texto",
blocos: ["text", "text_charAt", "text_indexOf", "text_length", "alfabeto"],
},
{ nome: "Matemática", blocos: ["math_number", "math_arithmetic"] },
];
/**
* Registra todos os blocos e geradores do jogo Padrões no Blockly.
* Deve ser chamado uma vez na montagem do componente.
* @returns {void}
*/
export const registerBlocks = () => {
defineBlocks();
defineGenerators();
};
/**
* Gera a toolbox contendo apenas os blocos permitidos pela fase.
* @param {Array<string>} [allowedBlocks=[]] - Lista de blocos habilitados
* @returns {Object} Estrutura `categoryToolbox` para o Blockly
*/
export const generateDynamicToolbox = (allowedBlocks = []) => {
return gerarToolboxDeEstrutura(ESTRUTURA_TOOLBOX, allowedBlocks);
};
// ===================== Definições de blocos =====================
const defineBlocks = () => {
// Entrada (somente leitura — é pré-definida pela fase)
criarBlocoExpressaoSimples(
"obter_entrada",
"ENTRADA",
null,
C.VARIAVEIS,
"O texto que a fase quer analisar",
);
// Saída
criarBlocoStatementComValor(
"definir_saida",
"definir SAÍDA como",
"VALUE",
null,
C.VARIAVEIS,
);
criarBlocoExpressaoSimples(
"obter_saida",
"SAÍDA",
null,
C.VARIAVEIS,
"O veredito atual",
);
// Contador (índice do loop)
criarBlocoStatementComValor(
"definir_contador",
"definir CONTADOR como",
"VALUE",
null,
C.LOOPS,
);
criarBlocoExpressaoSimples(
"obter_contador",
"CONTADOR",
null,
C.LOOPS,
"Valor atual do contador",
);
// Letra (caractere atual)
criarBlocoStatementComValor(
"definir_letra",
"definir LETRA como",
"VALUE",
null,
C.VARIAVEIS,
);
criarBlocoExpressaoSimples(
"obter_letra",
"LETRA",
null,
C.VARIAVEIS,
"Valor atual da letra",
);
// Constante: alfabeto A-Z
criarBlocoExpressaoSimples(
"alfabeto",
"ALFABETO A-Z",
"String",
C.TEXTO,
"Retorna ABCDEFGHIJKLMNOPQRSTUVWXYZ",
);
// text_charAt — 0-based (sobrescreve o nativo para alinhar com CONTADOR=0)
Blockly.Blocks["text_charAt"] = {
init: function () {
this.setColour(C.TEXTO);
this.setOutput(true, "String");
this.appendValueInput("VALUE")
.setCheck("String")
.appendField("no texto");
this.appendValueInput("AT")
.setCheck("Number")
.appendField("obter letra nº");
this.setInputsInline(true);
this.setTooltip("Letra na posição informada (0 = primeira).");
},
};
// text_indexOf — 0-based (retorna -1 quando não encontra)
Blockly.Blocks["text_indexOf"] = {
init: function () {
this.setColour(C.TEXTO);
this.setOutput(true, "Number");
this.appendValueInput("VALUE")
.setCheck("String")
.appendField("no texto");
this.appendValueInput("FIND")
.setCheck("String")
.appendField("buscar a posição de");
this.setInputsInline(true);
this.setTooltip(
"Primeira posição (0-based) do trecho, ou -1 se não existir.",
);
},
};
};
// ===================== Geradores de código =====================
const defineGenerators = () => {
// Prefix de highlight (faz o bloco piscar na execução passo a passo)
configurarGerador();
// Statements com valor
gerarStatementComValor("definir_saida", "VALUE", (v) => `definirSaida(${v})`);
gerarStatementComValor("definir_contador", "VALUE", (v) =>
`definirContador(${v})`,
);
gerarStatementComValor("definir_letra", "VALUE", (v) => `var letra = ${v}`);
// Expressões
gerarExpressao(
"obter_entrada",
"obterEntrada()",
javascriptGenerator.ORDER_FUNCTION_CALL,
);
gerarExpressao(
"obter_saida",
"obterSaida()",
javascriptGenerator.ORDER_FUNCTION_CALL,
);
gerarExpressao(
"obter_contador",
"obterContador()",
javascriptGenerator.ORDER_FUNCTION_CALL,
);
gerarExpressao("obter_letra", "letra", javascriptGenerator.ORDER_ATOMIC);
gerarExpressao(
"alfabeto",
'"ABCDEFGHIJKLMNOPQRSTUVWXYZ"',
javascriptGenerator.ORDER_ATOMIC,
);
// text_charAt — 0-based
javascriptGenerator.forBlock["text_charAt"] = function (block) {
const text =
javascriptGenerator.valueToCode(
block,
"VALUE",
javascriptGenerator.ORDER_MEMBER,
) || "''";
const at =
javascriptGenerator.valueToCode(
block,
"AT",
javascriptGenerator.ORDER_NONE,
) || "0";
return [`${text}.charAt(${at})`, javascriptGenerator.ORDER_MEMBER];
};
// text_indexOf — 0-based
javascriptGenerator.forBlock["text_indexOf"] = function (block) {
const text =
javascriptGenerator.valueToCode(
block,
"VALUE",
javascriptGenerator.ORDER_MEMBER,
) || "''";
const search =
javascriptGenerator.valueToCode(
block,
"FIND",
javascriptGenerator.ORDER_NONE,
) || "''";
return [`${text}.indexOf(${search})`, javascriptGenerator.ORDER_MEMBER];
};
};

View File

@@ -0,0 +1,92 @@
/**
* @fileoverview Configuração do jogo Padrões (reconhecimento de padrões).
*
* Conceito: o aluno escreve programas que analisam textos e identificam
* padrões (apenas letras A-Z, sequências, estruturas, etc.).
*
* A ENTRADA de cada fase é pré-definida em `entradaTeste`. O aluno lê a
* entrada com `obterEntrada()` e deve produzir um veredito em `SAÍDA`.
*
* @module games.padroes.config.config
*/
export const gameConfig = {
gameId: "padroes",
gameName: "Padrões",
type: "blocks",
icon: "🔍",
thumbnail: "",
descricao:
"Aprenda reconhecimento de padrões criando programas que analisam textos e identificam padrões como letras válidas, sequências e estruturas.",
dificuldade: "Intermediário",
categoria: "Lógica",
tempoEstimado: "20-30 min",
conceitos: [
"Reconhecimento de padrões",
"Repetição",
"Condicionais",
"Strings",
],
route: "/atividades/programacao/padroes",
component: "PadroesGame",
objectives: [
"Identificar padrões em textos usando loops e condicionais",
"Combinar operações de string (charAt, indexOf, length) para validar entradas",
],
metadata: {
lastUpdated: "2026-06-27",
version: "0.1.0",
},
fases: [
{
id: 1,
nome: "Fase 1: Apenas letras de A a Z",
descricao:
'Crie um loop "enquanto" que percorra cada caractere da ENTRADA e verifique se TODOS pertencem ao alfabeto (A-Z). ' +
'Se algum caractere NÃO for uma letra, defina a SAÍDA como "INVÁLIDO". ' +
'Se todos forem letras, defina como "VÁLIDO". ' +
'Dica: use o bloco ALFABETO A-Z e "buscar a posição de" (indexOf) para testar cada letra — indexOf retorna -1 quando o caractere não existe no alfabeto.',
timeout: 30,
maxBlocks: 25,
// Exige um loop (while/for) com incremento do contador para evitar loop infinito
validationRegex: /(while|for)[\s\S]*definirContador[\s\S]*\+/,
// Caso de teste desta fase (fase de teste/dev — caso único)
entradaTeste: "ABC123",
expectedSaida: "INVÁLIDO",
allowedBlocks: [
"obter_entrada",
"definir_saida",
"obter_saida",
"definir_contador",
"obter_contador",
"definir_letra",
"obter_letra",
"alfabeto",
"text_charAt",
"text_indexOf",
"text_length",
"text",
"controls_whileUntil",
"controls_if",
"logic_compare",
"logic_operation",
"math_number",
"math_arithmetic",
],
},
],
mensagens: {
semMovimento:
"Seu programa não executou nenhuma ação. Use os blocos para analisar a ENTRADA.",
semSaida:
'Você não definiu a SAÍDA. Use "definir SAÍDA como" com o veredito (VÁLIDO ou INVÁLIDO).',
erradoInvalido:
'Algo fugiu ao padrão. Algum caractere da entrada NÃO é uma letra de A-Z — a resposta correta seria "INVÁLIDO".',
erradoValido:
'Todos os caracteres são letras de A-Z — a resposta correta seria "VÁLIDO".',
erroEstrutura:
"Você precisa usar um loop 'enquanto' para percorrer a ENTRADA caractere a caractere.",
},
};

View File

@@ -0,0 +1,182 @@
/**
* @fileoverview Cena Phaser do jogo Padrões.
*
* A cena mantém o estado de execução (entrada, saída, contador) e expõe
* métodos que o interpretador chama via `setupPadroesAPI`. A ENTRADA é
* pré-carregada a partir de `configFase.entradaTeste`.
*
* @module games.padroes.game
*/
import Phaser from "phaser";
import { BaseGameScene } from "../../../shared/BaseGameScene.js";
import { setupPadroesAPI } from "./hooks/interpreterSetup.js";
import { validationSolution } from "./validation/validators.js";
import { gameConfig } from "./config/config.js";
import { ConstantesJogo } from "./ui/constants.js";
import { inicializarLayout } from "./ui/layout.js";
class PadroesScene extends BaseGameScene {
constructor() {
super("PadroesScene");
this.entrada = "";
this.saida = "";
this.contador = 0;
this.textoEntrada = null;
this.textoSaida = null;
}
/**
* Inicializa o estado da cena a partir da config da fase.
* @param {Object} data - Dados passados à cena
* @returns {void}
*/
init(data) {
super.init(data);
this.limparVariaveis();
}
/**
* Preload de assets globais (sons compartilhados).
* @returns {void}
*/
preload() {
this.preloadGlobalAssets();
}
/**
* Cria o layout visual e conecta o controlador padrão de execução.
* @returns {void}
*/
create() {
this.validatorFunc = (historico) =>
validationSolution(historico, this.configFase, gameConfig, this);
this.setupStandardController(
() => setupPadroesAPI(this, { animationSpeed: 150 }),
this.validatorFunc,
);
const layout = inicializarLayout(this);
this.textoEntrada = layout.textoEntrada;
this.textoSaida = layout.textoSaida;
this._atualizarDisplayEntrada();
}
/**
* Preparações antes de executar o código do aluno.
* @returns {void}
*/
onBeforeRun() {
this.historico = [];
this.limparVariaveis();
}
/**
* Restaura o estado visual ao resetar.
* @returns {void}
*/
onReset() {
this.limparVariaveis();
}
/**
* Reseta as variáveis de estado e os displays visuais.
* A entrada volta a ser o caso de teste da fase.
* @returns {void}
*/
limparVariaveis() {
this.entrada = this.configFase?.entradaTeste ?? "";
this.saida = "";
this.contador = 0;
this._atualizarDisplayEntrada();
if (this.textoSaida) this.textoSaida.setText("");
}
/**
* Atualiza o texto do painel de ENTRADA.
* @returns {void}
*/
_atualizarDisplayEntrada() {
if (this.textoEntrada) this.textoEntrada.setText(this.entrada || "");
}
// ===================== API exposta ao interpretador =====================
/**
* Define o veredito (SAÍDA) e atualiza o display.
* @param {string} valor - Veredito do aluno (ex.: "VÁLIDO" / "INVÁLIDO")
* @returns {Promise<void>}
*/
definirSaida(valor) {
this.saida = String(valor ?? "");
this.historico.push({ tipo: "definir_saida", valor: this.saida });
if (this.textoSaida) this.textoSaida.setText(this.saida);
return Promise.resolve();
}
/**
* Retorna o veredito atual.
* @returns {string}
*/
obterSaida() {
this.historico.push({ tipo: "obter_saida", valor: this.saida });
return this.saida;
}
/**
* Define o valor do contador.
* @param {number} valor
* @returns {Promise<void>}
*/
definirContador(valor) {
this.contador = Number(valor) || 0;
this.historico.push({ tipo: "definir_contador", valor: this.contador });
return Promise.resolve();
}
/**
* Retorna o valor atual do contador.
* @returns {number}
*/
obterContador() {
this.historico.push({ tipo: "obter_contador", valor: this.contador });
return this.contador;
}
/**
* Retorna a entrada de teste da fase.
* @returns {string}
*/
obterEntrada() {
this.historico.push({ tipo: "obter_entrada", valor: this.entrada });
return this.entrada;
}
}
/**
* Factory que cria a configuração Phaser do jogo Padrões.
* Injeta configFase e gameConfig no registry antes do boot da cena.
* @param {HTMLElement} elementoPai - Container DOM
* @param {Object} configFaseAtual - Configuração da fase selecionada
* @returns {Object} Configuração do Phaser
*/
export const createGame = (elementoPai, configFaseAtual) => {
const scene = new PadroesScene();
return {
type: Phaser.AUTO,
width: ConstantesJogo.LARGURA_TELA,
height: ConstantesJogo.ALTURA_TELA,
backgroundColor: ConstantesJogo.COR_FUNDO,
parent: elementoPai,
scale: { mode: Phaser.Scale.FIT, autoCenter: Phaser.Scale.CENTER_BOTH },
scene,
callbacks: {
preBoot(game) {
game.registry.set("configFase", configFaseAtual);
game.registry.set("gameConfig", gameConfig);
},
},
};
};

View File

@@ -0,0 +1,68 @@
/**
* @fileoverview Ponte entre o js-interpreter (sandbox) e a cena Phaser do
* jogo Padrões. Expõe as funções que o código gerado pelos blocos chama.
*
* @module games.padroes.hooks.interpreterSetup
*/
import { ApiHelpers } from "../../../../interpreters/ApiHelpers.js";
/**
* Configura a API disponível ao interpretador para o jogo Padrões.
* @param {Object} scene - Instância da cena Phaser (PadroesScene)
* @param {Object} [config] - Opções (ex.: `animationSpeed`)
* @returns {Function} Função de init com assinatura (interpreter, globalScope)
*/
export const setupPadroesAPI = (scene, config = {}) => {
const animationDelay = config.animationSpeed ?? 150;
return (interpreter, globalScope) => {
// Ações (assíncronas — pausam o interpretador para feedback visual)
ApiHelpers.registerFunction(
interpreter,
globalScope,
"definirSaida",
ApiHelpers.createActionWrapper(scene, "definirSaida", animationDelay),
true,
);
ApiHelpers.registerFunction(
interpreter,
globalScope,
"definirContador",
ApiHelpers.createActionWrapper(scene, "definirContador", animationDelay),
true,
);
// Condições (síncronas — retornam valor imediatamente)
ApiHelpers.registerFunction(
interpreter,
globalScope,
"obterEntrada",
ApiHelpers.createConditionWrapper(scene, "obterEntrada"),
false,
);
ApiHelpers.registerFunction(
interpreter,
globalScope,
"obterSaida",
ApiHelpers.createConditionWrapper(scene, "obterSaida"),
false,
);
ApiHelpers.registerFunction(
interpreter,
globalScope,
"obterContador",
ApiHelpers.createConditionWrapper(scene, "obterContador"),
false,
);
// Highlight visual dos blocos durante a execução
ApiHelpers.registerFunction(
interpreter,
globalScope,
"highlightBlock",
ApiHelpers.createHighlightWrapper(scene),
false,
);
};
};

View File

@@ -0,0 +1,26 @@
/**
* @fileoverview Constantes visuais do jogo Padrões (reconhecimento de padrões).
* @module games.padroes.ui.constants
*/
export const ConstantesJogo = {
LARGURA_TELA: 800,
ALTURA_TELA: 600,
COR_FUNDO: "#1b1e2e",
};
export const ConstantesLayout = {
MARGEM: 40,
COR_BORDA_ENTRADA: 0x4a6cf7,
COR_BORDA_SAIDA: 0xf5a623,
ESPESSURA_BORDA: 4,
RAIO: 16,
PADDING: 24,
};
export const ConstantesTexto = {
TITULO: { TAMANHO: "28px", COR: "#ffffff" },
LABEL: { TAMANHO: "16px", COR: "#9aa4c7" },
ENTRADA: { TAMANHO: "40px", COR: "#4ade80" },
SAIDA: { TAMANHO: "46px", COR: "#f5a623" },
};

View File

@@ -0,0 +1,83 @@
/**
* @fileoverview Layout do jogo Padrões: dois painéis (ENTRADA e SAÍDA).
* @module games.padroes.ui.layout
*/
import {
ConstantesJogo,
ConstantesLayout,
ConstantesTexto,
} from "./constants.js";
/**
* Monta o layout visual da cena: título, painel de ENTRADA e painel de SAÍDA.
* @param {Phaser.Scene} scene - Instância da cena Phaser ativa
* @returns {{ textoEntrada: Phaser.GameObjects.Text, textoSaida: Phaser.GameObjects.Text }}
*/
export function inicializarLayout(scene) {
const W = ConstantesJogo.LARGURA_TELA;
const H = ConstantesJogo.ALTURA_TELA;
const M = ConstantesLayout.MARGEM;
const raio = ConstantesLayout.RAIO;
const pad = ConstantesLayout.PADDING;
scene.add
.text(W / 2, M, "RECONHECIMENTO DE PADRÕES", {
fontSize: ConstantesTexto.TITULO.TAMANHO,
fontStyle: "bold",
fill: ConstantesTexto.TITULO.COR,
})
.setOrigin(0.5, 0);
const tituloH = 64;
const gap = 24;
const panelW = W - M * 2;
const panelTopY = M + tituloH;
const panelTopH = (H - panelTopY - M - gap) / 2;
const panelBotY = panelTopY + panelTopH + gap;
const panelBotH = panelTopH;
const graphics = scene.add.graphics();
// Painel ENTRADA
graphics.lineStyle(
ConstantesLayout.ESPESSURA_BORDA,
ConstantesLayout.COR_BORDA_ENTRADA,
1,
);
graphics.strokeRoundedRect(M, panelTopY, panelW, panelTopH, raio);
scene.add.text(M + pad, panelTopY + 10, "ENTRADA (texto a analisar)", {
fontSize: ConstantesTexto.LABEL.TAMANHO,
fill: ConstantesTexto.LABEL.COR,
fontStyle: "bold",
});
const textoEntrada = scene.add.text(M + pad, panelTopY + 42, "", {
fontSize: ConstantesTexto.ENTRADA.TAMANHO,
fill: ConstantesTexto.ENTRADA.COR,
fontStyle: "bold",
align: "left",
wordWrap: { width: panelW - pad * 2, useAdvancedWrap: true },
});
// Painel SAÍDA
graphics.lineStyle(
ConstantesLayout.ESPESSURA_BORDA,
ConstantesLayout.COR_BORDA_SAIDA,
1,
);
graphics.strokeRoundedRect(M, panelBotY, panelW, panelBotH, raio);
scene.add.text(M + pad, panelBotY + 10, "SAÍDA (seu veredito)", {
fontSize: ConstantesTexto.LABEL.TAMANHO,
fill: ConstantesTexto.LABEL.COR,
fontStyle: "bold",
});
const textoSaida = scene.add.text(M + pad, panelBotY + 48, "", {
fontSize: ConstantesTexto.SAIDA.TAMANHO,
fill: ConstantesTexto.SAIDA.COR,
fontStyle: "bold",
align: "left",
wordWrap: { width: panelW - pad * 2, useAdvancedWrap: true },
});
return { textoEntrada, textoSaida };
}

View File

@@ -0,0 +1,95 @@
/**
* @fileoverview Validadores do jogo Padrões.
*
* Cada fase pode declarar `expectedSaida` (veredito esperado para a
* `entradaTeste` da fase). O validador normaliza o veredito do aluno
* (maiúsculas, sem acento, sem espaços) antes de comparar.
*
* @module games.padroes.validation.validators
*/
import { BaseGameValidator } from "../../../../shared/BaseGameValidator";
/**
* Normaliza um veredito para comparação: trim + uppercase + sem acentos.
* Ex.: "válido" -> "VALIDO", "Inválido" -> "INVALIDO".
* @param {string} texto
* @returns {string}
*/
function normalizar(texto) {
return String(texto ?? "")
.trim()
.toUpperCase()
.normalize("NFD")
.replace(/[\u0300-\u036f]/g, "");
}
export class PadroesValidator extends BaseGameValidator {
/**
* Delega para `validateFase{n}` quando disponível.
* @param {Array} history
* @param {Object} config
* @param {Object} gameConfig
* @param {Object} sceneRef
* @returns {{success:boolean, reason?:string}}
*/
validatePhase(history, config, gameConfig, sceneRef) {
const phaseMethod = this[`validateFase${config.id}`];
if (phaseMethod) {
return phaseMethod.call(this, history, config, gameConfig, sceneRef);
}
return this.success();
}
/**
* Fase 1 — Apenas letras de A a Z.
* Compara o veredito produzido pelo aluno (sceneRef.saida) com o esperado
* (config.expectedSaida) para a entrada de teste da fase.
* @returns {{success:boolean, reason?:string}}
*/
validateFase1(history, config, gameConfig, sceneRef) {
const esperado = normalizar(config.expectedSaida);
const recebido = normalizar(sceneRef.saida);
if (!recebido) {
return this.failure(
gameConfig?.mensagens?.semSaida ??
'Você não definiu a SAÍDA. Use "definir SAÍDA como" com o veredito.',
);
}
if (recebido !== esperado) {
const dica =
esperado === "INVALIDO"
? gameConfig?.mensagens?.erradoInvalido
: gameConfig?.mensagens?.erradoValido;
return this.failure(dicaComEntrada(dica, sceneRef.entrada, recebido));
}
return this.success();
}
}
/**
* Anexa a entrada testada e o veredito recebido à mensagem de falha.
* @param {string} dica
* @param {string} entrada
* @param {string} recebido
* @returns {string}
*/
function dicaComEntrada(dica, entrada, recebido) {
return `${dica}\n\nEntrada: "${entrada}"\nSeu veredito: "${recebido}"`;
}
/**
* Função exportada para validação de soluções do jogo Padrões.
* @param {Array} history - Histórico de ações do usuário
* @param {Object} config - Configuração da fase
* @param {Object} gameConfig - Configuração global do jogo
* @param {Object} sceneRef - Referência à cena (opcional)
* @returns {{success:boolean, reason?:string}}
*/
export function validationSolution(history, config, gameConfig, sceneRef) {
const validator = new PadroesValidator();
return validator.validate(history, config, gameConfig, sceneRef);
}

View File

@@ -12,14 +12,17 @@ const EXPECTED_IDS = [
"aspirador",
"automato",
"cripto",
"exemplo",
"molemash",
"ordenacao",
"padroes",
"puzzle",
"semaforo",
"turtle",
];
describe("GAMES_REGISTRY — estrutura", () => {
it("contem todos os 7 jogos esperados", () => {
it("contem todos os 10 jogos esperados", () => {
expect(Object.keys(GAMES_REGISTRY)).toEqual(expect.arrayContaining(EXPECTED_IDS));
expect(Object.keys(GAMES_REGISTRY)).toHaveLength(EXPECTED_IDS.length);
});
@@ -66,7 +69,7 @@ describe("getGameConfig()", () => {
});
describe("getAllGames()", () => {
it("retorna array com todos os 7 jogos", () => {
it("retorna array com todos os 10 jogos", () => {
const games = getAllGames();
expect(games).toHaveLength(EXPECTED_IDS.length);
});

View File

@@ -12,8 +12,10 @@
import { gameConfig as ASPIRADOR_GAME_CONFIG } from "../atividades/programacao/aspirador/config/config.js";
import { gameConfig as AUTOMATO_GAME_CONFIG } from "../atividades/programacao/automato/config/config.js";
import { gameConfig as CRIPTO_GAME_CONFIG } from "../atividades/programacao/cripto/config/config.js";
import { gameConfig as EXEMPLO_GAME_CONFIG } from "../atividades/programacao/exemplo/config/config.js";
import { gameConfig as MOLE_MASH_GAME_CONFIG } from "../atividades/programacao/mole-mash/config/config.js";
import { gameConfig as ORDERNACAO_GAME_CONFIG } from "../atividades/programacao/ordenacao/config/config.js";
import { gameConfig as PADROES_GAME_CONFIG } from "../atividades/programacao/padroes/config/config.js";
import { gameConfig as PUZZLE_GAME_CONFIG } from "../atividades/programacao/puzzle/config/config.js";
import { gameConfig as SEMAFORO_GAME_CONFIG } from "../atividades/programacao/semaforo/config/config.js";
import { gameConfig as TURTLE_GAME_CONFIG } from "../atividades/programacao/turtle/config/config.js";
@@ -40,8 +42,10 @@ export const GAMES_REGISTRY = {
aspirador: ASPIRADOR_GAME_CONFIG,
automato: AUTOMATO_GAME_CONFIG,
cripto: CRIPTO_GAME_CONFIG,
exemplo: EXEMPLO_GAME_CONFIG,
molemash: MOLE_MASH_GAME_CONFIG,
ordenacao: ORDERNACAO_GAME_CONFIG,
padroes: PADROES_GAME_CONFIG,
puzzle: PUZZLE_GAME_CONFIG,
semaforo: SEMAFORO_GAME_CONFIG,
turtle: TURTLE_GAME_CONFIG,

View File

@@ -1,8 +0,0 @@
import ht from "./../../lang/ht.json";
import pt from "./../../lang/pt.json";
export function getLocalizedMessages(locale) {
return {
ht,
pt
};
}