Commit inicial - add do repo privado para o repo NT style: changes header's logo and colors style: changes home page first session layout feat: creates about us home page section chore: creates home page section for whom chore: creates student materails home page section chore: creates teachers materials home page section chore: creates teacher materials home page section style: changes primary color style: changes color at activities page style: changes about page color style: changes name to Decoda fix: changes route to about page at footer fix: changes background color style: changes game page header colors style: changes footer colors chore: adds home page sections title style: changes main font family to Lato style: adds title font fix: changes sizes to be more responsive for mobile ajuste no build vercel atualiza regras envio homol Adiciona instrucoes de uso add JupyterLite fix solucao turtle Add Mole Mash e Modal de Falhas Add Progress Bar na pagina de Atividades fix game name chore: atualiza lockfile removendo vercel analytics inclusão de efeito ao mudar de fase add mecanismo de solução de fases em debug vite config test add BaseGame e refator do MoleMash refatoração turtle refatoração automato refatoração automato add tag bug 1 e 2 automato mostrar apenas games em homologação na pagina de atividades aumentar timeout das fases finais do Turtle fix bug scroll add video refactor semaforo arrumar ordem das cores add build docs update vercel update vercel update vercel update vercel update vercel add vercel jupyter add vercel jupyter fix deploy Vercel fix deploy Vercel fix deploy Vercel add cripto add cripto refatoração fix tour Mole Mash . remover arquivos de controle chore: adds development tag for activity card remover arquivos de status indevidamente versionados atualizar cores nas atividades add Quebra Cabeças add Quebra Cabeças add iniciativas add Iniciativas alteração de fotos pesadas fix menu mobile fix menu mobile fix menu mobile add Aspirador update icons update identidade visual documentação update jupyter add kernel python local add kernel python local add kernel python local feat: add health check feat: add primeiros passos add letramento mover letramento de lugar update path games update path games fix: ajuste clique rapido no botão executar remover dead code fix: refactor: extract shared utilities for storage, phase unlock and mobile detection stabilize context references and fix stale closure extrair GameProgressContext do GameStateContext (SRP) refactor(game): extrair usePhaser e useGameModals de GameBase + corrigir bugs descobertos refactor(game): remove todos os aliases PT/EN duplicados Remover aliases PT/EN da camada de modais refactor + tests security: add CodeSanitizer and integrate into GameInterpreter - CodeSanitizer.js: 4 built-in rules (max_length, infinite_while, infinite_for, excessive_nesting) with pluggable extra rules - GameInterpreter.executeCode: calls sanitizeCode() before js-interpreter, differentiates CodeSanitizationError (warn) from other errors (error) - 21 unit tests for CodeSanitizer (100% coverage) - 4 integration tests in GameInterpreter for sanitization paths add CodeSanitizer fix: fase 10 aspirador fix: bug semaforo teste feat: add version Ajusta a landing page para ficar mais próxima ao protótipo ajusta raio da borda do botão de Acesse nosso Laboratório pequenos ajustes de layout na página de iniciativas atualiza tabela de jogos educativos com os jogos disponíveis atualmente ajustados pequenos detalhes e informações do jogos na seção de guias pedagógicos troca nome playground para laboratório e adiciona imagens do lab adiciona documentação de conceitos básicos de programação ajustado pequenos erros de digitação adiciona tooltip com conceitos escondidos em hover na tag +N de conceitos update docs dev desativar tour setup matriz MoleMash setup matriz MoleMash fix: link update version update docs update docs mudou o layout de quem somos mudei as imgs dos icons e baixei o botao centraliza titulo com imagem e ajusta sessão com gradiente vermelho-rosa adiciona responsividade para a pagina quem somos ajusta botão de conheça nossa história ajustes ajustes na home + add. teclado update version security security feat: add tapume para telas pequenas v1.1.0 feat: decoda offline feat: doc offline offline fix: ajustes para release fix: navbar; config ordenação; versão fix: rotas docs e jupyter para pwa delete private files Co-authored-by: Indra Araujo <indra.araujo.santos@gmail.com> Co-authored-by: solange dos santos <sollangelive71@gmail.com>
2 lines
548 KiB
JSON
2 lines
548 KiB
JSON
[{"filePath":"/home/rui/src/new/plataforma-edu/app/coverage/block-navigation.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/coverage/prettify.js","messages":[],"suppressedMessages":[{"ruleId":"no-redeclare","severity":2,"message":"'ar' is already defined.","line":2,"column":3893,"nodeType":"Identifier","messageId":"redeclared","endLine":2,"endColumn":3895,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-redeclare","severity":2,"message":"'ar' is already defined.","line":2,"column":4061,"nodeType":"Identifier","messageId":"redeclared","endLine":2,"endColumn":4063,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-redeclare","severity":2,"message":"'at' is already defined.","line":2,"column":4089,"nodeType":"Identifier","messageId":"redeclared","endLine":2,"endColumn":4091,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-redeclare","severity":2,"message":"'ak' is already defined.","line":2,"column":4604,"nodeType":"Identifier","messageId":"redeclared","endLine":2,"endColumn":4606,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-redeclare","severity":2,"message":"'ak' is already defined.","line":2,"column":4665,"nodeType":"Identifier","messageId":"redeclared","endLine":2,"endColumn":4667,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-redeclare","severity":2,"message":"'am' is already defined.","line":2,"column":4670,"nodeType":"Identifier","messageId":"redeclared","endLine":2,"endColumn":4672,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-redeclare","severity":2,"message":"'ag' is already defined.","line":2,"column":4691,"nodeType":"Identifier","messageId":"redeclared","endLine":2,"endColumn":4693,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-redeclare","severity":2,"message":"'af' is already defined.","line":2,"column":4789,"nodeType":"Identifier","messageId":"redeclared","endLine":2,"endColumn":4791,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-redeclare","severity":2,"message":"'ak' is already defined.","line":2,"column":4854,"nodeType":"Identifier","messageId":"redeclared","endLine":2,"endColumn":4856,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-redeclare","severity":2,"message":"'am' is already defined.","line":2,"column":4859,"nodeType":"Identifier","messageId":"redeclared","endLine":2,"endColumn":4861,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-redeclare","severity":2,"message":"'ak' is already defined.","line":2,"column":4949,"nodeType":"Identifier","messageId":"redeclared","endLine":2,"endColumn":4951,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-redeclare","severity":2,"message":"'ag' is already defined.","line":2,"column":4970,"nodeType":"Identifier","messageId":"redeclared","endLine":2,"endColumn":4972,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-redeclare","severity":2,"message":"'V' is already defined.","line":2,"column":5216,"nodeType":"Identifier","messageId":"redeclared","endLine":2,"endColumn":5217,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-redeclare","severity":2,"message":"'U' is already defined.","line":2,"column":5220,"nodeType":"Identifier","messageId":"redeclared","endLine":2,"endColumn":5221,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-redeclare","severity":2,"message":"'ae' is already defined.","line":2,"column":5244,"nodeType":"Identifier","messageId":"redeclared","endLine":2,"endColumn":5246,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-prototype-builtins","severity":2,"message":"Do not access Object.prototype method 'hasOwnProperty' from target object.","line":2,"column":6621,"nodeType":"CallExpression","messageId":"prototypeBuildIn","endLine":2,"endColumn":6635,"suggestions":[{"messageId":"callObjectPrototype","data":{"prop":"hasOwnProperty"},"fix":{"range":[6638,6656],"text":"Object.prototype.hasOwnProperty.call(ag, "},"desc":"Call Object.prototype.hasOwnProperty explicitly."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":7590,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7591,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7610,7611],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7610,7610],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":7592,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7593,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7612,7613],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7612,7612],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":7594,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7595,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7614,7615],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7614,7614],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":7601,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7602,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7621,7622],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7621,7621],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":7616,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7617,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7636,7637],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7636,7636],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":7628,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7629,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7648,7649],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7648,7648],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":7637,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7638,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7657,7658],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7657,7657],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":7639,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7640,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7659,7660],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7659,7659],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":7641,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7642,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7661,7662],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7661,7661],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":7647,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7648,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7667,7668],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7667,7667],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":7649,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7650,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7669,7670],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7669,7669],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":7651,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7652,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7671,7672],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7671,7671],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":7658,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7659,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7678,7679],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7678,7678],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":7673,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7674,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7693,7694],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7693,7693],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":7685,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7686,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7705,7706],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7705,7705],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":7694,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7695,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7714,7715],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7714,7714],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":7696,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7697,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7716,7717],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7716,7716],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":7698,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7699,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7718,7719],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7718,7718],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":7704,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7705,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7724,7725],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7724,7724],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":7713,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7714,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7733,7734],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7733,7733],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":7730,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7731,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7750,7751],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7750,7750],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":7736,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7737,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7756,7757],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7756,7756],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":7745,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7746,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7765,7766],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7765,7765],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":7762,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7763,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7782,7783],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7782,7782],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":7826,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7827,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7846,7847],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7846,7846],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":7835,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7836,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7855,7856],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7855,7855],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":7852,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7853,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7872,7873],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7872,7872],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":7858,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7859,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7878,7879],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7878,7878],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":7867,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7868,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7887,7888],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7887,7887],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":7884,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7885,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7904,7905],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7904,7904],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\`.","line":2,"column":7890,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7891,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7910,7911],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7910,7910],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\`.","line":2,"column":7899,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7900,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7919,7920],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7919,7919],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\`.","line":2,"column":7916,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7917,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7936,7937],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7936,7936],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":7958,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7959,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7978,7979],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7978,7978],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":7967,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7968,"suggestions":[{"messageId":"removeEscape","fix":{"range":[7987,7988],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[7987,7987],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":7983,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7984,"suggestions":[{"messageId":"removeEscape","fix":{"range":[8003,8004],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[8003,8003],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":7989,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7990,"suggestions":[{"messageId":"removeEscape","fix":{"range":[8009,8010],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[8009,8009],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":7998,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":7999,"suggestions":[{"messageId":"removeEscape","fix":{"range":[8018,8019],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[8018,8018],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":8014,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":8015,"suggestions":[{"messageId":"removeEscape","fix":{"range":[8034,8035],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[8034,8034],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":8071,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":8072,"suggestions":[{"messageId":"removeEscape","fix":{"range":[8091,8092],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[8091,8091],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":8078,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":8079,"suggestions":[{"messageId":"removeEscape","fix":{"range":[8098,8099],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[8098,8098],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":8082,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":8083,"suggestions":[{"messageId":"removeEscape","fix":{"range":[8102,8103],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[8102,8102],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":8084,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":8085,"suggestions":[{"messageId":"removeEscape","fix":{"range":[8104,8105],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[8104,8104],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":8091,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":8092,"suggestions":[{"messageId":"removeEscape","fix":{"range":[8111,8112],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[8111,8111],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\..","line":2,"column":9231,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":9232,"suggestions":[{"messageId":"removeEscape","fix":{"range":[9251,9252],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[9251,9251],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":9235,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":9236,"suggestions":[{"messageId":"removeEscape","fix":{"range":[9255,9256],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[9255,9255],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":9237,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":9238,"suggestions":[{"messageId":"removeEscape","fix":{"range":[9257,9258],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[9257,9257],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\`.","line":2,"column":9239,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":9240,"suggestions":[{"messageId":"removeEscape","fix":{"range":[9259,9260],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[9259,9259],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\/.","line":2,"column":9241,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":9242,"suggestions":[{"messageId":"removeEscape","fix":{"range":[9261,9262],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[9261,9261],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\#.","line":2,"column":9243,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":9244,"suggestions":[{"messageId":"removeEscape","fix":{"range":[9263,9264],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[9263,9263],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-redeclare","severity":2,"message":"'Y' is already defined.","line":2,"column":10812,"nodeType":"Identifier","messageId":"redeclared","endLine":2,"endColumn":10813,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-unused-vars","severity":1,"message":"'ae' is assigned a value but never used. Allowed unused vars must match /^_/u.","line":2,"column":11414,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":11416,"suggestions":[{"messageId":"removeVar","data":{"varName":"ae"},"fix":{"range":[11430,11442],"text":""},"desc":"Remove unused variable 'ae'."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-unused-vars","severity":1,"message":"'af' is assigned a value but never used. Allowed unused vars must match /^_/u.","line":2,"column":11438,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":11440,"suggestions":[{"messageId":"removeVar","data":{"varName":"af"},"fix":{"range":[11454,11468],"text":""},"desc":"Remove unused variable 'af'."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-unused-vars","severity":1,"message":"'ag' is assigned a value but never used. Allowed unused vars must match /^_/u.","line":2,"column":11471,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":11473,"suggestions":[{"messageId":"removeVar","data":{"varName":"ag"},"fix":{"range":[11487,11499],"text":""},"desc":"Remove unused variable 'ag'."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-redeclare","severity":2,"message":"'W' is already defined.","line":2,"column":11501,"nodeType":"Identifier","messageId":"redeclared","endLine":2,"endColumn":11502,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-prototype-builtins","severity":2,"message":"Do not access Object.prototype method 'hasOwnProperty' from target object.","line":2,"column":11964,"nodeType":"CallExpression","messageId":"prototypeBuildIn","endLine":2,"endColumn":11978,"suggestions":[{"messageId":"callObjectPrototype","data":{"prop":"hasOwnProperty"},"fix":{"range":[11982,11999],"text":"Object.prototype.hasOwnProperty.call(t, "},"desc":"Call Object.prototype.hasOwnProperty explicitly."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-prototype-builtins","severity":2,"message":"Do not access Object.prototype method 'hasOwnProperty' from target object.","line":2,"column":12097,"nodeType":"CallExpression","messageId":"prototypeBuildIn","endLine":2,"endColumn":12111,"suggestions":[{"messageId":"callObjectPrototype","data":{"prop":"hasOwnProperty"},"fix":{"range":[12115,12132],"text":"Object.prototype.hasOwnProperty.call(t, "},"desc":"Call Object.prototype.hasOwnProperty explicitly."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\!.","line":2,"column":12253,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":12254,"suggestions":[{"messageId":"removeEscape","fix":{"range":[12273,12274],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[12273,12273],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\-.","line":2,"column":12269,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":12270,"suggestions":[{"messageId":"removeEscape","fix":{"range":[12289,12290],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[12289,12289],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":12685,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":12686,"suggestions":[{"messageId":"removeEscape","fix":{"range":[12705,12706],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[12705,12705],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":12689,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":12690,"suggestions":[{"messageId":"removeEscape","fix":{"range":[12709,12710],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[12709,12709],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":12693,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":12694,"suggestions":[{"messageId":"removeEscape","fix":{"range":[12713,12714],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[12713,12713],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":12697,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":12698,"suggestions":[{"messageId":"removeEscape","fix":{"range":[12717,12718],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[12717,12717],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":12701,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":12702,"suggestions":[{"messageId":"removeEscape","fix":{"range":[12721,12722],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[12721,12721],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":12705,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":12706,"suggestions":[{"messageId":"removeEscape","fix":{"range":[12725,12726],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[12725,12725],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":12835,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":12836,"suggestions":[{"messageId":"removeEscape","fix":{"range":[12855,12856],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[12855,12855],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":12837,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":12838,"suggestions":[{"messageId":"removeEscape","fix":{"range":[12857,12858],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[12857,12857],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":12849,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":12850,"suggestions":[{"messageId":"removeEscape","fix":{"range":[12869,12870],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[12869,12869],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":12851,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":12852,"suggestions":[{"messageId":"removeEscape","fix":{"range":[12871,12872],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[12871,12871],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\/.","line":2,"column":12855,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":12856,"suggestions":[{"messageId":"removeEscape","fix":{"range":[12875,12876],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[12875,12875],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\/.","line":2,"column":12881,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":12882,"suggestions":[{"messageId":"removeEscape","fix":{"range":[12901,12902],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[12901,12901],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":12913,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":12914,"suggestions":[{"messageId":"removeEscape","fix":{"range":[12933,12934],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[12933,12933],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":12918,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":12919,"suggestions":[{"messageId":"removeEscape","fix":{"range":[12938,12939],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[12938,12938],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":12923,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":12924,"suggestions":[{"messageId":"removeEscape","fix":{"range":[12943,12944],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[12943,12943],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":12954,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":12955,"suggestions":[{"messageId":"removeEscape","fix":{"range":[12974,12975],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[12974,12974],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":12959,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":12960,"suggestions":[{"messageId":"removeEscape","fix":{"range":[12979,12980],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[12979,12979],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":12964,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":12965,"suggestions":[{"messageId":"removeEscape","fix":{"range":[12984,12985],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[12984,12984],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":12998,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":12999,"suggestions":[{"messageId":"removeEscape","fix":{"range":[13018,13019],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[13018,13018],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":13000,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":13001,"suggestions":[{"messageId":"removeEscape","fix":{"range":[13020,13021],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[13020,13020],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":13038,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":13039,"suggestions":[{"messageId":"removeEscape","fix":{"range":[13058,13059],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[13058,13058],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":13043,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":13044,"suggestions":[{"messageId":"removeEscape","fix":{"range":[13063,13064],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[13063,13063],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":13048,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":13049,"suggestions":[{"messageId":"removeEscape","fix":{"range":[13068,13069],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[13068,13068],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":13080,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":13081,"suggestions":[{"messageId":"removeEscape","fix":{"range":[13100,13101],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[13100,13100],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":13085,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":13086,"suggestions":[{"messageId":"removeEscape","fix":{"range":[13105,13106],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[13105,13105],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":13090,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":13091,"suggestions":[{"messageId":"removeEscape","fix":{"range":[13110,13111],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[13110,13110],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":13125,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":13126,"suggestions":[{"messageId":"removeEscape","fix":{"range":[13145,13146],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[13145,13145],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":13127,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":13128,"suggestions":[{"messageId":"removeEscape","fix":{"range":[13147,13148],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[13147,13147],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-unused-vars","severity":1,"message":"'ae' is assigned a value but never used. Allowed unused vars must match /^_/u.","line":2,"column":14757,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":14759,"suggestions":[{"messageId":"removeVar","data":{"varName":"ae"},"fix":{"range":[14773,14798],"text":""},"desc":"Remove unused variable 'ae'."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-undef","severity":2,"message":"'PR' is not defined.","line":2,"column":15786,"nodeType":"Identifier","messageId":"undef","endLine":2,"endColumn":15788,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-undef","severity":2,"message":"'PR' is not defined.","line":2,"column":15809,"nodeType":"Identifier","messageId":"undef","endLine":2,"endColumn":15811,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-undef","severity":2,"message":"'PR' is not defined.","line":2,"column":15835,"nodeType":"Identifier","messageId":"undef","endLine":2,"endColumn":15837,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-undef","severity":2,"message":"'PR' is not defined.","line":2,"column":15875,"nodeType":"Identifier","messageId":"undef","endLine":2,"endColumn":15877,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\!.","line":2,"column":15892,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":15893,"suggestions":[{"messageId":"removeEscape","fix":{"range":[15912,15913],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[15912,15912],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\-.","line":2,"column":15908,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":15909,"suggestions":[{"messageId":"removeEscape","fix":{"range":[15928,15929],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[15928,15928],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-undef","severity":2,"message":"'PR' is not defined.","line":2,"column":15918,"nodeType":"Identifier","messageId":"undef","endLine":2,"endColumn":15920,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-undef","severity":2,"message":"'PR' is not defined.","line":2,"column":16362,"nodeType":"Identifier","messageId":"undef","endLine":2,"endColumn":16364,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-undef","severity":2,"message":"'PR' is not defined.","line":2,"column":16410,"nodeType":"Identifier","messageId":"undef","endLine":2,"endColumn":16412,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-undef","severity":2,"message":"'PR' is not defined.","line":2,"column":16453,"nodeType":"Identifier","messageId":"undef","endLine":2,"endColumn":16455,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-undef","severity":2,"message":"'PR' is not defined.","line":2,"column":16498,"nodeType":"Identifier","messageId":"undef","endLine":2,"endColumn":16500,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-undef","severity":2,"message":"'PR' is not defined.","line":2,"column":16551,"nodeType":"Identifier","messageId":"undef","endLine":2,"endColumn":16553,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-undef","severity":2,"message":"'PR' is not defined.","line":2,"column":16574,"nodeType":"Identifier","messageId":"undef","endLine":2,"endColumn":16576,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-undef","severity":2,"message":"'PR' is not defined.","line":2,"column":16597,"nodeType":"Identifier","messageId":"undef","endLine":2,"endColumn":16599,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-undef","severity":2,"message":"'PR' is not defined.","line":2,"column":16646,"nodeType":"Identifier","messageId":"undef","endLine":2,"endColumn":16648,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":16661,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":16662,"suggestions":[{"messageId":"removeEscape","fix":{"range":[16681,16682],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[16681,16681],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":16676,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":16677,"suggestions":[{"messageId":"removeEscape","fix":{"range":[16696,16697],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[16696,16696],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":16708,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":16709,"suggestions":[{"messageId":"removeEscape","fix":{"range":[16728,16729],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[16728,16728],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-undef","severity":2,"message":"'PR' is not defined.","line":2,"column":16719,"nodeType":"Identifier","messageId":"undef","endLine":2,"endColumn":16721,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":16734,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":16735,"suggestions":[{"messageId":"removeEscape","fix":{"range":[16754,16755],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[16754,16754],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":16749,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":16750,"suggestions":[{"messageId":"removeEscape","fix":{"range":[16769,16770],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[16769,16769],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":16781,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":16782,"suggestions":[{"messageId":"removeEscape","fix":{"range":[16801,16802],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[16801,16801],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\).","line":2,"column":16817,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":16818,"suggestions":[{"messageId":"removeEscape","fix":{"range":[16837,16838],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[16837,16837],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":16819,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":16820,"suggestions":[{"messageId":"removeEscape","fix":{"range":[16839,16840],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[16839,16839],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":16821,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":16822,"suggestions":[{"messageId":"removeEscape","fix":{"range":[16841,16842],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[16841,16841],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-undef","severity":2,"message":"'PR' is not defined.","line":2,"column":16833,"nodeType":"Identifier","messageId":"undef","endLine":2,"endColumn":16835,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\!.","line":2,"column":16860,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":16861,"suggestions":[{"messageId":"removeEscape","fix":{"range":[16880,16881],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[16880,16880],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\-.","line":2,"column":16991,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":16992,"suggestions":[{"messageId":"removeEscape","fix":{"range":[17011,17012],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[17011,17011],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-undef","severity":2,"message":"'PR' is not defined.","line":2,"column":17026,"nodeType":"Identifier","messageId":"undef","endLine":2,"endColumn":17028,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\/.","line":2,"column":17059,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":17060,"suggestions":[{"messageId":"removeEscape","fix":{"range":[17079,17080],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[17079,17079],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-undef","severity":2,"message":"'PR' is not defined.","line":2,"column":17079,"nodeType":"Identifier","messageId":"undef","endLine":2,"endColumn":17081,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-undef","severity":2,"message":"'PR' is not defined.","line":2,"column":17111,"nodeType":"Identifier","messageId":"undef","endLine":2,"endColumn":17113,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-undef","severity":2,"message":"'PR' is not defined.","line":2,"column":17161,"nodeType":"Identifier","messageId":"undef","endLine":2,"endColumn":17163,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-undef","severity":2,"message":"'PR' is not defined.","line":2,"column":17203,"nodeType":"Identifier","messageId":"undef","endLine":2,"endColumn":17205,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\-.","line":2,"column":17256,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":17257,"suggestions":[{"messageId":"removeEscape","fix":{"range":[17276,17277],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[17276,17276],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-undef","severity":2,"message":"'PR' is not defined.","line":2,"column":17285,"nodeType":"Identifier","messageId":"undef","endLine":2,"endColumn":17287,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":17311,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":17312,"suggestions":[{"messageId":"removeEscape","fix":{"range":[17331,17332],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[17331,17331],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":17313,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":17314,"suggestions":[{"messageId":"removeEscape","fix":{"range":[17333,17334],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[17333,17333],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-undef","severity":2,"message":"'PR' is not defined.","line":2,"column":17331,"nodeType":"Identifier","messageId":"undef","endLine":2,"endColumn":17333,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-undef","severity":2,"message":"'PR' is not defined.","line":2,"column":17354,"nodeType":"Identifier","messageId":"undef","endLine":2,"endColumn":17356,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-undef","severity":2,"message":"'PR' is not defined.","line":2,"column":17380,"nodeType":"Identifier","messageId":"undef","endLine":2,"endColumn":17382,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\-.","line":2,"column":17435,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":17436,"suggestions":[{"messageId":"removeEscape","fix":{"range":[17455,17456],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[17455,17455],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-undef","severity":2,"message":"'PR' is not defined.","line":2,"column":17477,"nodeType":"Identifier","messageId":"undef","endLine":2,"endColumn":17479,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-undef","severity":2,"message":"'PR' is not defined.","line":2,"column":17500,"nodeType":"Identifier","messageId":"undef","endLine":2,"endColumn":17502,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-undef","severity":2,"message":"'PR' is not defined.","line":2,"column":17526,"nodeType":"Identifier","messageId":"undef","endLine":2,"endColumn":17528,"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\).","line":2,"column":17543,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":17544,"suggestions":[{"messageId":"removeEscape","fix":{"range":[17563,17564],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[17563,17563],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\\".","line":2,"column":17545,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":17546,"suggestions":[{"messageId":"removeEscape","fix":{"range":[17565,17566],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[17565,17565],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]},{"ruleId":"no-useless-escape","severity":2,"message":"Unnecessary escape character: \\'.","line":2,"column":17547,"nodeType":"Literal","messageId":"unnecessaryEscape","endLine":2,"endColumn":17548,"suggestions":[{"messageId":"removeEscape","fix":{"range":[17567,17568],"text":""},"desc":"Remove the `\\`. This maintains the current functionality."},{"messageId":"escapeBackslash","fix":{"range":[17567,17567],"text":"\\"},"desc":"Replace the `\\` with `\\\\` to include the actual backslash character."}],"suppressions":[{"kind":"directive","justification":""}]}],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/coverage/sorter.js","messages":[],"suppressedMessages":[{"ruleId":"no-unused-vars","severity":1,"message":"'error' is defined but never used.","line":36,"column":18,"nodeType":"Identifier","messageId":"unusedVar","endLine":36,"endColumn":23,"suppressions":[{"kind":"directive","justification":""}]}],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/eslint.config.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/postcss.config.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/App.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'Suspense' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":16,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":24,"suggestions":[{"messageId":"removeVar","data":{"varName":"Suspense"},"fix":{"range":[13,23],"text":""},"desc":"Remove unused variable 'Suspense'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Router' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":27,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":33,"suggestions":[{"messageId":"removeVar","data":{"varName":"Router"},"fix":{"range":[49,73],"text":""},"desc":"Remove unused variable 'Router'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Routes' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":35,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":41,"suggestions":[{"messageId":"removeVar","data":{"varName":"Routes"},"fix":{"range":[72,80],"text":""},"desc":"Remove unused variable 'Routes'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Route' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":43,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":48,"suggestions":[{"messageId":"removeVar","data":{"varName":"Route"},"fix":{"range":[80,87],"text":""},"desc":"Remove unused variable 'Route'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'HomePage' is defined but never used. Allowed unused vars must match /^_/u.","line":4,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":4,"endColumn":16,"suggestions":[{"messageId":"removeVar","data":{"varName":"HomePage"},"fix":{"range":[142,156],"text":""},"desc":"Remove unused variable 'HomePage'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'LabPython' is defined but never used. Allowed unused vars must match /^_/u.","line":5,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":5,"endColumn":17,"suggestions":[{"messageId":"removeVar","data":{"varName":"LabPython"},"fix":{"range":[192,207],"text":""},"desc":"Remove unused variable 'LabPython'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Playground' is assigned a value but never used. Allowed unused vars must match /^_/u.","line":7,"column":7,"nodeType":"Identifier","messageId":"unusedVar","endLine":7,"endColumn":17,"suggestions":[{"messageId":"removeVar","data":{"varName":"Playground"},"fix":{"range":[239,310],"text":""},"desc":"Remove unused variable 'Playground'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'About' is assigned a value but never used. Allowed unused vars must match /^_/u.","line":8,"column":7,"nodeType":"Identifier","messageId":"unusedVar","endLine":8,"endColumn":12,"suggestions":[{"messageId":"removeVar","data":{"varName":"About"},"fix":{"range":[311,367],"text":""},"desc":"Remove unused variable 'About'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Faq' is assigned a value but never used. Allowed unused vars must match /^_/u.","line":9,"column":7,"nodeType":"Identifier","messageId":"unusedVar","endLine":9,"endColumn":10,"suggestions":[{"messageId":"removeVar","data":{"varName":"Faq"},"fix":{"range":[368,418],"text":""},"desc":"Remove unused variable 'Faq'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Atividades' is assigned a value but never used. Allowed unused vars must match /^_/u.","line":10,"column":7,"nodeType":"Identifier","messageId":"unusedVar","endLine":10,"endColumn":17,"suggestions":[{"messageId":"removeVar","data":{"varName":"Atividades"},"fix":{"range":[419,490],"text":""},"desc":"Remove unused variable 'Atividades'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Educadores' is assigned a value but never used. Allowed unused vars must match /^_/u.","line":11,"column":7,"nodeType":"Identifier","messageId":"unusedVar","endLine":11,"endColumn":17,"suggestions":[{"messageId":"removeVar","data":{"varName":"Educadores"},"fix":{"range":[491,562],"text":""},"desc":"Remove unused variable 'Educadores'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'AutomatoGame' is assigned a value but never used. Allowed unused vars must match /^_/u.","line":14,"column":7,"nodeType":"Identifier","messageId":"unusedVar","endLine":14,"endColumn":19,"suggestions":[{"messageId":"removeVar","data":{"varName":"AutomatoGame"},"fix":{"range":[577,650],"text":""},"desc":"Remove unused variable 'AutomatoGame'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'SemaforoGame' is assigned a value but never used. Allowed unused vars must match /^_/u.","line":15,"column":7,"nodeType":"Identifier","messageId":"unusedVar","endLine":15,"endColumn":19,"suggestions":[{"messageId":"removeVar","data":{"varName":"SemaforoGame"},"fix":{"range":[651,724],"text":""},"desc":"Remove unused variable 'SemaforoGame'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'MoleMashGame' is assigned a value but never used. Allowed unused vars must match /^_/u.","line":16,"column":7,"nodeType":"Identifier","messageId":"unusedVar","endLine":16,"endColumn":19,"suggestions":[{"messageId":"removeVar","data":{"varName":"MoleMashGame"},"fix":{"range":[725,795],"text":""},"desc":"Remove unused variable 'MoleMashGame'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'TurtleGame' is assigned a value but never used. Allowed unused vars must match /^_/u.","line":17,"column":7,"nodeType":"Identifier","messageId":"unusedVar","endLine":17,"endColumn":17,"suggestions":[{"messageId":"removeVar","data":{"varName":"TurtleGame"},"fix":{"range":[796,863],"text":""},"desc":"Remove unused variable 'TurtleGame'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'CriptoGame' is assigned a value but never used. Allowed unused vars must match /^_/u.","line":18,"column":7,"nodeType":"Identifier","messageId":"unusedVar","endLine":18,"endColumn":17,"suggestions":[{"messageId":"removeVar","data":{"varName":"CriptoGame"},"fix":{"range":[864,931],"text":""},"desc":"Remove unused variable 'CriptoGame'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'LoadingFallback' is assigned a value but never used. Allowed unused vars must match /^_/u.","line":20,"column":7,"nodeType":"Identifier","messageId":"unusedVar","endLine":20,"endColumn":22,"suggestions":[{"messageId":"removeVar","data":{"varName":"LoadingFallback"},"fix":{"range":[933,1639],"text":""},"desc":"Remove unused variable 'LoadingFallback'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":17,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { lazy, Suspense } from \"react\";\nimport { BrowserRouter as Router, Routes, Route } from \"react-router-dom\";\nimport \"./App.css\";\nimport HomePage from \"./pages/HomePage/HomePage\";\nimport LabPython from \"./pages/LabPython/LabPython\";\n\nconst Playground = lazy(() => import(\"./pages/Playground/Playground\"));\nconst About = lazy(() => import(\"./pages/About/About\"));\nconst Faq = lazy(() => import(\"./pages/Faq/Faq\"));\nconst Atividades = lazy(() => import(\"./pages/Atividades/Atividades\"));\nconst Educadores = lazy(() => import(\"./pages/Educadores/Educadores\"));\n\n//Atividades\nconst AutomatoGame = lazy(() => import(\"./games/automato/AutomatoGame\"));\nconst SemaforoGame = lazy(() => import(\"./games/semaforo/SemaforoGame\"));\nconst MoleMashGame = lazy(() => import(\"./games/mole-mash/MoleMash\"));\nconst TurtleGame = lazy(() => import(\"./games/turtle/TurtleGame\"));\nconst CriptoGame = lazy(() => import(\"./games/cripto/CriptoGame\"));\n\nconst LoadingFallback = () => (\n <div\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n height: \"100vh\",\n background: \"linear-gradient(135deg, #fe0002 0%, #f50c52 100%)\",\n color: \"white\",\n fontSize: \"1.5rem\",\n fontWeight: \"600\",\n }}\n >\n <div style={{ textAlign: \"center\" }}>\n <div\n style={{\n width: \"50px\",\n height: \"50px\",\n border: \"4px solid rgba(255,255,255,0.3)\",\n borderTop: \"4px solid white\",\n borderRadius: \"50%\",\n animation: \"spin 1s linear infinite\",\n margin: \"0 auto 20px\",\n }}\n ></div>\n Carregando...\n </div>\n </div>\n);\n\nexport default function App() {\n return (\n <Router>\n <Suspense fallback={<LoadingFallback />}>\n <Routes>\n <Route path=\"/\" element={<HomePage />} />\n <Route path=\"/playground\" element={<Playground />} />\n <Route path=\"/laboratorio\" element={<Playground />} />\n <Route path=\"/laboratorio-python\" element={<LabPython />} />\n <Route path=\"/sobre\" element={<About />} />\n <Route path=\"/faq\" element={<Faq />} />\n <Route path=\"/atividades\" element={<Atividades />} />\n <Route path=\"/educadores\" element={<Educadores />} />\n <Route path=\"/games/automato\" element={<AutomatoGame />} />\n <Route path=\"/games/cripto\" element={<CriptoGame />} />\n <Route path=\"/games/semaforo\" element={<SemaforoGame />} />\n <Route path=\"/games/molemash\" element={<MoleMashGame />} />\n <Route path=\"/games/turtle\" element={<TurtleGame />} />\n </Routes>\n </Suspense>\n </Router>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/components/Navbar.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'Link' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":14,"suggestions":[{"messageId":"removeVar","data":{"varName":"Link"},"fix":{"range":[0,40],"text":""},"desc":"Remove unused variable 'Link'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Menu' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":14,"suggestions":[{"messageId":"removeVar","data":{"varName":"Menu"},"fix":{"range":[50,55],"text":""},"desc":"Remove unused variable 'Menu'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'X' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":16,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":17,"suggestions":[{"messageId":"removeVar","data":{"varName":"X"},"fix":{"range":[54,57],"text":""},"desc":"Remove unused variable 'X'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'ChevronDown' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":19,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":30,"suggestions":[{"messageId":"removeVar","data":{"varName":"ChevronDown"},"fix":{"range":[57,70],"text":""},"desc":"Remove unused variable 'ChevronDown'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'scrolled' is assigned a value but never used. Allowed unused vars must match /^_/u.","line":8,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":8,"endColumn":18,"suggestions":[{"messageId":"removeVar","data":{"varName":"scrolled"},"fix":{"range":[273,281],"text":""},"desc":"Remove unused variable 'scrolled'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'openDropdown' is assigned a value but never used. Allowed unused vars must match /^_/u.","line":9,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":9,"endColumn":22,"suggestions":[{"messageId":"removeVar","data":{"varName":"openDropdown"},"fix":{"range":[324,336],"text":""},"desc":"Remove unused variable 'openDropdown'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'handleMouseEnter' is assigned a value but never used. Allowed unused vars must match /^_/u.","line":23,"column":9,"nodeType":"Identifier","messageId":"unusedVar","endLine":23,"endColumn":25,"suggestions":[{"messageId":"removeVar","data":{"varName":"handleMouseEnter"},"fix":{"range":[731,900],"text":""},"desc":"Remove unused variable 'handleMouseEnter'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'handleMouseLeave' is assigned a value but never used. Allowed unused vars must match /^_/u.","line":31,"column":9,"nodeType":"Identifier","messageId":"unusedVar","endLine":31,"endColumn":25,"suggestions":[{"messageId":"removeVar","data":{"varName":"handleMouseLeave"},"fix":{"range":[904,1052],"text":""},"desc":"Remove unused variable 'handleMouseLeave'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":8,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { Link } from \"react-router-dom\";\nimport { Menu, X, ChevronDown } from \"lucide-react\";\nimport { useState, useEffect } from \"react\";\nimport logo from \"../assets/logo_decoda.svg\";\n\nconst Navbar = () => {\n const [isMenuOpen, setIsMenuOpen] = useState(false);\n const [scrolled, setScrolled] = useState(false);\n const [openDropdown, setOpenDropdown] = useState(null);\n const [closeTimeout, setCloseTimeout] = useState(null);\n\n useEffect(() => {\n const handleScroll = () => {\n setScrolled(window.scrollY > 50);\n };\n\n window.addEventListener(\"scroll\", handleScroll, { passive: true });\n handleScroll(); // Check initial state\n\n return () => window.removeEventListener(\"scroll\", handleScroll);\n }, []);\n\n const handleMouseEnter = (dropdown) => {\n if (closeTimeout) {\n clearTimeout(closeTimeout);\n setCloseTimeout(null);\n }\n setOpenDropdown(dropdown);\n };\n\n const handleMouseLeave = () => {\n const timeout = setTimeout(() => {\n setOpenDropdown(null);\n }, 150);\n setCloseTimeout(timeout);\n };\n\n return (\n <nav\n className={`fixed top-0 left-0 right-0 z-50 border-b border-white/20 transition-all duration-300 bg-white`}\n >\n <div className=\"flex items-center justify-between px-4 py-1 lg:px-12 lg:py-12\">\n {/* Logo - Esquerda */}\n <div className=\"flex items-center space-x-4\">\n <Link\n to=\"/\"\n className=\"header-logo group cursor-pointer flex items-center space-x-2 hover:bg-white/10 px-3 py-2 rounded-lg transition-all duration-200\"\n >\n <img\n src={logo}\n alt=\"Decoda Logo\"\n className=\"h-[83px] transition-transform duration-200 group-hover:scale-110\"\n />\n </Link>\n </div>\n\n {/* Espaço Central vazio para empurrar o menu para a direita */}\n <div className=\"flex-1 flex justify-center\"></div>\n\n {/* Menu Desktop - Direita */}\n <div className=\"flex items-center space-x-2\">\n <div className=\"hidden md:flex items-center gap-6 lg:gap-8\">\n {/* Links diretos - Atividades */}\n <Link\n to=\"/atividades\"\n className=\"text-black hover:text-black/80 font-medium transition-colors hover:underline underline-offset-4\"\n >\n Atividades\n </Link>\n\n {/* Links diretos - Laboratório de Blocos */}\n <Link\n to=\"/playground\"\n className=\"text-black hover:text-black/80 font-medium transition-colors hover:underline underline-offset-4\"\n >\n Laboratório de Blocos\n </Link>\n\n {/* Links diretos - Laboratório Pytohn */}\n <Link\n to=\"/laboratorio-python/\"\n className=\"text-black hover:text-black/80 font-medium transition-colors hover:underline underline-offset-4\"\n >\n Laboratório Python\n </Link>\n\n <Link\n to=\"/sobre\"\n className=\"text-black hover:text-black/80 font-medium transition-colors hover:underline underline-offset-4\"\n >\n Quem somos\n </Link>\n\n <Link\n to=\"/faq\"\n className=\"text-black hover:text-black/80 font-medium transition-colors hover:underline underline-offset-4\"\n >\n Perguntas frequentes\n </Link>\n\n <Link\n to=\"/educadores\"\n className=\"text-black hover:text-black/80 font-medium transition-colors hover:underline underline-offset-4\"\n >\n Para educadores\n </Link>\n </div>\n\n {/* Botão Mobile Menu */}\n <button\n onClick={() => setIsMenuOpen(!isMenuOpen)}\n className=\"md:hidden p-2 rounded-lg hover:bg-white/10 transition-colors\"\n aria-label=\"Toggle menu\"\n >\n {isMenuOpen ? (\n <X className=\"size-10 text-red-600\" />\n ) : (\n <Menu className=\"size-10 text-red-600\" />\n )}\n </button>\n </div>\n </div>\n\n {/* Menu Mobile */}\n {isMenuOpen && (\n <div className=\"md:hidden border-t border-white/20 bg-white backdrop-blur-sm\">\n <div className=\"px-6 py-4 space-y-2\">\n {/* Links diretos Mobile */}\n <Link\n to=\"/atividades\"\n className=\"block px-4 py-2 text-black hover:bg-black/10 rounded-lg font-medium transition-colors\"\n onClick={() => setIsMenuOpen(false)}\n >\n Atividades\n </Link>\n <Link\n to=\"/playground\"\n className=\"block px-4 py-2 text-black hover:bg-black/10 rounded-lg font-medium transition-colors\"\n onClick={() => setIsMenuOpen(false)}\n >\n Laboratório\n </Link>\n <Link\n to=\"/sobre\"\n className=\"block px-4 py-2 text-black hover:bg-black/10 rounded-lg font-medium transition-colors\"\n onClick={() => setIsMenuOpen(false)}\n >\n Quem somos\n </Link>\n <Link\n to=\"/faq\"\n className=\"block px-4 py-2 text-black hover:bg-black/10 rounded-lg font-medium transition-colors\"\n onClick={() => setIsMenuOpen(false)}\n >\n Perguntas frequentes\n </Link>\n <Link\n to=\"/educadores\"\n className=\"block px-4 py-2 text-black hover:bg-black/10 rounded-lg font-medium transition-colors\"\n onClick={() => setIsMenuOpen(false)}\n >\n Para educadores\n </Link>\n </div>\n </div>\n )}\n </nav>\n );\n};\n\nexport default Navbar;\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/components/game/ConfettiOverlay.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'React' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":13,"suggestions":[{"messageId":"removeVar","data":{"varName":"React"},"fix":{"range":[7,13],"text":""},"desc":"Remove unused variable 'React'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import React, { useEffect, useRef } from \"react\";\nimport PropTypes from \"prop-types\";\nimport { confetti } from \"@tsparticles/confetti\";\n\nconst ConfettiOverlay = ({ isActive, onComplete }) => {\n const canvasRef = useRef(null);\n const animationRef = useRef(null);\n\n useEffect(() => {\n if (isActive && canvasRef.current) {\n const canvas = canvasRef.current;\n\n const randomInRange = (min, max) => {\n return Math.random() * (max - min) + min;\n };\n\n const triggerConfettiBlast = async () => {\n const defaultOptions = {\n angle: randomInRange(55, 125),\n spread: randomInRange(50, 70),\n particleCount: randomInRange(50, 100),\n origin: { y: 0.6 },\n canvas: canvas,\n };\n\n await confetti(defaultOptions);\n };\n\n triggerConfettiBlast();\n\n animationRef.current = setTimeout(() => {\n if (onComplete) {\n onComplete();\n }\n }, 500);\n } else if (!isActive && canvasRef.current) {\n const canvas = canvasRef.current;\n const ctx = canvas.getContext(\"2d\");\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n }\n\n return () => {\n if (animationRef.current) {\n clearTimeout(animationRef.current);\n }\n };\n }, [isActive, onComplete]);\n\n useEffect(() => {\n const updateCanvasSize = () => {\n if (canvasRef.current) {\n canvasRef.current.width = window.innerWidth;\n canvasRef.current.height = window.innerHeight;\n }\n };\n\n updateCanvasSize();\n window.addEventListener(\"resize\", updateCanvasSize);\n return () => window.removeEventListener(\"resize\", updateCanvasSize);\n }, []);\n\n return (\n <canvas\n ref={canvasRef}\n style={{\n position: \"fixed\",\n top: 0,\n left: 0,\n width: \"100%\",\n height: \"100%\",\n pointerEvents: \"none\",\n zIndex: 9999,\n opacity: isActive ? 1 : 0,\n transition: \"opacity 0.3s ease\",\n }}\n />\n );\n};\n\nConfettiOverlay.propTypes = {\n isActive: PropTypes.bool.isRequired,\n onComplete: PropTypes.func,\n};\n\nexport default ConfettiOverlay;\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/components/game/ConfirmacaoModal.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'X' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":11,"suggestions":[{"messageId":"removeVar","data":{"varName":"X"},"fix":{"range":[40,73],"text":""},"desc":"Remove unused variable 'X'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"// components/game/ConfirmacaoModal.jsx\nimport { X } from \"lucide-react\";\n\nexport default function ConfirmacaoModal({\n isVisible,\n onClose,\n onConfirm,\n titulo,\n mensagem,\n}) {\n if (!isVisible) return null;\n\n return (\n <div\n className=\"fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center p-4 z-50\"\n onClick={onClose}\n >\n <div\n className=\"bg-white/95 backdrop-blur-sm border border-white/30 rounded-xl shadow-xl max-w-md w-full p-6 relative\"\n onClick={(e) => e.stopPropagation()}\n >\n {/* Header */}\n <div className=\"flex justify-between items-center mb-4\">\n <h3 className=\"text-lg font-bold text-gray-800\">{titulo}</h3>\n <button\n className=\"w-8 h-8 rounded-lg bg-gray-200 hover:bg-gray-300 flex items-center justify-center text-gray-600 transition-colors\"\n onClick={onClose}\n >\n <X className=\"w-4 h-4\" />\n </button>\n </div>\n\n {/* Mensagem */}\n <p className=\"text-gray-700 mb-6\">{mensagem}</p>\n\n {/* Ações */}\n <div className=\"flex justify-end space-x-3\">\n <button\n className=\"px-4 py-2 bg-gray-200 rounded-lg hover:bg-gray-300\"\n onClick={onClose}\n >\n Cancelar\n </button>\n <button\n className=\"px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700\"\n onClick={() => {\n onConfirm();\n onClose();\n }}\n >\n Confirmar\n </button>\n </div>\n </div>\n </div>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/components/game/FalhaModal.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'React' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":13,"suggestions":[{"messageId":"removeVar","data":{"varName":"React"},"fix":{"range":[7,18],"text":""},"desc":"Remove unused variable 'React'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'RefreshCw' is defined but never used. Allowed unused vars must match /^_/u.","line":3,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":3,"endColumn":19,"suggestions":[{"messageId":"removeVar","data":{"varName":"RefreshCw"},"fix":{"range":[63,104],"text":""},"desc":"Remove unused variable 'RefreshCw'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'ModalBase' is defined but never used. Allowed unused vars must match /^_/u.","line":5,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":5,"endColumn":19,"suggestions":[{"messageId":"removeVar","data":{"varName":"ModalBase"},"fix":{"range":[106,152],"text":""},"desc":"Remove unused variable 'ModalBase'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'ModalHeader' is defined but never used. Allowed unused vars must match /^_/u.","line":6,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":6,"endColumn":21,"suggestions":[{"messageId":"removeVar","data":{"varName":"ModalHeader"},"fix":{"range":[153,203],"text":""},"desc":"Remove unused variable 'ModalHeader'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'CodeArea' is defined but never used. Allowed unused vars must match /^_/u.","line":7,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":7,"endColumn":18,"suggestions":[{"messageId":"removeVar","data":{"varName":"CodeArea"},"fix":{"range":[204,248],"text":""},"desc":"Remove unused variable 'CodeArea'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'FeedbackBox' is defined but never used. Allowed unused vars must match /^_/u.","line":8,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":8,"endColumn":21,"suggestions":[{"messageId":"removeVar","data":{"varName":"FeedbackBox"},"fix":{"range":[249,299],"text":""},"desc":"Remove unused variable 'FeedbackBox'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":6,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import React from \"react\";\nimport PropTypes from \"prop-types\";\nimport { RefreshCw } from \"lucide-react\";\n\nimport { ModalBase } from \"./modal/ModalBase\";\nimport { ModalHeader } from \"./modal/ModalHeader\";\nimport { CodeArea } from \"./modal/CodeArea\";\nimport { FeedbackBox } from \"./modal/FeedbackBox\";\n\nconst FalhaModal = ({\n isOpen,\n onClose,\n onRetry,\n mensagemCustomizada,\n currentPhase,\n codigoGerado,\n}) => {\n const handleRetry = () => {\n onClose();\n if (onRetry) {\n onRetry();\n }\n };\n\n const mensagemExibida =\n mensagemCustomizada ||\n \"Ops! Parece que algo não funcionou como esperado. Tente novamente!\";\n\n return (\n <ModalBase isOpen={isOpen} onClose={onClose}>\n <ModalHeader\n title=\"Quase lá! Tente novamente\"\n subTitle={`Fase ${currentPhase}`}\n variant=\"failure\"\n onClose={onClose}\n />\n\n <div className=\"flex-1 overflow-hidden flex flex-col\">\n <div className=\"p-6 flex-1 overflow-auto\">\n <div className=\"mb-6 p-4 bg-red-50 border-l-4 border-red-500 rounded-r-lg\">\n <p className=\"text-red-800 font-medium\">{mensagemExibida}</p>\n </div>\n\n <CodeArea\n title=\"Seu Código Atual\"\n code={codigoGerado}\n variant=\"failure\"\n />\n\n <FeedbackBox title=\"Dicas para o Desafio\" variant=\"failure\">\n <ul className=\"text-amber-800 text-sm space-y-1 ml-4 list-disc\">\n <li>Revise o enunciado.</li>\n <li>Verifique se os blocos estão corretamente conectados.</li>\n <li>\n Certifique-se de que a lógica atende a todos os requisitos da\n fase.\n </li>\n </ul>\n </FeedbackBox>\n </div>\n </div>\n\n {/* 3. Rodapé (Botões de Ação) */}\n <div className=\"p-6 border-t border-gray-200 flex justify-between items-center bg-gray-50\">\n <button\n onClick={onClose}\n className=\"px-6 py-2 bg-gray-200 hover:bg-gray-300 text-gray-700 rounded-full font-medium transition-colors\"\n >\n Fechar\n </button>\n\n <button\n onClick={handleRetry}\n className=\"flex items-center space-x-2 px-6 py-3 rounded-full font-medium transition-all duration-200 shadow-md bg-gradient-to-r from-orange-500 to-red-600 hover:from-orange-600 hover:to-red-700 hover:scale-105 hover:shadow-lg text-white\"\n >\n <RefreshCw className=\"w-4 h-4\" />\n <span>Tentar Novamente</span>\n </button>\n </div>\n </ModalBase>\n );\n};\n\nFalhaModal.propTypes = {\n isOpen: PropTypes.bool.isRequired,\n onClose: PropTypes.func.isRequired,\n onRetry: PropTypes.func,\n mensagemCustomizada: PropTypes.string,\n currentPhase: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),\n codigoGerado: PropTypes.string,\n};\n\nexport default FalhaModal;\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/components/game/GameArea.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'ConfettiOverlay' is defined but never used. Allowed unused vars must match /^_/u.","line":4,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":4,"endColumn":23,"suggestions":[{"messageId":"removeVar","data":{"varName":"ConfettiOverlay"},"fix":{"range":[192,213],"text":""},"desc":"Remove unused variable 'ConfettiOverlay'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { useEffect, useState, useRef } from \"react\";\nimport { useGameState, GAME_STATES } from \"../../contexts/GameStateContext\";\nimport { gameEventBus } from \"../../utils/gameEvents\";\nimport ConfettiOverlay from \"./ConfettiOverlay\";\n\nexport default function GameArea({\n children,\n blocosRestantes = null,\n faseId = null,\n}) {\n const {\n executionState,\n generatedCode,\n finalizeWithSuccess,\n finalizeWithFailure,\n } = useGameState();\n\n const [showConfetti, setShowConfetti] = useState(false);\n const [isTransitioning, setIsTransitioning] = useState(false);\n const previousFaseId = useRef(faseId);\n\n useEffect(() => {\n if (faseId !== null && faseId !== previousFaseId.current) {\n setIsTransitioning(true);\n\n const timer = setTimeout(() => {\n setIsTransitioning(false);\n previousFaseId.current = faseId;\n }, 600);\n\n return () => clearTimeout(timer);\n }\n }, [faseId]);\n\n useEffect(() => {\n const handleGameSuccess = () => {\n finalizeWithSuccess();\n setShowConfetti(true);\n };\n\n const handleGameFailure = () => {\n finalizeWithFailure();\n };\n\n gameEventBus.addEventListener(\"gameSuccess\", handleGameSuccess);\n gameEventBus.addEventListener(\"gameFailure\", handleGameFailure);\n\n return () => {\n gameEventBus.removeEventListener(\"gameSuccess\", handleGameSuccess);\n gameEventBus.removeEventListener(\"gameFailure\", handleGameFailure);\n };\n }, [finalizeWithSuccess, finalizeWithFailure]);\n\n useEffect(() => {\n switch (executionState) {\n case GAME_STATES.EXECUTANDO:\n if (generatedCode) {\n const codigo =\n typeof generatedCode === \"string\"\n ? generatedCode\n : generatedCode.codigo;\n const ws =\n typeof generatedCode === \"object\" ? generatedCode.workspace : null;\n\n gameEventBus.executeCode(codigo, ws);\n }\n break;\n case GAME_STATES.PARADO:\n gameEventBus.resetGame();\n setShowConfetti(false);\n break;\n }\n }, [executionState, generatedCode]);\n\n return (\n <div\n className=\"w-full h-full overflow-hidden relative flex items-center justify-center game-area-container\"\n style={{\n background: \"linear-gradient(135deg, #667eea 0%, #764ba2 100%)\",\n borderRadius: \"5px\",\n padding: \"5px\",\n }}\n id=\"visualization\"\n >\n {/* Confetti de sucesso de uma fase */}\n <ConfettiOverlay isActive={showConfetti} />\n\n {/* Overlay de transição */}\n <div\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n right: 0,\n bottom: 0,\n backgroundColor: \"rgba(102, 126, 234)\",\n opacity: isTransitioning ? 1 : 0,\n transition: \"opacity 300ms ease-in-out\",\n pointerEvents: isTransitioning ? \"auto\" : \"none\",\n zIndex: 10,\n borderRadius: \"5px\",\n }}\n className=\"flex items-center justify-center\"\n >\n <div className=\"w-12 h-12 border-4 border-white/30 border-t-white rounded-full animate-spin\" />\n </div>\n\n {/* Indicador de blocos restantes */}\n {blocosRestantes !== null && !isNaN(blocosRestantes) && (\n <div id=\"capacityBubble\">\n <div id=\"capacity\" style={{ display: \"block\" }}>\n Blocos restantes:{\" \"}\n <span className=\"capacityNumber\">{blocosRestantes}</span>\n </div>\n </div>\n )}\n\n <div\n className=\"flex items-center justify-center w-full h-full phaser-container\"\n style={{\n borderRadius: \"5px\",\n backgroundColor: \"#ffffff\",\n border: \"2px solid #333\",\n }}\n >\n {children}\n </div>\n </div>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/components/game/GameBase.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'Panel' is defined but never used. Allowed unused vars must match /^_/u.","line":3,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":3,"endColumn":15,"suggestions":[{"messageId":"removeVar","data":{"varName":"Panel"},"fix":{"range":[111,117],"text":""},"desc":"Remove unused variable 'Panel'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'PanelGroup' is defined but never used. Allowed unused vars must match /^_/u.","line":3,"column":17,"nodeType":"Identifier","messageId":"unusedVar","endLine":3,"endColumn":27,"suggestions":[{"messageId":"removeVar","data":{"varName":"PanelGroup"},"fix":{"range":[116,128],"text":""},"desc":"Remove unused variable 'PanelGroup'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'GameNavBar' is defined but never used. Allowed unused vars must match /^_/u.","line":4,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":4,"endColumn":18,"suggestions":[{"messageId":"removeVar","data":{"varName":"GameNavBar"},"fix":{"range":[169,185],"text":""},"desc":"Remove unused variable 'GameNavBar'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'GameFaseInfo' is defined but never used. Allowed unused vars must match /^_/u.","line":5,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":5,"endColumn":20,"suggestions":[{"messageId":"removeVar","data":{"varName":"GameFaseInfo"},"fix":{"range":[208,226],"text":""},"desc":"Remove unused variable 'GameFaseInfo'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'GameArea' is defined but never used. Allowed unused vars must match /^_/u.","line":6,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":6,"endColumn":16,"suggestions":[{"messageId":"removeVar","data":{"varName":"GameArea"},"fix":{"range":[251,265],"text":""},"desc":"Remove unused variable 'GameArea'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'GameFooter' is defined but never used. Allowed unused vars must match /^_/u.","line":7,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":7,"endColumn":18,"suggestions":[{"messageId":"removeVar","data":{"varName":"GameFooter"},"fix":{"range":[286,302],"text":""},"desc":"Remove unused variable 'GameFooter'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'SeletorDeFases' is defined but never used. Allowed unused vars must match /^_/u.","line":8,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":8,"endColumn":22,"suggestions":[{"messageId":"removeVar","data":{"varName":"SeletorDeFases"},"fix":{"range":[325,345],"text":""},"desc":"Remove unused variable 'SeletorDeFases'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'SucessoModal' is defined but never used. Allowed unused vars must match /^_/u.","line":9,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":9,"endColumn":20,"suggestions":[{"messageId":"removeVar","data":{"varName":"SucessoModal"},"fix":{"range":[372,390],"text":""},"desc":"Remove unused variable 'SucessoModal'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'FalhaModal' is defined but never used. Allowed unused vars must match /^_/u.","line":10,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":10,"endColumn":18,"suggestions":[{"messageId":"removeVar","data":{"varName":"FalhaModal"},"fix":{"range":[415,431],"text":""},"desc":"Remove unused variable 'FalhaModal'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'ResizeHandle' is defined but never used. Allowed unused vars must match /^_/u.","line":11,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":11,"endColumn":20,"suggestions":[{"messageId":"removeVar","data":{"varName":"ResizeHandle"},"fix":{"range":[454,472],"text":""},"desc":"Remove unused variable 'ResizeHandle'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'EditorProvider' is defined but never used. Allowed unused vars must match /^_/u.","line":14,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":14,"endColumn":24,"suggestions":[{"messageId":"removeVar","data":{"varName":"EditorProvider"},"fix":{"range":[622,684],"text":""},"desc":"Remove unused variable 'EditorProvider'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'GameBaseContent' is defined but never used. Allowed unused vars must match /^_/u.","line":16,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":16,"endColumn":25,"suggestions":[{"messageId":"removeVar","data":{"varName":"GameBaseContent"},"fix":{"range":[686,7736],"text":""},"desc":"Remove unused variable 'GameBaseContent'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'title' is defined but never used. Allowed unused args must match /^_/u.","line":271,"column":3,"nodeType":"Identifier","messageId":"unusedVar","endLine":271,"endColumn":8,"suggestions":[{"messageId":"removeVar","data":{"varName":"title"},"fix":{"range":[7800,7809],"text":""},"desc":"Remove unused variable 'title'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":13,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import React, { useEffect, useRef, useCallback, useState } from \"react\";\nimport Phaser from \"phaser\";\nimport { Panel, PanelGroup } from \"react-resizable-panels\";\nimport GameNavBar from \"./GameNavBar\";\nimport GameFaseInfo from \"./GameFaseInfo\";\nimport GameArea from \"./GameArea\";\nimport GameFooter from \"./GameFooter\";\nimport SeletorDeFases from \"./SeletorDeFases\";\nimport SucessoModal from \"./SucessoModal\";\nimport FalhaModal from \"./FalhaModal\";\nimport ResizeHandle from \"./ResizeHandle\";\nimport { useIsMobile } from \"../../hooks/useIsMobile\";\nimport { useGameState, GAME_STATES } from \"../../contexts/GameStateContext\";\nimport { EditorProvider } from \"../../contexts/EditorContext\";\n\nfunction GameBaseContent({\n gameFactory,\n gameConfig,\n children,\n onHelpClick,\n customFailureHandler,\n}) {\n const gameContainerRef = useRef(null);\n const gameInstanceRef = useRef(null);\n const isInitializingRef = useRef(false);\n const isMobile = useIsMobile();\n const [modalFasesAberto, setModalFasesAberto] = useState(false);\n const [modalSucessoAberto, setModalSucessoAberto] = useState(false);\n const [modalFalhaAberto, setModalFalhaAberto] = useState(false);\n const [blocosRestantesCount, setBlocosRestantesCount] = useState(null);\n\n const {\n currentPhase,\n setCurrentPhase,\n resetProgress,\n executionState,\n generatedCode,\n failureMessage,\n restart,\n setOnWorkspaceChange,\n } = useGameState();\n\n const phaseConfig = gameConfig.fases[currentPhase - 1];\n const usaModalFalha = !!customFailureHandler;\n\n useEffect(() => {\n if (gameInstanceRef.current && gameContainerRef.current) {\n const phaserScale = gameInstanceRef.current.scale;\n phaserScale.resize(\n gameContainerRef.current.clientWidth,\n gameContainerRef.current.clientHeight,\n );\n }\n }, [isMobile]);\n\n useEffect(() => {\n if (isInitializingRef.current) {\n return;\n }\n\n isInitializingRef.current = true;\n\n if (gameInstanceRef.current) {\n try {\n gameInstanceRef.current.destroy(true);\n } catch (error) {\n console.warn(\"Erro ao destruir Phaser:\", error);\n }\n gameInstanceRef.current = null;\n }\n\n // Aguardar um frame para garantir que o cleanup foi concluído\n const timeoutId = setTimeout(() => {\n try {\n if (!gameContainerRef.current) {\n console.warn(\"Container do jogo não disponível\");\n isInitializingRef.current = false;\n return;\n }\n\n const config = gameFactory(\n gameContainerRef.current,\n phaseConfig,\n customFailureHandler,\n currentPhase,\n gameConfig,\n );\n gameInstanceRef.current = new Phaser.Game(config);\n } catch (error) {\n console.error(\"Erro ao inicializar Phaser:\", error);\n } finally {\n isInitializingRef.current = false;\n }\n }, 10); // Pequeno delay para garantir cleanup\n\n return () => {\n clearTimeout(timeoutId);\n\n if (gameInstanceRef.current) {\n try {\n gameInstanceRef.current.destroy(true);\n } catch (error) {\n console.warn(\"Erro durante cleanup do Phaser:\", error);\n }\n gameInstanceRef.current = null;\n }\n\n isInitializingRef.current = false;\n };\n }, [gameFactory, currentPhase, customFailureHandler]);\n\n const handleWorkspaceChange = useCallback(\n (blockCount) => {\n if (phaseConfig.maxBlocks === Infinity) {\n setBlocosRestantesCount(null);\n return;\n }\n const blocosUsados = typeof blockCount === \"number\" ? blockCount : 0;\n const blocosRestantes = phaseConfig.maxBlocks - blocosUsados;\n setBlocosRestantesCount(blocosRestantes);\n },\n [phaseConfig.maxBlocks],\n );\n\n useEffect(() => {\n if (setOnWorkspaceChange) {\n setOnWorkspaceChange(() => handleWorkspaceChange);\n }\n return () => {\n if (setOnWorkspaceChange) {\n setOnWorkspaceChange(null);\n }\n };\n }, [setOnWorkspaceChange, handleWorkspaceChange]);\n\n const handleResetProgresso = () => {\n resetProgress();\n\n window.dispatchEvent(\n new CustomEvent(\"resetBlocklyWorkspace\", {\n detail: { gameId: gameConfig.gameId },\n }),\n );\n };\n\n useEffect(() => {\n if (executionState === GAME_STATES.SUCESSO) {\n setModalSucessoAberto(true);\n }\n\n if (executionState === GAME_STATES.FALHA && usaModalFalha) {\n setModalFalhaAberto(true);\n }\n }, [executionState, usaModalFalha]);\n\n const handleProximaFase = () => {\n const proximaFase = currentPhase + 1;\n if (proximaFase <= gameConfig.fases.length) {\n setCurrentPhase(proximaFase);\n }\n\n setModalSucessoAberto(false);\n restart();\n };\n\n const handleFecharModalSucesso = () => {\n setModalSucessoAberto(false);\n };\n\n const handleFecharModalFalha = () => {\n setModalFalhaAberto(false);\n };\n\n const handleTentarNovamente = () => {\n setModalFalhaAberto(false);\n restart();\n };\n\n const codigoParaExibir = React.useMemo(() => {\n if (!generatedCode) return \"Nenhum código gerado\";\n\n let codigo = \"\";\n\n if (typeof generatedCode === \"string\") {\n codigo = generatedCode;\n } else if (typeof generatedCode === \"object\" && generatedCode.codigo) {\n codigo = generatedCode.codigo;\n } else {\n return \"Código não disponível\";\n }\n\n const codigoLimpo = codigo\n .split(\"\\n\")\n .filter((linha) => !linha.trim().startsWith(\"highlightBlock(\"))\n .join(\"\\n\")\n .trim();\n\n return codigoLimpo || codigo;\n }, [generatedCode]);\n\n return (\n <div className=\"game-base-page flex flex-col h-screen w-screen\">\n <GameNavBar\n title={`${gameConfig.gameName}`}\n type={`${gameConfig.type}`}\n />\n <GameFaseInfo dadosFase={phaseConfig} numeroFase={currentPhase} />\n <div className=\"flex-1 min-h-0 flex flex-col\">\n <PanelGroup\n direction={isMobile ? \"vertical\" : \"horizontal\"}\n className=\"h-full w-full\"\n >\n <Panel defaultSize={isMobile ? 50 : 50} minSize={isMobile ? 10 : 10}>\n <EditorProvider gameConfig={gameConfig} faseAtual={currentPhase}>\n {children}\n </EditorProvider>\n </Panel>\n <ResizeHandle direction={isMobile ? \"vertical\" : \"horizontal\"} />\n <Panel defaultSize={isMobile ? 50 : 50} minSize={isMobile ? 10 : 10}>\n <GameArea blocosRestantes={blocosRestantesCount} faseId={currentPhase}>\n <div ref={gameContainerRef} className=\"w-full h-full\" />\n </GameArea>\n </Panel>\n </PanelGroup>\n </div>\n\n <GameFooter\n gameConfig={gameConfig}\n faseAtual={currentPhase}\n onAbrirSeletor={() => setModalFasesAberto(true)}\n onHelpClick={onHelpClick}\n />\n\n <SeletorDeFases\n isVisible={modalFasesAberto}\n onClose={() => setModalFasesAberto(false)}\n faseAtual={currentPhase}\n gameConfig={gameConfig}\n onMudarFase={(fase) => {\n setCurrentPhase(fase);\n setModalFasesAberto(false);\n }}\n onResetProgresso={handleResetProgresso}\n />\n\n <SucessoModal\n isOpen={modalSucessoAberto}\n onClose={handleFecharModalSucesso}\n onNextPhase={handleProximaFase}\n codigoGerado={codigoParaExibir}\n currentPhase={currentPhase}\n totalPhases={gameConfig.fases.length}\n canGoNext={currentPhase < gameConfig.fases.length}\n />\n\n <FalhaModal\n isOpen={modalFalhaAberto}\n onClose={handleFecharModalFalha}\n onRetry={handleTentarNovamente}\n mensagemCustomizada={failureMessage}\n currentPhase={currentPhase}\n codigoGerado={codigoParaExibir}\n />\n </div>\n );\n}\n\nexport default function GameBase({\n gameFactory,\n gameConfig,\n title,\n children,\n onHelpClick,\n customFailureHandler,\n}) {\n return (\n <GameBaseContent\n gameFactory={gameFactory}\n gameConfig={gameConfig}\n onHelpClick={onHelpClick}\n customFailureHandler={customFailureHandler}\n >\n {children}\n </GameBaseContent>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/components/game/GameEditor.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'Play' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":14,"suggestions":[{"messageId":"removeVar","data":{"varName":"Play"},"fix":{"range":[9,14],"text":""},"desc":"Remove unused variable 'Play'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Loader' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":16,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":22,"suggestions":[{"messageId":"removeVar","data":{"varName":"Loader"},"fix":{"range":[13,21],"text":""},"desc":"Remove unused variable 'Loader'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'RotateCcw' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":24,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":33,"suggestions":[{"messageId":"removeVar","data":{"varName":"RotateCcw"},"fix":{"range":[21,32],"text":""},"desc":"Remove unused variable 'RotateCcw'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'CircleAlert' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":35,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":46,"suggestions":[{"messageId":"removeVar","data":{"varName":"CircleAlert"},"fix":{"range":[32,45],"text":""},"desc":"Remove unused variable 'CircleAlert'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":4,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { Play, Loader, RotateCcw, CircleAlert } from \"lucide-react\";\nimport { useGameState, GAME_STATES } from \"../../contexts/GameStateContext\";\n\nexport default function GameEditor({\n children,\n textoExecutar = \"Executar\",\n textoReiniciar = \"Reiniciar\",\n}) {\n const {\n executionState,\n execute,\n restart,\n stop,\n currentBlockCount,\n editorType,\n } = useGameState();\n\n const isExecuting = executionState === GAME_STATES.EXECUTANDO;\n const needsRestart =\n executionState === GAME_STATES.SUCESSO ||\n executionState === GAME_STATES.FALHA;\n const noBlocks = currentBlockCount === 0;\n\n const handleClick = () => {\n if (noBlocks && !isExecuting && !needsRestart) {\n return; // Não faz nada se não há blocos\n }\n\n if (isExecuting) {\n stop();\n return;\n }\n\n if (needsRestart) {\n restart();\n } else {\n execute();\n }\n };\n\n const setStyle = () => {\n const style =\n \"game-controls-custom flex items-center space-x-2 px-6 py-3 rounded-full font-medium transition-all duration-200 shadow-md\";\n\n if (noBlocks) {\n return `${style} bg-gradient-to-r from-yellow-700 to-yellow-500 cursor-not-allowed text-white`;\n }\n\n if (isExecuting) {\n return `${style} bg-gradient-to-r from-blue-500 to-green-600 hover:from-blue-600 hover:to-green-700 text-white`;\n }\n\n if (needsRestart) {\n return `${style} bg-gradient-to-r from-orange-500 to-red-600 hover:from-orange-600 hover:to-red-700 text-white`;\n }\n\n return `${style} bg-gradient-to-r from-red-500 via-pink-500 to-purple-600 hover:from-red-600 hover:via-pink-600 hover:to-purple-700 text-white`;\n };\n\n const getEmptyStateText = () => {\n return editorType === \"code\"\n ? \"Adicione código para executar\"\n : \"Adicione blocos para executar\";\n };\n\n return (\n <div className=\"w-full h-full flex flex-col\">\n <div className=\"flex-1 min-h-0\">{children}</div>\n <div className=\"game-editor-controls py-1 flex flex-col items-center space-y-2\">\n <button\n onClick={handleClick}\n className={setStyle()}\n disabled={noBlocks && !isExecuting && !needsRestart}\n data-tour={needsRestart ? \"reset-button\" : \"run-button\"}\n >\n {isExecuting ? (\n <>\n <Loader className=\"w-4 h-4 animate-spin\" />\n <span>Executando, clique para interromper...</span>\n </>\n ) : needsRestart ? (\n <>\n <RotateCcw className=\"w-4 h-4\" />\n <span>{textoReiniciar}</span>\n </>\n ) : noBlocks ? (\n <>\n <CircleAlert className=\"w-4 h-4 opacity-50\" />\n <span>{getEmptyStateText()}</span>\n </>\n ) : (\n <>\n <Play className=\"w-4 h-4\" />\n <span>{textoExecutar}</span>\n </>\n )}\n </button>\n </div>\n </div>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/components/game/GameFaseInfo.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'React' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":13,"suggestions":[{"messageId":"removeVar","data":{"varName":"React"},"fix":{"range":[7,18],"text":""},"desc":"Remove unused variable 'React'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import React from \"react\";\n\nfunction obterDificuldade(dadosFase) {\n // Usa a dificuldade da fase, se existir, senão calcula pelo número\n if (dadosFase?.dificuldade) {\n switch (dadosFase.dificuldade) {\n case \"Fácil\":\n return { nivel: \"Fácil\", cor: \"bg-green-500\", emoji: \"😊\" };\n case \"Médio\":\n return { nivel: \"Médio\", cor: \"bg-yellow-500\", emoji: \"🤔\" };\n case \"Difícil\":\n return { nivel: \"Difícil\", cor: \"bg-orange-500\", emoji: \"😤\" };\n case \"Extremo\":\n return { nivel: \"Extremo\", cor: \"bg-red-500\", emoji: \"🔥\" };\n default:\n return null;\n }\n }\n return null;\n}\n\nfunction GameFaseInfo({ dadosFase = {}, numeroFase }) {\n const dificuldade = obterDificuldade(dadosFase);\n\n return (\n <div className=\"game-fase-info p-1 lg:px-3 lg:py-2\">\n {dadosFase && dadosFase.nome ? (\n <div className=\"flex items-center justify-between\">\n {/* Número da fase */}\n <div className=\"flex-shrink-0 w-8 h-8 lg:w-12 lg:h-12 bg-gradient-to-br from-red-500 via-pink-500 to-purple-600 rounded-lg flex items-center justify-center text-white text-sm lg:text-lg font-bold shadow-lg\">\n {numeroFase}\n </div>\n {/* Título/Subtítulo */}\n <div className=\"flex-1 min-w-0 px-3 lg:px-5\">\n <h3 className=\"text-base lg:text-2xl font-semibold text-gray-800 truncate\">\n {dadosFase.nome}\n </h3>\n {dadosFase.descricao && (\n <p className=\"text-sm lg:text-xl text-gray-600 leading-tight whitespace-pre-wrap mt-1 pr-12 lg:pr-0\">\n {dadosFase.descricao}\n </p>\n )}\n </div>\n {/* Dificuldade */}\n <div className=\"flex-shrink-0\">\n {dificuldade && (\n <div\n className={`flex items-center space-x-1 text-xs lg:text-sm text-white px-2 py-1 lg:px-3 lg:py-1.5 rounded-full font-medium ${dificuldade.cor}`}\n >\n <span className=\"lg:text-base\">{dificuldade.emoji}</span>\n <span>{dificuldade.nivel}</span>\n </div>\n )}\n </div>\n </div>\n ) : (\n <div className=\"flex items-center justify-between text-gray-500\">\n <div className=\"w-8 h-8 lg:w-12 lg:h-12 bg-gray-200 rounded-lg flex items-center justify-center text-gray-600 text-sm lg:text-lg font-bold\">\n ?\n </div>\n <div className=\"flex-1 px-3 lg:px-5\">\n <p className=\"text-xs lg:text-sm\">\n Selecione uma fase para começar\n </p>\n </div>\n <div className=\"flex-shrink-0\"></div>\n </div>\n )}\n </div>\n );\n}\n\nexport default GameFaseInfo;\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/components/game/GameFooter.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'React' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":13,"suggestions":[{"messageId":"removeVar","data":{"varName":"React"},"fix":{"range":[7,18],"text":""},"desc":"Remove unused variable 'React'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import React from \"react\";\n\nexport default function GameFooter({\n gameConfig,\n faseAtual,\n onAbrirSeletor,\n onHelpClick,\n}) {\n const totalFases = gameConfig.fases.length;\n\n const ajuda = () => {\n if (onHelpClick) {\n onHelpClick();\n } else {\n alert(\"Recurso de ajuda será implementado em breve!\");\n }\n };\n\n return (\n <div className=\"bg-gradient-to-r from-red-600 via-pink-600 to-purple-600 border-t border-white/20 border-b-0\">\n <div className=\"flex items-center justify-between px-6 py-3\">\n {/* Lado esquerdo - Botão de Ajuda */}\n <div className=\"flex items-center\">\n <button\n onClick={ajuda}\n data-tour=\"help-button\"\n title=\"Ajuda\"\n className=\"!bg-white !text-gray-800 font-medium py-2 px-6 lg:py-3 lg:px-9 lg:text-lg rounded-full transition-all duration-200 hover:!bg-gray-900 hover:!text-white hover:scale-105 hover:shadow-lg focus:outline-none focus:ring-2 focus:ring-white focus:ring-opacity-50\"\n >\n Ajuda\n </button>\n </div>\n {/* Centro - Indicador de Fase Atual/Total */}\n <div className=\"flex items-center space-x-4\">\n <div className=\"phase-indicator\">\n <span className=\"text-white font-medium text-base lg:text-2xl\">\n <span className=\"phase-current\">{faseAtual}</span>\n <span className=\"phase-separator\">/</span>\n <span className=\"phase-total\">{totalFases}</span>\n </span>\n </div>\n </div>\n {/* Lado direito - Botão do Seletor de Fases */}\n <div className=\"flex items-center\">\n <button\n onClick={onAbrirSeletor}\n data-tour=\"phase-selector\"\n title=\"Selecionar Fase\"\n className=\"!bg-white !text-gray-800 font-medium py-2 px-6 lg:py-3 lg:px-9 lg:text-lg rounded-full transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-white focus:ring-opacity-50 hover:!bg-gray-900 hover:!text-white hover:scale-105 hover:shadow-lg\"\n >\n Fases\n </button>\n </div>\n </div>\n </div>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/components/game/GameNavBar.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'Code' is defined but never used. Allowed unused vars must match /^_/u.","line":4,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":4,"endColumn":14,"suggestions":[{"messageId":"removeVar","data":{"varName":"Code"},"fix":{"range":[140,145],"text":""},"desc":"Remove unused variable 'Code'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Puzzle' is defined but never used. Allowed unused vars must match /^_/u.","line":4,"column":16,"nodeType":"Identifier","messageId":"unusedVar","endLine":4,"endColumn":22,"suggestions":[{"messageId":"removeVar","data":{"varName":"Puzzle"},"fix":{"range":[144,152],"text":""},"desc":"Remove unused variable 'Puzzle'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'ArrowLeft' is defined but never used. Allowed unused vars must match /^_/u.","line":5,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":5,"endColumn":19,"suggestions":[{"messageId":"removeVar","data":{"varName":"ArrowLeft"},"fix":{"range":[176,217],"text":""},"desc":"Remove unused variable 'ArrowLeft'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":3,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { useState } from \"react\";\nimport { useNavigate } from \"react-router-dom\";\nimport logo from \"../../assets/logo_decoda.svg\";\nimport { Code, Puzzle } from \"lucide-react\";\nimport { ArrowLeft } from \"lucide-react\";\n\nexport default function GameNavBar({ title, type = \"blocks\" }) {\n const navigate = useNavigate();\n const [menuOpen, setMenuOpen] = useState(false);\n\n const renderIcon = (size = \"w-6 h-6 lg:w-8 lg:h-8\") => {\n if (type === \"code\") {\n return <Code className={`text-white ${size}`} />;\n } else {\n return <Puzzle className={`text-white ${size}`} />;\n }\n };\n\n return (\n <>\n {/* Navbar normal - oculto em telas pequenas */}\n <nav className=\"bg-white sticky top-0 z-50 border-b border-white/20 shadow-lg hidden sm:block\">\n <div className=\"flex items-center justify-between px-5 py-2 lg:py-3 lg:px-10\">\n <div className=\"flex items-center space-x-4\">\n <div\n onClick={() => navigate(\"/\")}\n title=\"Ir para Home\"\n className=\"header-logo group cursor-pointer flex items-center space-x-2\"\n >\n <img\n src={logo}\n alt=\"Decoda Logo\"\n className=\"h-[30px] transition-transform duration-200 group-hover:scale-110\"\n />\n </div>\n </div>\n <div className=\"flex-1 flex justify-center\"></div>\n <div className=\"flex items-center space-x-2\">\n <div className=\"flex items-center space-x-3\">\n {renderIcon(\"w-8 h-8\")}\n <h5 className=\"text-brand-500 font-semibold text-3xl\">{title}</h5>\n </div>\n </div>\n </div>\n </nav>\n\n {/* Botão menu flutuante - só aparece em telas pequenas */}\n <button\n className=\"fixed top-4 right-4 z-50 sm:hidden bg-white/80 rounded-full p-2 shadow-lg\"\n aria-label=\"Abrir menu\"\n onClick={() => setMenuOpen(true)}\n >\n <svg width=\"32\" height=\"32\" fill=\"none\" viewBox=\"0 0 24 24\">\n <rect x=\"4\" y=\"7\" width=\"16\" height=\"2\" rx=\"1\" fill=\"#a21caf\" />\n <rect x=\"4\" y=\"11\" width=\"16\" height=\"2\" rx=\"1\" fill=\"#a21caf\" />\n <rect x=\"4\" y=\"15\" width=\"16\" height=\"2\" rx=\"1\" fill=\"#a21caf\" />\n </svg>\n </button>\n\n {/* Overlay do menu mobile */}\n {menuOpen && (\n <div className=\"fixed inset-0 z-50 bg-black bg-opacity-70 flex flex-col items-end \">\n <div className=\"bg-gray-100 h-full p-5 w-[60%] flex flex-col justify-between\">\n <div>\n <div className=\"w-full flex justify-end\">\n <button\n onClick={() => setMenuOpen(false)}\n className=\"text-brand-500 text-3xl\"\n aria-label=\"Fechar menu\"\n >\n ×\n </button>\n </div>\n <div className=\"flex items-center space-x-3\">\n {type === \"code\" && <Code className=\"text-brand-500 size-6\" />}\n {type === \"blocks\" && (\n <Puzzle className=\"text-brand-500 size-5\" />\n )}\n <h5 className=\"text-bold font-semibold text-2xl\">{title}</h5>\n </div>\n <button\n onClick={() => {\n setMenuOpen(false);\n navigate(\"/\");\n }}\n className=\"flex items-center gap-5 mt-10\"\n >\n <ArrowLeft className=\"text-brand-500\" />\n <span className=\"text-black font-bold text-base\">Voltar</span>\n </button>\n </div>\n <button\n onClick={() => {\n setMenuOpen(false);\n navigate(\"/\");\n }}\n >\n <img\n src={logo}\n alt=\"Decoda\"\n className=\"h-[83px]\"\n onError={(e) => {\n e.target.style.display = \"none\";\n e.target.nextSibling.style.display = \"inline\";\n }}\n />\n </button>\n </div>\n </div>\n )}\n </>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/components/game/OptionsEditor.jsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/components/game/ResizeHandle.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'PanelResizeHandle' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":27,"suggestions":[{"messageId":"removeVar","data":{"varName":"PanelResizeHandle"},"fix":{"range":[0,59],"text":""},"desc":"Remove unused variable 'PanelResizeHandle'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { PanelResizeHandle } from \"react-resizable-panels\";\n\nexport default function ResizeHandle({\n direction = \"horizontal\",\n theme = \"light\",\n disabled = false,\n}) {\n const isHorizontal = direction === \"horizontal\";\n const isDark = theme === \"dark\";\n\n return (\n <PanelResizeHandle\n disabled={disabled}\n className={`\n group\n ${isHorizontal ? \"w-3\" : \"h-3\"}\n ${disabled ? \"cursor-default\" : isHorizontal ? \"cursor-col-resize\" : \"cursor-row-resize\"}\n flex items-center justify-center\n ${isDark ? \"bg-gray-700 hover:bg-gray-600\" : \"bg-gray-200 hover:bg-gray-300\"}\n transition-colors\n `}\n >\n <div\n className={`\n flex ${isHorizontal ? \"flex-col space-y-1\" : \"flex-row space-x-1\"}\n items-center justify-center\n `}\n >\n <span\n className={`block w-1 h-1 rounded-full ${isDark ? \"bg-gray-400 group-hover:bg-gray-300\" : \"bg-gray-500 group-hover:bg-gray-700\"}`}\n />\n <span\n className={`block w-1 h-1 rounded-full ${isDark ? \"bg-gray-400 group-hover:bg-gray-300\" : \"bg-gray-500 group-hover:bg-gray-700\"}`}\n />\n <span\n className={`block w-1 h-1 rounded-full ${isDark ? \"bg-gray-400 group-hover:bg-gray-300\" : \"bg-gray-500 group-hover:bg-gray-700\"}`}\n />\n </div>\n </PanelResizeHandle>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/components/game/SeletorDeFases.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'React' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":13,"suggestions":[{"messageId":"removeVar","data":{"varName":"React"},"fix":{"range":[7,13],"text":""},"desc":"Remove unused variable 'React'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'ConfirmacaoModal' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":24,"suggestions":[{"messageId":"removeVar","data":{"varName":"ConfirmacaoModal"},"fix":{"range":[59,81],"text":""},"desc":"Remove unused variable 'ConfirmacaoModal'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Lock' is defined but never used. Allowed unused vars must match /^_/u.","line":3,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":3,"endColumn":14,"suggestions":[{"messageId":"removeVar","data":{"varName":"Lock"},"fix":{"range":[112,117],"text":""},"desc":"Remove unused variable 'Lock'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'CheckCircle' is defined but never used. Allowed unused vars must match /^_/u.","line":3,"column":16,"nodeType":"Identifier","messageId":"unusedVar","endLine":3,"endColumn":27,"suggestions":[{"messageId":"removeVar","data":{"varName":"CheckCircle"},"fix":{"range":[116,129],"text":""},"desc":"Remove unused variable 'CheckCircle'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Star' is defined but never used. Allowed unused vars must match /^_/u.","line":3,"column":29,"nodeType":"Identifier","messageId":"unusedVar","endLine":3,"endColumn":33,"suggestions":[{"messageId":"removeVar","data":{"varName":"Star"},"fix":{"range":[129,135],"text":""},"desc":"Remove unused variable 'Star'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'X' is defined but never used. Allowed unused vars must match /^_/u.","line":3,"column":35,"nodeType":"Identifier","messageId":"unusedVar","endLine":3,"endColumn":36,"suggestions":[{"messageId":"removeVar","data":{"varName":"X"},"fix":{"range":[135,138],"text":""},"desc":"Remove unused variable 'X'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":6,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import React, { useEffect, useState } from \"react\";\nimport ConfirmacaoModal from \"./ConfirmacaoModal\";\nimport { Lock, CheckCircle, Star, X } from \"lucide-react\";\n\nexport default function SeletorDeFases({\n isVisible,\n onClose,\n gameConfig,\n faseAtual,\n onMudarFase,\n onResetProgresso,\n}) {\n const gameId = gameConfig.gameId;\n const totalFases = gameConfig.fases.length;\n const storageKey = `${gameId}-fases-concluidas`;\n const [fasesCompletadas, setFasesCompletadas] = useState([]);\n const [mostrarConfirmacaoReset, setMostrarConfirmacaoReset] = useState(false);\n\n useEffect(() => {\n if (!isVisible) return;\n const salvo = localStorage.getItem(storageKey);\n if (salvo) {\n try {\n const fases = JSON.parse(salvo);\n setFasesCompletadas(fases);\n } catch {\n setFasesCompletadas([]);\n }\n } else {\n setFasesCompletadas([]);\n }\n }, [isVisible, storageKey]);\n\n const fasesLiberadas = (() => {\n if (fasesCompletadas.length === 0) return [1];\n const maxCompleta = Math.max(...fasesCompletadas);\n return Array.from(\n { length: Math.min(maxCompleta + 1, totalFases) },\n (_, i) => i + 1,\n );\n })();\n\n const selecionarFase = (numero) => {\n if (!fasesLiberadas.includes(numero)) return;\n onMudarFase(numero);\n onClose();\n };\n\n if (!isVisible) return null;\n\n return (\n <div\n className=\"fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center p-4 z-50\"\n onClick={onClose}\n >\n <div\n className=\"bg-white/95 backdrop-blur-sm border border-white/30 rounded-xl shadow-xl max-w-4xl w-full max-h-[90vh] overflow-hidden flex flex-col\"\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"flex items-center justify-between p-6 border-b border-white/20\">\n <div className=\"flex items-center space-x-3\">\n <div className=\"w-8 h-8 bg-gradient-to-br from-purple-400 to-pink-500 rounded-lg flex items-center justify-center text-white\">\n <Star className=\"w-5 h-5\" />\n </div>\n <h3 className=\"text-xl font-bold text-gray-800\">\n Selecionar Fase - {gameConfig.gameName || \"Jogo\"}\n </h3>\n </div>\n <button\n onClick={onClose}\n className=\"w-8 h-8 rounded-lg bg-gray-200 hover:bg-gray-300 flex items-center justify-center text-gray-600 transition-colors\"\n >\n <X className=\"w-4 h-4\" />\n </button>\n </div>\n\n <div className=\"flex-1 overflow-y-auto p-6\">\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4\">\n {Array.from({ length: totalFases }, (_, i) => {\n const numeroFase = i + 1;\n const estaLiberada = fasesLiberadas.includes(numeroFase);\n const foiCompletada = fasesCompletadas.includes(numeroFase);\n const ehAtual = faseAtual === numeroFase;\n const dadosFase = gameConfig.fases[i];\n\n return (\n <div\n key={numeroFase}\n className={`bg-white/80 backdrop-blur-sm border border-white/30 rounded-xl shadow-lg p-4 cursor-pointer transition-all duration-200 border-2 ${\n ehAtual\n ? \"border-blue-400 ring-2 ring-blue-200\"\n : estaLiberada\n ? \"border-transparent hover:border-blue-300\"\n : \"border-gray-200 opacity-60 cursor-not-allowed\"\n }`}\n onClick={() => estaLiberada && selecionarFase(numeroFase)}\n >\n <div className=\"flex items-center justify-between mb-3\">\n <div className=\"flex items-center space-x-2\">\n <div\n className={`w-8 h-8 rounded-lg flex items-center justify-center text-sm font-bold ${\n foiCompletada\n ? \"bg-green-100 text-green-600\"\n : !estaLiberada\n ? \"bg-gray-100 text-gray-400\"\n : \"bg-blue-100 text-blue-600\"\n }`}\n >\n {foiCompletada ? (\n <CheckCircle className=\"w-4 h-4\" />\n ) : !estaLiberada ? (\n <Lock className=\"w-4 h-4\" />\n ) : (\n numeroFase\n )}\n </div>\n </div>\n {ehAtual && (\n <span className=\"text-xs bg-blue-500 text-white px-2 py-1 rounded-full font-medium\">\n Atual\n </span>\n )}\n </div>\n\n <div className=\"space-y-2\">\n <h4 className=\"font-semibold text-gray-800 text-sm\">\n Fase {numeroFase}\n </h4>\n <h5 className=\"font-medium text-gray-700 text-sm\">\n {dadosFase.nome}\n </h5>\n <p className=\"text-xs text-gray-600 line-clamp-2\">\n {dadosFase.descricao}\n </p>\n <span className=\"inline-block mt-1 px-2 py-0.5 rounded-full text-xs font-medium bg-gray-200 text-gray-700\">\n {dadosFase.dificuldade}\n </span>\n </div>\n </div>\n );\n })}\n </div>\n </div>\n\n <div className=\"p-4 border-t border-white/20 flex justify-center\">\n <button\n className=\"bg-red-600 hover:bg-red-700 text-white px-6 py-2 rounded-lg font-semibold shadow transition-all duration-200\"\n onClick={() => setMostrarConfirmacaoReset(true)}\n >\n Resetar TODO o progresso do jogo\n </button>\n </div>\n\n <ConfirmacaoModal\n isVisible={mostrarConfirmacaoReset}\n onClose={() => setMostrarConfirmacaoReset(false)}\n onConfirm={() => {\n onResetProgresso();\n setMostrarConfirmacaoReset(false);\n onClose();\n }}\n titulo=\"Resetar progresso\"\n mensagem=\"Tem certeza que deseja apagar TODO o progresso e blocos salvos deste jogo? Esta ação não pode ser desfeita.\"\n />\n </div>\n </div>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/components/game/SucessoModal.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'React' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":13,"suggestions":[{"messageId":"removeVar","data":{"varName":"React"},"fix":{"range":[7,18],"text":""},"desc":"Remove unused variable 'React'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'ChevronRight' is defined but never used. Allowed unused vars must match /^_/u.","line":3,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":3,"endColumn":22,"suggestions":[{"messageId":"removeVar","data":{"varName":"ChevronRight"},"fix":{"range":[63,107],"text":""},"desc":"Remove unused variable 'ChevronRight'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'ModalBase' is defined but never used. Allowed unused vars must match /^_/u.","line":5,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":5,"endColumn":19,"suggestions":[{"messageId":"removeVar","data":{"varName":"ModalBase"},"fix":{"range":[109,155],"text":""},"desc":"Remove unused variable 'ModalBase'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'ModalHeader' is defined but never used. Allowed unused vars must match /^_/u.","line":6,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":6,"endColumn":21,"suggestions":[{"messageId":"removeVar","data":{"varName":"ModalHeader"},"fix":{"range":[156,206],"text":""},"desc":"Remove unused variable 'ModalHeader'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'CodeArea' is defined but never used. Allowed unused vars must match /^_/u.","line":7,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":7,"endColumn":18,"suggestions":[{"messageId":"removeVar","data":{"varName":"CodeArea"},"fix":{"range":[207,251],"text":""},"desc":"Remove unused variable 'CodeArea'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'FeedbackBox' is defined but never used. Allowed unused vars must match /^_/u.","line":8,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":8,"endColumn":21,"suggestions":[{"messageId":"removeVar","data":{"varName":"FeedbackBox"},"fix":{"range":[252,302],"text":""},"desc":"Remove unused variable 'FeedbackBox'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":6,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import React from \"react\";\nimport PropTypes from \"prop-types\";\nimport { ChevronRight } from \"lucide-react\";\n\nimport { ModalBase } from \"./modal/ModalBase\";\nimport { ModalHeader } from \"./modal/ModalHeader\";\nimport { CodeArea } from \"./modal/CodeArea\";\nimport { FeedbackBox } from \"./modal/FeedbackBox\";\n\nconst SucessoModal = ({\n isOpen,\n onClose,\n onNextPhase,\n codigoGerado,\n canGoNext,\n}) => {\n const handleNextPhase = () => {\n onClose();\n if (onNextPhase) {\n onNextPhase();\n }\n };\n\n return (\n <ModalBase isOpen={isOpen} onClose={onClose}>\n <ModalHeader\n title=\"Parabéns!\"\n subTitle=\"Veja o código que foi gerado\"\n variant=\"success\"\n onClose={onClose}\n />\n\n <div className=\"flex-1 overflow-hidden flex flex-col\">\n <div className=\"p-6 flex-1 overflow-auto\">\n <CodeArea\n code={\n typeof codigoGerado === \"string\"\n ? codigoGerado\n : codigoGerado?.codigo\n }\n variant=\"success\"\n />\n\n <FeedbackBox title=\"O que aconteceu?\" variant=\"success\">\n <p>\n Os blocos que você conectou foram convertidos em código JavaScript\n real. Este é o mesmo tipo de código que os programadores usam para\n criar aplicações!\n </p>\n </FeedbackBox>\n </div>\n </div>\n\n {/* 3. Rodapé (Botões de Ação) */}\n <div className=\"p-6 border-t border-gray-200 flex justify-between items-center bg-gray-50\">\n <button\n onClick={onClose}\n className=\"px-4 py-2 bg-blue-100 hover:bg-blue-200 text-blue-700 rounded-full font-medium transition-colors\"\n >\n Fechar\n </button>\n\n {canGoNext && (\n <button\n onClick={handleNextPhase}\n className=\"flex items-center space-x-2 px-6 py-3 rounded-full font-medium transition-all duration-200 shadow-md bg-gradient-to-r from-red-500 via-pink-500 to-purple-600 hover:from-red-600 hover:via-pink-600 hover:to-purple-700 hover:scale-105 hover:shadow-lg text-white\"\n >\n <span>Próxima Fase</span>\n <ChevronRight className=\"w-4 h-4\" />\n </button>\n )}\n </div>\n </ModalBase>\n );\n};\n\nSucessoModal.propTypes = {\n isOpen: PropTypes.bool.isRequired,\n onClose: PropTypes.func.isRequired,\n onNextPhase: PropTypes.func,\n codigoGerado: PropTypes.any,\n canGoNext: PropTypes.bool.isRequired,\n};\n\nexport default SucessoModal;\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/components/game/editors/BlocklyEditor.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'Hammer' is defined but never used. Allowed unused vars must match /^_/u.","line":21,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":21,"endColumn":16,"suggestions":[{"messageId":"removeVar","data":{"varName":"Hammer"},"fix":{"range":[617,655],"text":""},"desc":"Remove unused variable 'Hammer'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'LoadingSpinner' is assigned a value but never used. Allowed unused vars must match /^_/u.","line":25,"column":7,"nodeType":"Identifier","messageId":"unusedVar","endLine":25,"endColumn":21,"suggestions":[{"messageId":"removeVar","data":{"varName":"LoadingSpinner"},"fix":{"range":[722,873],"text":""},"desc":"Remove unused variable 'LoadingSpinner'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'error' is defined but never used.","line":52,"column":16,"nodeType":"Identifier","messageId":"unusedVar","endLine":52,"endColumn":21},{"ruleId":"no-unused-vars","severity":1,"message":"'currentBlockCount' is assigned a value but never used. Allowed unused vars must match /^_/u.","line":78,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":78,"endColumn":27,"suggestions":[{"messageId":"removeVar","data":{"varName":"currentBlockCount"},"fix":{"range":[2390,2407],"text":""},"desc":"Remove unused variable 'currentBlockCount'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":4,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import React, {\n useEffect,\n useRef,\n forwardRef,\n useImperativeHandle,\n useState,\n useCallback,\n useMemo,\n} from \"react\";\nimport * as Blockly from \"blockly/core\";\nimport Theme from \"@blockly/theme-modern\";\nimport { javascriptGenerator } from \"blockly/javascript\";\nimport { useGameState } from \"../../../contexts/GameStateContext\";\nimport { validateBlocklyWorkspace } from \"../../../utils/blocklyValidation\";\nimport { useEditor } from \"../../../contexts/EditorContext\";\nimport {\n loadWorkspace,\n createDebouncedSave,\n} from \"../../../services/blockstorage\";\nimport { getCategoryIcon } from \"./toolboxIcons\";\nimport { Hammer } from \"lucide-react\";\nimport \"./custom_category\";\nimport \"./BlocklyEditor.mobile.css\";\n\nconst LoadingSpinner = () => (\n <div className=\"blockly-loading-spinner\">\n <p className=\"blockly-loading-text\">Carregando Editor...</p>\n </div>\n);\n\nconst BlocklyEditor = forwardRef(function BlocklyEditor(\n { toolboxGenerator, debugSolutions = null, starterBlocks = null },\n ref,\n) {\n const { registerExecutionFunction, onWorkspaceChange } = useGameState();\n const { gameConfig, faseAtual, editorData, updateEditorData, gameNameKey } =\n useEditor();\n\n const hasSolution = debugSolutions && debugSolutions[faseAtual];\n\n const handleLoadSolution = () => {\n if (!workspaceRef.current) {\n alert(\"Editor não está pronto\");\n return;\n }\n\n if (window.confirm(\"Deseja carregar a solução desta fase?\")) {\n try {\n const solution = debugSolutions[faseAtual];\n workspaceRef.current.clear();\n Blockly.serialization.workspaces.load(solution, workspaceRef.current);\n } catch (error) {\n alert(\"Erro ao carregar a solução\");\n }\n }\n };\n\n useEffect(() => {\n if (!gameConfig || !toolboxGenerator) return;\n\n const currentPhase = gameConfig.fases[faseAtual - 1];\n const toolbox = toolboxGenerator(currentPhase.allowedBlocks);\n const max = currentPhase.maxBlocks;\n\n updateEditorData({\n toolboxJson: toolbox,\n maxBlocks: max,\n });\n }, [gameConfig, faseAtual, toolboxGenerator, updateEditorData]);\n\n const { toolboxJson, maxBlocks } = editorData;\n\n const blocklyDiv = useRef(null);\n const workspaceRef = useRef(null);\n const [initialJson, setInitialJson] = useState(undefined);\n const [isLoading, setIsLoading] = useState(true);\n const isInitializedRef = useRef(false);\n const [currentBlockCount, setCurrentBlockCount] = useState(0);\n\n const debouncedSave = useMemo(() => createDebouncedSave(1000), []);\n const stableToolboxJson = useMemo(() => toolboxJson, [toolboxJson]);\n\n const updateCategoriesState = useCallback((limitReached) => {\n if (!workspaceRef.current) return;\n\n const toolbox = workspaceRef.current.getToolbox();\n if (!toolbox) return;\n\n const categories = toolbox.getToolboxItems();\n\n categories.forEach((category) => {\n category.setDisabled(limitReached);\n });\n }, []);\n\n const workspaceChange = useCallback(() => {\n if (!workspaceRef.current) return;\n const blockCount = workspaceRef.current.getAllBlocks().length;\n setCurrentBlockCount(blockCount);\n\n const limitReached =\n maxBlocks !== undefined &&\n maxBlocks !== Infinity &&\n maxBlocks <= blockCount;\n updateCategoriesState(limitReached);\n\n if (onWorkspaceChange) {\n onWorkspaceChange(blockCount);\n }\n }, [onWorkspaceChange, maxBlocks, updateCategoriesState]);\n\n useEffect(() => {\n updateCategoriesState();\n }, [updateCategoriesState]);\n\n useEffect(() => {\n const generateAndValidateCode = () => {\n if (!workspaceRef.current) {\n return { codigo: null, workspace: null };\n }\n const validation = validateBlocklyWorkspace(workspaceRef.current, {\n allowMultipleTopBlocks: false,\n preferredStartBlocks: [\"start\", \"when_run\", \"main\"],\n });\n if (!validation.isValid) {\n return { codigo: null, workspace: null };\n }\n const codigo = javascriptGenerator.workspaceToCode(workspaceRef.current);\n return { codigo, workspace: workspaceRef.current };\n };\n registerExecutionFunction(generateAndValidateCode);\n return () => {\n registerExecutionFunction(null);\n };\n }, [registerExecutionFunction]);\n\n useEffect(() => {\n setIsLoading(true);\n const loadedData = loadWorkspace(gameNameKey);\n setInitialJson(loadedData);\n setIsLoading(false);\n }, [gameNameKey]);\n\n useImperativeHandle(ref, () => workspaceRef.current, []);\n\n useEffect(() => {\n if (\n isLoading ||\n isInitializedRef.current ||\n !blocklyDiv.current ||\n !stableToolboxJson\n ) {\n return;\n }\n isInitializedRef.current = true;\n\n const toolboxWithIcons = {\n ...stableToolboxJson,\n contents: stableToolboxJson.contents.map((cat) => ({\n ...cat,\n \"css-icon\": getCategoryIcon(cat.name),\n })),\n };\n\n workspaceRef.current = Blockly.inject(blocklyDiv.current, {\n toolbox: toolboxWithIcons,\n trashcan: true,\n scrollbars: true,\n renderer: \"zelos\",\n theme: Theme,\n grid: { spacing: 25, length: 3, colour: \"#ccc\", snap: true },\n zoom: { controls: false, wheel: true, startScale: 0.7 },\n });\n\n // Criar variáveis pré-definidas para o jogo Cripto\n if (gameConfig?.gameId === \"cripto\") {\n [\"entrada\", \"saida\", \"pos\"].forEach((varName) => {\n const variableMap = workspaceRef.current.getVariableMap();\n if (!variableMap.getVariable(varName)) {\n workspaceRef.current.createVariable(varName);\n }\n });\n }\n\n // Carregar workspace: prioridade = localStorage > starterBlocks > vazio\n if (initialJson) {\n try {\n Blockly.serialization.workspaces.load(\n initialJson,\n workspaceRef.current,\n );\n } catch (error) {\n console.error(\n \"Falha ao carregar workspace do localStorage, limpando.\",\n error,\n );\n workspaceRef.current.clear();\n }\n } else if (starterBlocks && starterBlocks[faseAtual]) {\n // Se não há nada salvo, carregar blocos iniciais da fase\n try {\n console.log(`Carregando blocos iniciais para fase ${faseAtual}`);\n Blockly.serialization.workspaces.load(\n starterBlocks[faseAtual],\n workspaceRef.current,\n );\n } catch (error) {\n console.error(\"Falha ao carregar blocos iniciais.\", error);\n }\n }\n\n workspaceChange();\n\n const listener = (event) => {\n if (!workspaceRef.current || event.isUiEvent) {\n return;\n }\n\n if (event.type === Blockly.Events.BLOCK_CREATE) {\n const currentCount = workspaceRef.current.getAllBlocks().length;\n if (\n maxBlocks !== undefined &&\n maxBlocks !== Infinity &&\n currentCount > maxBlocks\n ) {\n const blockToRemove = workspaceRef.current.getBlockById(\n event.blockId,\n );\n if (blockToRemove) {\n console.warn(\n `Limite de blocos atingido (${maxBlocks}). Removendo bloco extra: ${event.blockId}`,\n );\n\n Blockly.Events.disable();\n blockToRemove.dispose(false);\n Blockly.Events.enable();\n return;\n }\n }\n }\n\n workspaceChange();\n const currentState = Blockly.serialization.workspaces.save(\n workspaceRef.current,\n );\n debouncedSave(gameNameKey, currentState);\n };\n\n workspaceRef.current.addChangeListener(listener);\n\n const observer = new ResizeObserver(() => {\n if (workspaceRef.current) {\n Blockly.svgResize(workspaceRef.current);\n }\n });\n observer.observe(blocklyDiv.current);\n\n setTimeout(() => {\n updateCategoriesState();\n const bg = document.querySelector(\".blocklyToolboxBackground\");\n if (bg) {\n bg.setAttribute(\"fill\", \"none\");\n bg.setAttribute(\"stroke\", \"none\");\n }\n }, 100);\n\n return () => {\n debouncedSave.cancel();\n if (workspaceRef.current) {\n workspaceRef.current.removeChangeListener(listener);\n try {\n workspaceRef.current.dispose();\n } catch (error) {\n console.warn(\"Erro ao fazer dispose do workspace:\", error);\n }\n workspaceRef.current = null;\n }\n observer.disconnect();\n isInitializedRef.current = false;\n };\n }, [\n isLoading,\n initialJson,\n gameNameKey,\n stableToolboxJson,\n debouncedSave,\n workspaceChange,\n maxBlocks,\n updateCategoriesState,\n ]);\n\n if (isLoading || !toolboxJson) {\n return <LoadingSpinner />;\n }\n\n return (\n <div className=\"relative w-full h-full\">\n {hasSolution && (\n <button\n onClick={handleLoadSolution}\n className=\"blockly-debug-solution-btn\"\n title=\"Carregar solução (Debug)\"\n >\n <Hammer className=\"w-5 h-5 text-yellow-500\" />\n </button>\n )}\n <div ref={blocklyDiv} className=\"w-full h-full\" />\n </div>\n );\n});\n\nexport default React.memo(BlocklyEditor);\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/components/game/editors/CodeEditor.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'React' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":13,"suggestions":[{"messageId":"removeVar","data":{"varName":"React"},"fix":{"range":[7,13],"text":""},"desc":"Remove unused variable 'React'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'CodeMirror' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":18,"suggestions":[{"messageId":"removeVar","data":{"varName":"CodeMirror"},"fix":{"range":[68,84],"text":""},"desc":"Remove unused variable 'CodeMirror'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'insertTab' is defined but never used. Allowed unused vars must match /^_/u.","line":5,"column":25,"nodeType":"Identifier","messageId":"unusedVar","endLine":5,"endColumn":34,"suggestions":[{"messageId":"removeVar","data":{"varName":"insertTab"},"fix":{"range":[248,259],"text":""},"desc":"Remove unused variable 'insertTab'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":3,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import React, { useState, useEffect, useMemo } from \"react\";\nimport CodeMirror from \"@uiw/react-codemirror\";\nimport { javascript } from \"@codemirror/lang-javascript\";\nimport { autocompletion } from \"@codemirror/autocomplete\";\nimport { indentWithTab, insertTab } from \"@codemirror/commands\";\nimport { keymap } from \"@codemirror/view\";\nimport { indentOnInput, indentUnit } from \"@codemirror/language\";\nimport { useGameState } from \"../../../contexts/GameStateContext\";\nimport { useEditor } from \"../../../contexts/EditorContext\";\nimport {\n loadCode,\n createDebouncedCodeSave,\n} from \"../../../services/codestorage\";\n\nconst createGameCompletion = (gameConfig) => {\n return autocompletion({\n override: [\n (context) => {\n const word = context.matchBefore(/\\w*/);\n if (!word || (word.from === word.to && !context.explicit)) return null;\n\n const allowedFunctions = gameConfig?.allowedFunctions || [];\n const functionDocs = gameConfig?.functionDocumentation || {};\n const allowedStructures = gameConfig?.allowedControlStructures || [];\n\n const functionOptions = allowedFunctions.map((funcName) => {\n const doc = functionDocs[funcName] || {};\n return {\n label: funcName,\n type: \"function\",\n info: doc.description || `Função ${funcName}`,\n detail: doc.syntax || `${funcName}()`,\n apply: doc.example || `${funcName}()`,\n };\n });\n\n const structureOptions = allowedStructures.map((structure) => {\n const templates = {\n if: \"if (${condition}) {\\n ${code}\\n}\",\n else: \"else {\\n ${code}\\n}\",\n while: \"while (${condition}) {\\n ${code}\\n}\",\n for: \"for (${init}; ${condition}; ${update}) {\\n ${code}\\n}\",\n var: \"var ${name} = ${value};\",\n function: \"function ${name}() {\\n ${code}\\n}\",\n };\n\n return {\n label: structure,\n type: \"keyword\",\n info: `Estrutura de controle ${structure}`,\n detail: templates[structure] || structure,\n apply: templates[structure] || structure,\n };\n });\n\n return {\n from: word.from,\n options: [...functionOptions, ...structureOptions],\n };\n },\n ],\n });\n};\n\nexport default function CodeEditor() {\n const {\n registerCodeEditorFunction,\n onCodeEditorChange,\n gameConfig,\n currentPhase,\n } = useGameState();\n const faseAtual = currentPhase;\n const { gameNameKey } = useEditor();\n\n const faseConfig = gameConfig?.fases?.find((fase) => fase.id === faseAtual);\n const initialCode = faseConfig?.initialCode || \"// Digite seu código aqui\";\n\n const [code, setCode] = useState(initialCode);\n const [isLoading, setIsLoading] = useState(true);\n\n const debouncedCodeSave = useMemo(() => createDebouncedCodeSave(1000), []);\n const codeStorageKey = `${gameNameKey}-code`;\n\n useEffect(() => {\n setIsLoading(true);\n\n const savedCode = loadCode(codeStorageKey);\n if (savedCode !== null) {\n setCode(savedCode);\n } else {\n setCode(initialCode);\n }\n\n setIsLoading(false);\n }, [codeStorageKey, initialCode]);\n\n useEffect(() => {\n const getCodeFromEditor = () => {\n return code;\n };\n\n registerCodeEditorFunction(getCodeFromEditor);\n\n return () => {\n registerCodeEditorFunction(null);\n };\n }, [code, registerCodeEditorFunction]);\n\n useEffect(() => {\n if (!isLoading) {\n onCodeEditorChange(code);\n }\n }, [code, onCodeEditorChange, isLoading]);\n\n const handleChange = (value) => {\n setCode(value);\n\n if (!isLoading) {\n debouncedCodeSave(codeStorageKey, value);\n }\n };\n\n useEffect(() => {\n return () => {\n debouncedCodeSave.cancel();\n };\n }, [debouncedCodeSave]);\n\n return (\n <div className=\"w-full h-full\">\n <CodeMirror\n value={code}\n height=\"100%\"\n extensions={[\n javascript(),\n createGameCompletion(gameConfig),\n indentOnInput(),\n indentUnit.of(\" \"), // 2 espaços para indentação\n keymap.of([\n indentWithTab,\n {\n key: \"Enter\",\n run: ({ state, dispatch }) => {\n const { from } = state.selection.main;\n const line = state.doc.lineAt(from);\n const lineText = line.text;\n const indent = lineText.match(/^\\s*/)[0];\n\n let newIndent = indent;\n if (lineText.trim().endsWith(\"{\")) {\n newIndent += \" \";\n }\n\n dispatch(\n state.update({\n changes: {\n from,\n insert: \"\\n\" + newIndent,\n },\n selection: { anchor: from + newIndent.length + 1 },\n }),\n );\n\n return true;\n },\n },\n ]),\n ]}\n onChange={handleChange}\n basicSetup={{\n lineNumbers: true,\n highlightActiveLineGutter: true,\n highlightSpecialChars: true,\n history: true,\n foldGutter: true,\n drawSelection: true,\n dropCursor: true,\n allowMultipleSelections: true,\n indentOnInput: true,\n bracketMatching: true,\n closeBrackets: true,\n autocompletion: true,\n highlightSelectionMatches: false,\n }}\n className=\"h-full\"\n />\n </div>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/components/game/editors/OptionsEditor.jsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/components/game/editors/custom_category.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/components/game/editors/toolboxIcons.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/components/game/modal/CodeArea.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'React' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":13,"suggestions":[{"messageId":"removeVar","data":{"varName":"React"},"fix":{"range":[7,18],"text":""},"desc":"Remove unused variable 'React'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Code' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":14,"suggestions":[{"messageId":"removeVar","data":{"varName":"Code"},"fix":{"range":[27,63],"text":""},"desc":"Remove unused variable 'Code'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import React from \"react\";\nimport { Code } from \"lucide-react\";\n\nexport const CodeArea = ({\n code,\n title = \"Código Gerado\",\n variant = \"success\",\n}) => {\n const textColors = {\n success: \"text-green-400\",\n failure: \"text-red-300\",\n };\n\n return (\n <div className=\"mt-4\">\n <div className=\"flex items-center space-x-2 mb-3\">\n <Code className=\"w-5 h-5 text-gray-500\" />\n <h3 className=\"text-lg font-medium text-gray-900\">{title}</h3>\n </div>\n\n <div className=\"bg-gray-900 rounded-lg p-4 overflow-auto border border-gray-800\">\n <pre\n className={`${textColors[variant]} text-sm font-mono whitespace-pre-wrap`}\n >\n {code || \"Nenhum código disponível\"}\n </pre>\n </div>\n </div>\n );\n};\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/components/game/modal/FeedbackBox.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'React' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":13,"suggestions":[{"messageId":"removeVar","data":{"varName":"React"},"fix":{"range":[7,18],"text":""},"desc":"Remove unused variable 'React'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import React from \"react\";\n\nexport const FeedbackBox = ({ title, children, variant = \"success\" }) => {\n const styles = {\n success: \"bg-blue-50 border-blue-200 text-blue-800\",\n failure: \"bg-amber-50 border-amber-200 text-amber-800\",\n };\n\n const titleStyles = {\n success: \"text-blue-900\",\n failure: \"text-amber-900\",\n };\n\n return (\n <div className={`mt-6 p-4 rounded-lg border ${styles[variant]}`}>\n <h4\n className={`font-medium mb-2 flex items-center gap-2 ${titleStyles[variant]}`}\n >\n {variant === \"success\" ? \"💡\" : \"⚠️\"} {title}\n </h4>\n <div className=\"text-sm leading-relaxed\">{children}</div>\n </div>\n );\n};\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/components/game/modal/ModalBase.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'React' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":13,"suggestions":[{"messageId":"removeVar","data":{"varName":"React"},"fix":{"range":[7,18],"text":""},"desc":"Remove unused variable 'React'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'X' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":11,"suggestions":[{"messageId":"removeVar","data":{"varName":"X"},"fix":{"range":[27,60],"text":""},"desc":"Remove unused variable 'X'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import React from \"react\";\nimport { X } from \"lucide-react\";\n\nexport const ModalBase = ({ isOpen, onClose, children }) => {\n if (!isOpen) return null;\n\n return (\n <div\n className=\"fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4\"\n onClick={(e) => e.target === e.currentTarget && onClose()}\n >\n <div className=\"bg-white rounded-lg shadow-xl max-w-2xl w-full max-h-[85vh] flex flex-col overflow-hidden\">\n {children}\n </div>\n </div>\n );\n};\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/components/game/modal/ModalHeader.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'React' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":13,"suggestions":[{"messageId":"removeVar","data":{"varName":"React"},"fix":{"range":[7,18],"text":""},"desc":"Remove unused variable 'React'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'X' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":11,"suggestions":[{"messageId":"removeVar","data":{"varName":"X"},"fix":{"range":[36,38],"text":""},"desc":"Remove unused variable 'X'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'isSuccess' is assigned a value but never used. Allowed unused vars must match /^_/u.","line":10,"column":9,"nodeType":"Identifier","messageId":"unusedVar","endLine":10,"endColumn":18,"suggestions":[{"messageId":"removeVar","data":{"varName":"isSuccess"},"fix":{"range":[183,223],"text":""},"desc":"Remove unused variable 'isSuccess'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Icon' is assigned a value but never used. Allowed unused vars must match /^_/u.","line":26,"column":31,"nodeType":"Identifier","messageId":"unusedVar","endLine":26,"endColumn":35,"suggestions":[{"messageId":"removeVar","data":{"varName":"Icon"},"fix":{"range":[540,546],"text":""},"desc":"Remove unused variable 'Icon'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":4,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import React from \"react\";\nimport { X, CheckCircle, AlertCircle } from \"lucide-react\";\n\nexport const ModalHeader = ({\n title,\n subTitle,\n variant = \"success\",\n onClose,\n}) => {\n const isSuccess = variant === \"success\";\n\n // Mapeamento de estilos por variante\n const config = {\n success: {\n bgColor: \"bg-green-100\",\n iconColor: \"text-green-600\",\n Icon: CheckCircle,\n },\n failure: {\n bgColor: \"bg-red-100\",\n iconColor: \"text-red-600\",\n Icon: AlertCircle,\n },\n };\n\n const { bgColor, iconColor, Icon } = config[variant];\n\n return (\n <div className=\"flex items-center justify-between p-6 border-b border-gray-200\">\n <div className=\"flex items-center space-x-3\">\n <div\n className={`w-10 h-10 ${bgColor} rounded-full flex items-center justify-center`}\n >\n <Icon className={`w-6 h-6 ${iconColor}`} />\n </div>\n <div>\n <h2 className=\"text-xl font-semibold text-gray-900\">{title}</h2>\n <p className=\"text-sm text-gray-600\">{subTitle}</p>\n </div>\n </div>\n <button\n onClick={onClose}\n className=\"text-gray-400 hover:text-gray-600 transition-colors\"\n aria-label=\"Fechar\"\n >\n <X className=\"w-6 h-6\" />\n </button>\n </div>\n );\n};\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/config/blocklyConfig.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/config/categories.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/config/difficulty.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/config/gameRegistry.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/config/type.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/contexts/EditorContext.jsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/contexts/GameStateContext.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'React' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":13,"suggestions":[{"messageId":"removeVar","data":{"varName":"React"},"fix":{"range":[7,13],"text":""},"desc":"Remove unused variable 'React'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import React, {\n createContext,\n useContext,\n useEffect,\n useState,\n useCallback,\n useRef,\n} from \"react\";\n\nexport const GAME_STATES = {\n PARADO: \"parado\",\n EXECUTANDO: \"executando\",\n SUCESSO: \"sucesso\",\n FALHA: \"falha\",\n};\n\nconst GameStateContext = createContext();\n\nexport function GameStateProvider({ children, gameConfig }) {\n const [executionState, setExecutionState] = useState(GAME_STATES.PARADO);\n const [generatedCode, setGeneratedCode] = useState(\"\");\n const [currentBlockCount, setCurrentBlockCount] = useState(0);\n const [onWorkspaceChangeCallback, setOnWorkspaceChangeCallback] =\n useState(null);\n const [editorType, setEditorType] = useState(\"blockly\"); // 'blockly' ou 'code'\n const [codeEditorContent, setCodeEditorContent] = useState(\"\");\n const [failureMessage, setFailureMessage] = useState(\"\");\n const [isDebugMode, setIsDebugMode] = useState(false);\n \n const [currentPhase, setCurrentPhase] = useState(1);\n const [completedPhases, setCompletedPhases] = useState([]);\n\n const storageKey = `${gameConfig.gameId}-fases-concluidas`;\n const initialized = useRef(false);\n\n const getCodeFromWorkspace = useRef(null);\n const getCodeFromEditor = useRef(null);\n\n useEffect(() => {\n const urlParams = new URLSearchParams(window.location.search);\n const debugKey = Array.from(urlParams.keys()).find(\n (key) => key.toLowerCase() === \"debug\",\n );\n const isDebug = urlParams.get(debugKey)?.toLowerCase() === \"true\";\n\n if (isDebug) {\n setIsDebugMode(true);\n const todas = Array.from(\n { length: gameConfig.fases.length },\n (_, i) => i + 1,\n );\n setCompletedPhases(todas);\n localStorage.setItem(storageKey, JSON.stringify(todas));\n setCurrentPhase(gameConfig.fases.length);\n return;\n }\n\n if (initialized.current) return;\n\n const saved = localStorage.getItem(storageKey);\n\n if (saved) {\n const fasesSalvas = JSON.parse(saved);\n\n setCompletedPhases(fasesSalvas);\n\n if (fasesSalvas.length > 0) {\n const ultimaFaseConcluida = Math.max(...fasesSalvas);\n const proximaFase = ultimaFaseConcluida + 1;\n const faseParaSetar =\n proximaFase <= gameConfig.fases.length\n ? proximaFase\n : ultimaFaseConcluida;\n setCurrentPhase(faseParaSetar);\n }\n }\n initialized.current = true;\n }, [storageKey, gameConfig.fases.length]);\n\n useEffect(() => {\n if (!initialized.current) return;\n\n localStorage.setItem(storageKey, JSON.stringify(completedPhases));\n }, [completedPhases, storageKey]);\n\n const execute = () => {\n if (editorType === \"code\") {\n if (getCodeFromEditor.current) {\n const codigo = getCodeFromEditor.current();\n\n if (codigo && codigo.trim()) {\n setGeneratedCode(codigo);\n setExecutionState(GAME_STATES.EXECUTANDO);\n } else {\n console.error(\n \"CodeEditor ainda não registrou sua função de execução.\",\n );\n }\n }\n } else {\n if (getCodeFromWorkspace.current) {\n const { codigo, workspace } = getCodeFromWorkspace.current();\n if (codigo && workspace) {\n setGeneratedCode({ codigo, workspace });\n setExecutionState(GAME_STATES.EXECUTANDO);\n }\n } else {\n console.error(\n \"BlocklyEditor ainda não registrou sua função de execução.\",\n );\n }\n }\n };\n\n const finalizeWithSuccess = () => {\n setExecutionState(GAME_STATES.SUCESSO);\n\n if (!completedPhases.includes(currentPhase)) {\n setCompletedPhases([...completedPhases, currentPhase]);\n }\n };\n\n const finalizeWithFailure = () => {\n setExecutionState(GAME_STATES.FALHA);\n };\n\n const restart = () => {\n setExecutionState(GAME_STATES.PARADO);\n setGeneratedCode(\"\");\n };\n\n const resetProgress = () => {\n setCompletedPhases([]);\n setCurrentPhase(1);\n localStorage.removeItem(storageKey);\n };\n\n const changePhase = (numeroFase) => {\n setCurrentPhase(numeroFase);\n setExecutionState(GAME_STATES.PARADO);\n setGeneratedCode(\"\");\n setCurrentBlockCount(0);\n setCodeEditorContent(\"\");\n };\n\n const stop = () => {\n setExecutionState(GAME_STATES.PARADO);\n setGeneratedCode(\"\");\n };\n\n const registerExecutionFunction = useCallback((func) => {\n getCodeFromWorkspace.current = func;\n }, []);\n\n const registerCodeEditorFunction = useCallback((func) => {\n getCodeFromEditor.current = func;\n }, []);\n\n const onWorkspaceChange = useCallback(\n (blockCount) => {\n setCurrentBlockCount(blockCount);\n if (onWorkspaceChangeCallback) {\n onWorkspaceChangeCallback(blockCount);\n }\n },\n [onWorkspaceChangeCallback],\n );\n\n const onCodeEditorChange = useCallback((content) => {\n setCodeEditorContent(content);\n setCurrentBlockCount(content.trim() ? 1 : 0);\n }, []);\n\n useEffect(() => {\n if (editorType === \"code\" && getCodeFromEditor.current) {\n setCurrentBlockCount(\n codeEditorContent && codeEditorContent.trim() ? 1 : 0,\n );\n } else {\n setCodeEditorContent(0);\n }\n }, [editorType, codeEditorContent]);\n\n return (\n <GameStateContext.Provider\n value={{\n // English API\n executionState,\n setExecutionState,\n generatedCode,\n setGeneratedCode,\n currentBlockCount,\n execute,\n finalizeWithSuccess,\n finalizeWithFailure,\n restart,\n stop,\n currentPhase,\n // public: change phase using `changePhase` behavior\n setCurrentPhase: changePhase,\n completedPhases,\n setCompletedPhases,\n resetProgress,\n gameConfig,\n registerExecutionFunction,\n registerCodeEditorFunction,\n onWorkspaceChange,\n onCodeEditorChange,\n setOnWorkspaceChange: setOnWorkspaceChangeCallback,\n editorType,\n setEditorType,\n codeEditorContent,\n failureMessage,\n setFailureMessage,\n isDebugMode,\n setIsDebugMode,\n // Portuguese aliases for backward compatibility\n estadoExecucao: executionState,\n setEstadoExecucao: setExecutionState,\n codigoGerado: generatedCode,\n setCodigoGerado: setGeneratedCode,\n faseAtual: currentPhase,\n setFaseAtual: changePhase,\n fasesConcluidas: completedPhases,\n setFasesConcluidas: setCompletedPhases,\n mensagemFalha: failureMessage,\n setMensagemFalha: setFailureMessage,\n debugMode: isDebugMode,\n setDebugMode: setIsDebugMode,\n executar: execute,\n finalizarComSucesso: finalizeWithSuccess,\n finalizarComFalha: finalizeWithFailure,\n reiniciar: restart,\n resetarProgresso: resetProgress,\n mudarFase: changePhase,\n parar: stop,\n registrarFuncaoExecucao: registerExecutionFunction,\n registrarFuncaoCodeEditor: registerCodeEditorFunction,\n }}\n >\n {children}\n </GameStateContext.Provider>\n );\n}\n\nexport function useGameState() {\n const context = useContext(GameStateContext);\n if (!context) {\n throw new Error(\"useGameState deve ser usado dentro de GameStateProvider\");\n }\n return context;\n}\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/automato/AutomatoGame.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'React' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":13,"suggestions":[{"messageId":"removeVar","data":{"varName":"React"},"fix":{"range":[7,13],"text":""},"desc":"Remove unused variable 'React'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'GameBase' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":16,"suggestions":[{"messageId":"removeVar","data":{"varName":"GameBase"},"fix":{"range":[58,72],"text":""},"desc":"Remove unused variable 'GameBase'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'GameEditor' is defined but never used. Allowed unused vars must match /^_/u.","line":3,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":3,"endColumn":18,"suggestions":[{"messageId":"removeVar","data":{"varName":"GameEditor"},"fix":{"range":[113,129],"text":""},"desc":"Remove unused variable 'GameEditor'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'BlocklyEditor' is defined but never used. Allowed unused vars must match /^_/u.","line":4,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":4,"endColumn":21,"suggestions":[{"messageId":"removeVar","data":{"varName":"BlocklyEditor"},"fix":{"range":[172,191],"text":""},"desc":"Remove unused variable 'BlocklyEditor'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'GameStateProvider' is defined but never used. Allowed unused vars must match /^_/u.","line":9,"column":3,"nodeType":"Identifier","messageId":"unusedVar","endLine":9,"endColumn":20,"suggestions":[{"messageId":"removeVar","data":{"varName":"GameStateProvider"},"fix":{"range":[406,424],"text":""},"desc":"Remove unused variable 'GameStateProvider'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'AutomatoGameContent' is defined but never used. Allowed unused vars must match /^_/u.","line":17,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":17,"endColumn":29,"suggestions":[{"messageId":"removeVar","data":{"varName":"AutomatoGameContent"},"fix":{"range":[687,1432],"text":""},"desc":"Remove unused variable 'AutomatoGameContent'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":6,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import React, { useEffect, useMemo } from \"react\";\nimport GameBase from \"../../components/game/GameBase\";\nimport GameEditor from \"../../components/game/GameEditor\";\nimport BlocklyEditor from \"../../components/game/editors/BlocklyEditor\";\nimport { createGame } from \"./game\";\nimport { gameConfig } from \"./config/config\";\nimport { registerBlocks, generateDynamicToolbox } from \"./blocks/blocks\";\nimport {\n GameStateProvider,\n useGameState,\n} from \"../../contexts/GameStateContext\";\nimport { useAutomatoTour } from \"./hooks/useAutomatoTour\";\nimport { debugSolutions } from \"./config/debugSolutions\";\nimport \"shepherd.js/dist/css/shepherd.css\";\nimport \"../../styles/shepherd-theme.css\";\n\nfunction AutomatoGameContent() {\n const { isDebugMode, setFailureMessage } = useGameState();\n const { startTour } = useAutomatoTour();\n\n useEffect(() => {\n registerBlocks();\n }, []);\n\n const toolboxGenerator = useMemo(() => {\n return (allowedBlocks) => generateDynamicToolbox(allowedBlocks);\n }, []);\n\n const renderEditor = () => {\n return (\n <BlocklyEditor\n toolboxGenerator={toolboxGenerator}\n debugSolutions={isDebugMode ? debugSolutions : null}\n />\n );\n };\n\n return (\n <GameBase\n gameFactory={createGame}\n gameConfig={gameConfig}\n onHelpClick={startTour}\n customFailureHandler={setFailureMessage}\n >\n <GameEditor>{renderEditor()}</GameEditor>\n </GameBase>\n );\n}\n\nexport default function AutomatoGame() {\n return (\n <GameStateProvider gameConfig={gameConfig}>\n <AutomatoGameContent />\n </GameStateProvider>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/automato/__tests__/integration.test.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/automato/blocks/blocks.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/automato/config/config.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/automato/config/debugSolutions.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/automato/config/tourSteps.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/automato/game.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/automato/hooks/interpreterSetup.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/automato/hooks/useAutomatoTour.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/automato/validation/validators.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/cripto/CriptoGame.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'React' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":13,"suggestions":[{"messageId":"removeVar","data":{"varName":"React"},"fix":{"range":[7,13],"text":""},"desc":"Remove unused variable 'React'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'GameBase' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":16,"suggestions":[{"messageId":"removeVar","data":{"varName":"GameBase"},"fix":{"range":[58,72],"text":""},"desc":"Remove unused variable 'GameBase'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'GameEditor' is defined but never used. Allowed unused vars must match /^_/u.","line":3,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":3,"endColumn":18,"suggestions":[{"messageId":"removeVar","data":{"varName":"GameEditor"},"fix":{"range":[113,129],"text":""},"desc":"Remove unused variable 'GameEditor'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'BlocklyEditor' is defined but never used. Allowed unused vars must match /^_/u.","line":4,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":4,"endColumn":21,"suggestions":[{"messageId":"removeVar","data":{"varName":"BlocklyEditor"},"fix":{"range":[172,191],"text":""},"desc":"Remove unused variable 'BlocklyEditor'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'GameStateProvider' is defined but never used. Allowed unused vars must match /^_/u.","line":9,"column":3,"nodeType":"Identifier","messageId":"unusedVar","endLine":9,"endColumn":20,"suggestions":[{"messageId":"removeVar","data":{"varName":"GameStateProvider"},"fix":{"range":[406,424],"text":""},"desc":"Remove unused variable 'GameStateProvider'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'CriptoContent' is defined but never used. Allowed unused vars must match /^_/u.","line":18,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":18,"endColumn":23,"suggestions":[{"messageId":"removeVar","data":{"varName":"CriptoContent"},"fix":{"range":[739,1406],"text":""},"desc":"Remove unused variable 'CriptoContent'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":6,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import React, { useEffect, useMemo } from \"react\";\nimport GameBase from \"../../components/game/GameBase\";\nimport GameEditor from \"../../components/game/GameEditor\";\nimport BlocklyEditor from \"../../components/game/editors/BlocklyEditor\";\nimport { createGame } from \"./game\";\nimport { gameConfig } from \"./config/config\";\nimport { generateDynamicToolbox, registerBlocks } from \"./blocks/blocks\";\nimport {\n GameStateProvider,\n useGameState,\n} from \"../../contexts/GameStateContext\";\nimport { starterBlocks } from \"./config/starterBlocks\";\nimport { useCriptoTour } from \"./hooks/useCriptoTour\";\nimport { debugSolutions } from \"./config/debugSolutions\";\nimport \"shepherd.js/dist/css/shepherd.css\";\nimport \"../../styles/shepherd-theme.css\";\n\nfunction CriptoContent() {\n const { setFailureMessage, isDebugMode } = useGameState();\n useCriptoTour();\n\n useEffect(() => {\n registerBlocks();\n }, []);\n\n const toolboxGenerator = useMemo(() => {\n return (allowedBlocks) => generateDynamicToolbox(allowedBlocks);\n }, []);\n\n return (\n <GameBase\n gameFactory={createGame}\n gameConfig={gameConfig}\n customFailureHandler={setFailureMessage}\n >\n <GameEditor>\n <BlocklyEditor\n toolboxGenerator={toolboxGenerator}\n debugSolutions={isDebugMode ? debugSolutions : null}\n starterBlocks={starterBlocks}\n />\n </GameEditor>\n </GameBase>\n );\n}\n\nexport default function CriptoGame() {\n return (\n <GameStateProvider gameConfig={gameConfig}>\n <CriptoContent />\n </GameStateProvider>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/cripto/blocks/blocks.js","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'block' is defined but never used. Allowed unused args must match /^_/u.","line":628,"column":61,"nodeType":"Identifier","messageId":"unusedVar","endLine":628,"endColumn":66,"suggestions":[{"messageId":"removeVar","data":{"varName":"block"},"fix":{"range":[16272,16277],"text":""},"desc":"Remove unused variable 'block'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'block' is defined but never used. Allowed unused args must match /^_/u.","line":634,"column":59,"nodeType":"Identifier","messageId":"unusedVar","endLine":634,"endColumn":64,"suggestions":[{"messageId":"removeVar","data":{"varName":"block"},"fix":{"range":[16466,16471],"text":""},"desc":"Remove unused variable 'block'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'block' is defined but never used. Allowed unused args must match /^_/u.","line":640,"column":62,"nodeType":"Identifier","messageId":"unusedVar","endLine":640,"endColumn":67,"suggestions":[{"messageId":"removeVar","data":{"varName":"block"},"fix":{"range":[16664,16669],"text":""},"desc":"Remove unused variable 'block'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'block' is defined but never used. Allowed unused args must match /^_/u.","line":646,"column":56,"nodeType":"Identifier","messageId":"unusedVar","endLine":646,"endColumn":61,"suggestions":[{"messageId":"removeVar","data":{"varName":"block"},"fix":{"range":[16853,16858],"text":""},"desc":"Remove unused variable 'block'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'block' is defined but never used. Allowed unused args must match /^_/u.","line":652,"column":64,"nodeType":"Identifier","messageId":"unusedVar","endLine":652,"endColumn":69,"suggestions":[{"messageId":"removeVar","data":{"varName":"block"},"fix":{"range":[17064,17069],"text":""},"desc":"Remove unused variable 'block'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'block' is defined but never used. Allowed unused args must match /^_/u.","line":708,"column":59,"nodeType":"Identifier","messageId":"unusedVar","endLine":708,"endColumn":64,"suggestions":[{"messageId":"removeVar","data":{"varName":"block"},"fix":{"range":[18825,18830],"text":""},"desc":"Remove unused variable 'block'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'block' is defined but never used. Allowed unused args must match /^_/u.","line":723,"column":61,"nodeType":"Identifier","messageId":"unusedVar","endLine":723,"endColumn":66,"suggestions":[{"messageId":"removeVar","data":{"varName":"block"},"fix":{"range":[19249,19254],"text":""},"desc":"Remove unused variable 'block'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'block' is defined but never used. Allowed unused args must match /^_/u.","line":738,"column":66,"nodeType":"Identifier","messageId":"unusedVar","endLine":738,"endColumn":71,"suggestions":[{"messageId":"removeVar","data":{"varName":"block"},"fix":{"range":[19695,19700],"text":""},"desc":"Remove unused variable 'block'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'block' is defined but never used. Allowed unused args must match /^_/u.","line":753,"column":64,"nodeType":"Identifier","messageId":"unusedVar","endLine":753,"endColumn":69,"suggestions":[{"messageId":"removeVar","data":{"varName":"block"},"fix":{"range":[20139,20144],"text":""},"desc":"Remove unused variable 'block'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'block' is defined but never used. Allowed unused args must match /^_/u.","line":768,"column":59,"nodeType":"Identifier","messageId":"unusedVar","endLine":768,"endColumn":64,"suggestions":[{"messageId":"removeVar","data":{"varName":"block"},"fix":{"range":[20560,20565],"text":""},"desc":"Remove unused variable 'block'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'block' is defined but never used. Allowed unused args must match /^_/u.","line":783,"column":67,"nodeType":"Identifier","messageId":"unusedVar","endLine":783,"endColumn":72,"suggestions":[{"messageId":"removeVar","data":{"varName":"block"},"fix":{"range":[21009,21014],"text":""},"desc":"Remove unused variable 'block'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'block' is defined but never used. Allowed unused args must match /^_/u.","line":798,"column":58,"nodeType":"Identifier","messageId":"unusedVar","endLine":798,"endColumn":63,"suggestions":[{"messageId":"removeVar","data":{"varName":"block"},"fix":{"range":[21429,21434],"text":""},"desc":"Remove unused variable 'block'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":12,"fixableErrorCount":0,"fixableWarningCount":0,"source":"\"use strict\";\n\nimport * as Blockly from \"blockly/core\";\nimport \"blockly/blocks\";\nimport { javascriptGenerator } from \"blockly/javascript\";\n\nconst HUE_LOGICA = 210;\nconst HUE_MATEMATICA = 230;\nconst HUE_TEXTO = 160;\nconst HUE_REPETICAO = 120;\nconst HUE_VARIAVEIS = 330;\n\nexport const registerBlocks = () => {\n defineBlocks();\n defineGenerators();\n};\n\nexport const generateDynamicToolbox = (allowedBlocks = []) => {\n const blockDefinitions = {\n // Matemática\n math_number: {\n kind: \"block\",\n type: \"math_number\",\n },\n math_arithmetic: {\n kind: \"block\",\n type: \"math_arithmetic\",\n },\n math_modulo: {\n kind: \"block\",\n type: \"math_modulo\",\n },\n\n // Texto\n text: {\n kind: \"block\",\n type: \"text\",\n },\n text_indexOf: {\n kind: \"block\",\n type: \"text_indexOf\",\n },\n text_charAt: {\n kind: \"block\",\n type: \"text_charAt\",\n },\n text_join: {\n kind: \"block\",\n type: \"text_join\",\n },\n text_length: {\n kind: \"block\",\n type: \"text_length\",\n },\n alfabeto: {\n kind: \"block\",\n type: \"alfabeto\",\n },\n alfabeto_secreto: {\n kind: \"block\",\n type: \"alfabeto_secreto\",\n },\n\n // Lógica\n controls_if: {\n kind: \"block\",\n type: \"controls_if\",\n },\n logic_compare: {\n kind: \"block\",\n type: \"logic_compare\",\n },\n\n // Repetição\n controls_whileUntil: {\n kind: \"block\",\n type: \"controls_whileUntil\",\n },\n definir_contador: {\n kind: \"block\",\n type: \"definir_contador\",\n },\n obter_contador: {\n kind: \"block\",\n type: \"obter_contador\",\n },\n // Blocos Customizados de Entrada/Saída\n definir_entrada: {\n kind: \"block\",\n type: \"definir_entrada\",\n },\n definir_saida: {\n kind: \"block\",\n type: \"definir_saida\",\n },\n concatenar_saida: {\n kind: \"block\",\n type: \"concatenar_saida\",\n },\n obter_entrada: {\n kind: \"block\",\n type: \"obter_entrada\",\n },\n obter_saida: {\n kind: \"block\",\n type: \"obter_saida\",\n },\n\n // Variáveis Customizadas\n definir_letra: {\n kind: \"block\",\n type: \"definir_letra\",\n },\n obter_letra: {\n kind: \"block\",\n type: \"obter_letra\",\n },\n definir_posicao: {\n kind: \"block\",\n type: \"definir_posicao\",\n },\n obter_posicao: {\n kind: \"block\",\n type: \"obter_posicao\",\n },\n definir_nova_posicao: {\n kind: \"block\",\n type: \"definir_nova_posicao\",\n },\n obter_nova_posicao: {\n kind: \"block\",\n type: \"obter_nova_posicao\",\n },\n definir_nova_letra: {\n kind: \"block\",\n type: \"definir_nova_letra\",\n },\n obter_nova_letra: {\n kind: \"block\",\n type: \"obter_nova_letra\",\n },\n definir_chave: {\n kind: \"block\",\n type: \"definir_chave\",\n },\n obter_chave: {\n kind: \"block\",\n type: \"obter_chave\",\n },\n definir_letra_secreta: {\n kind: \"block\",\n type: \"definir_letra_secreta\",\n },\n obter_letra_secreta: {\n kind: \"block\",\n type: \"obter_letra_secreta\",\n },\n definir_soma: {\n kind: \"block\",\n type: \"definir_soma\",\n },\n obter_soma: {\n kind: \"block\",\n type: \"obter_soma\",\n },\n };\n\n const toolboxContents = {\n kind: \"categoryToolbox\",\n contents: [\n {\n kind: \"category\",\n name: \"Entrada/Saída\",\n colour: HUE_VARIAVEIS,\n contents: [],\n cssConfig: { container: \"variaveis\" },\n },\n {\n kind: \"category\",\n name: \"Lógica\",\n colour: HUE_LOGICA,\n contents: [],\n cssConfig: { container: \"logica\" },\n },\n {\n kind: \"category\",\n name: \"Repetição\",\n colour: HUE_REPETICAO,\n contents: [],\n cssConfig: { container: \"repeticao\" },\n },\n {\n kind: \"category\",\n name: \"Texto\",\n colour: HUE_TEXTO,\n contents: [],\n cssConfig: { container: \"texto\" },\n },\n {\n kind: \"category\",\n name: \"Matemática\",\n colour: HUE_MATEMATICA,\n contents: [],\n cssConfig: { container: \"matematica\" },\n },\n ],\n };\n\n allowedBlocks.forEach((blockId) => {\n const blockDef = blockDefinitions[blockId];\n\n if (!blockDef) {\n console.warn(`Bloco não encontrado: ${blockId}`);\n return;\n }\n\n const categoryMap = {\n obter_entrada: 0,\n obter_saida: 0,\n definir_entrada: 0,\n definir_saida: 0,\n concatenar_saida: 0,\n definir_letra: 0,\n obter_letra: 0,\n definir_posicao: 0,\n obter_posicao: 0,\n definir_nova_posicao: 0,\n obter_nova_posicao: 0,\n definir_nova_letra: 0,\n obter_nova_letra: 0,\n definir_chave: 0,\n obter_chave: 0,\n definir_letra_secreta: 0,\n obter_letra_secreta: 0,\n definir_soma: 0,\n obter_soma: 0,\n controls_if: 1,\n logic_compare: 1,\n controls_whileUntil: 2,\n definir_contador: 2,\n obter_contador: 2,\n text: 3,\n text_charAt: 3,\n text_join: 3,\n text_length: 3,\n text_indexOf: 3,\n alfabeto: 3,\n alfabeto_secreto: 3,\n math_number: 4,\n math_arithmetic: 4,\n math_modulo: 4,\n };\n\n const categoryIndex = categoryMap[blockId];\n\n if (\n categoryIndex !== undefined &&\n categoryIndex >= 0 &&\n toolboxContents.contents[categoryIndex]\n ) {\n if (!toolboxContents.contents[categoryIndex].contents) {\n toolboxContents.contents[categoryIndex].contents = [];\n }\n toolboxContents.contents[categoryIndex].contents.push(blockDef);\n }\n });\n\n return toolboxContents;\n};\n\nconst defineBlocks = () => {\n Blockly.Blocks[\"text_charAt\"] = {\n init: function () {\n this.setHelpUrl(Blockly.Msg[\"TEXT_CHARAT_HELPURL\"]);\n this.setColour(HUE_TEXTO);\n this.setOutput(true, \"String\");\n this.appendValueInput(\"VALUE\").setCheck(\"String\").appendField(\"no texto\");\n this.appendValueInput(\"AT\").setCheck(\"Number\").appendField(\"obter letra\");\n this.setInputsInline(true);\n this.setTooltip(Blockly.Msg[\"TEXT_CHARAT_TOOLTIP\"]);\n },\n };\n\n // Bloco: Definir Contador\n Blockly.Blocks[\"definir_contador\"] = {\n init: function () {\n this.appendValueInput(\"VALUE\")\n .setCheck(null)\n .appendField(\"definir CONTADOR como\");\n this.setPreviousStatement(true, null);\n this.setNextStatement(true, null);\n this.setColour(HUE_REPETICAO);\n this.setTooltip(\"Define o valor do CONTADOR\");\n this.setHelpUrl(\"\");\n },\n };\n\n // Bloco: Definir Entrada\n Blockly.Blocks[\"definir_entrada\"] = {\n init: function () {\n this.appendValueInput(\"VALUE\")\n .setCheck(null)\n .appendField(\"definir ENTRADA como\");\n this.setPreviousStatement(true, null);\n this.setNextStatement(true, null);\n this.setColour(HUE_VARIAVEIS);\n this.setTooltip(\"Define o valor da ENTRADA\");\n this.setHelpUrl(\"\");\n },\n };\n\n // Bloco: Definir Saída\n Blockly.Blocks[\"definir_saida\"] = {\n init: function () {\n this.appendValueInput(\"VALUE\")\n .setCheck(null)\n .appendField(\"definir SAÍDA como\");\n this.setPreviousStatement(true, null);\n this.setNextStatement(true, null);\n this.setColour(HUE_VARIAVEIS);\n this.setTooltip(\"Define o valor da SAÍDA\");\n this.setHelpUrl(\"\");\n },\n };\n\n // Bloco: Concatenar Saída\n Blockly.Blocks[\"concatenar_saida\"] = {\n init: function () {\n this.appendValueInput(\"VALUE\")\n .setCheck(null)\n .appendField(\"adicionar à SAÍDA\");\n this.setPreviousStatement(true, null);\n this.setNextStatement(true, null);\n this.setColour(HUE_VARIAVEIS);\n this.setTooltip(\"Adiciona um valor ao final da saída\");\n this.setHelpUrl(\"\");\n },\n };\n\n // Bloco: Obter Entrada\n Blockly.Blocks[\"obter_entrada\"] = {\n init: function () {\n this.appendDummyInput().appendField(\"ENTRADA\");\n this.setOutput(true, null);\n this.setColour(HUE_VARIAVEIS);\n this.setTooltip(\"Obtém o valor atual da entrada\");\n this.setHelpUrl(\"\");\n },\n };\n\n // Bloco: Obter Saída\n Blockly.Blocks[\"obter_saida\"] = {\n init: function () {\n this.appendDummyInput().appendField(\"SAÍDA\");\n this.setOutput(true, null);\n this.setColour(HUE_VARIAVEIS);\n this.setTooltip(\"Obtém o valor atual da saída\");\n this.setHelpUrl(\"\");\n },\n };\n\n // Bloco: Obter Contador\n Blockly.Blocks[\"obter_contador\"] = {\n init: function () {\n this.appendDummyInput().appendField(\"CONTADOR\");\n this.setOutput(true, null);\n this.setColour(HUE_REPETICAO);\n this.setTooltip(\"Obtém o valor atual do CONTADOR\");\n this.setHelpUrl(\"\");\n },\n };\n\n // Bloco: Alfabeto (constante)\n Blockly.Blocks[\"alfabeto\"] = {\n init: function () {\n this.appendDummyInput().appendField(\"ALFABETO\");\n this.setOutput(true, \"String\");\n this.setColour(HUE_TEXTO);\n this.setTooltip(\n \"Retorna o alfabeto completo: ABCDEFGHIJKLMNOPQRSTUVWXYZ\",\n );\n this.setHelpUrl(\"\");\n },\n };\n\n // Bloco: Alfabeto Secreto (constante)\n Blockly.Blocks[\"alfabeto_secreto\"] = {\n init: function () {\n this.appendDummyInput().appendField(\"ALFABETO SECRETO\");\n this.setOutput(true, \"String\");\n this.setColour(HUE_TEXTO);\n this.setTooltip(\n \"Retorna o alfabeto embaralhado: QWERTYUIOPASDFGHJKLZXCVBNM\",\n );\n this.setHelpUrl(\"\");\n },\n };\n\n // ============ BLOCOS DE VARIÁVEIS CUSTOMIZADAS ============\n\n // Bloco: Definir letra\n Blockly.Blocks[\"definir_letra\"] = {\n init: function () {\n this.appendValueInput(\"VALUE\")\n .setCheck(null)\n .appendField(\"definir LETRA como\");\n this.setPreviousStatement(true, null);\n this.setNextStatement(true, null);\n this.setColour(HUE_VARIAVEIS);\n this.setTooltip(\"Define o valor da variável LETRA\");\n this.setHelpUrl(\"\");\n },\n };\n\n // Bloco: Obter letra\n Blockly.Blocks[\"obter_letra\"] = {\n init: function () {\n this.appendDummyInput().appendField(\"LETRA\");\n this.setOutput(true, null);\n this.setColour(HUE_VARIAVEIS);\n this.setTooltip(\"Obtém o valor da variável LETRA\");\n this.setHelpUrl(\"\");\n },\n };\n\n // Bloco: Definir posicao\n Blockly.Blocks[\"definir_posicao\"] = {\n init: function () {\n this.appendValueInput(\"VALUE\")\n .setCheck(null)\n .appendField(\"definir POSIÇÃO como\");\n this.setPreviousStatement(true, null);\n this.setNextStatement(true, null);\n this.setColour(HUE_VARIAVEIS);\n this.setTooltip(\"Define o valor da variável POSIÇÃO\");\n this.setHelpUrl(\"\");\n },\n };\n\n // Bloco: Obter posicao\n Blockly.Blocks[\"obter_posicao\"] = {\n init: function () {\n this.appendDummyInput().appendField(\"POSIÇÃO\");\n this.setOutput(true, null);\n this.setColour(HUE_VARIAVEIS);\n this.setTooltip(\"Obtém o valor da variável POSIÇÃO\");\n this.setHelpUrl(\"\");\n },\n };\n\n // Bloco: Definir nova_posicao\n Blockly.Blocks[\"definir_nova_posicao\"] = {\n init: function () {\n this.appendValueInput(\"VALUE\")\n .setCheck(null)\n .appendField(\"definir nova_posicao como\");\n this.setPreviousStatement(true, null);\n this.setNextStatement(true, null);\n this.setColour(HUE_VARIAVEIS);\n this.setTooltip(\"Define o valor da variável nova_posicao\");\n this.setHelpUrl(\"\");\n },\n };\n\n // Bloco: Obter nova_posicao\n Blockly.Blocks[\"obter_nova_posicao\"] = {\n init: function () {\n this.appendDummyInput().appendField(\"nova_posicao\");\n this.setOutput(true, null);\n this.setColour(HUE_VARIAVEIS);\n this.setTooltip(\"Obtém o valor da variável nova_posicao\");\n this.setHelpUrl(\"\");\n },\n };\n\n // Bloco: Definir nova_letra\n Blockly.Blocks[\"definir_nova_letra\"] = {\n init: function () {\n this.appendValueInput(\"VALUE\")\n .setCheck(null)\n .appendField(\"definir nova_letra como\");\n this.setPreviousStatement(true, null);\n this.setNextStatement(true, null);\n this.setColour(HUE_VARIAVEIS);\n this.setTooltip(\"Define o valor da variável nova_letra\");\n this.setHelpUrl(\"\");\n },\n };\n\n // Bloco: Obter nova_letra\n Blockly.Blocks[\"obter_nova_letra\"] = {\n init: function () {\n this.appendDummyInput().appendField(\"nova_letra\");\n this.setOutput(true, null);\n this.setColour(HUE_VARIAVEIS);\n this.setTooltip(\"Obtém o valor da variável nova_letra\");\n this.setHelpUrl(\"\");\n },\n };\n\n // Bloco: Definir chave\n Blockly.Blocks[\"definir_chave\"] = {\n init: function () {\n this.appendValueInput(\"VALUE\")\n .setCheck(null)\n .appendField(\"definir chave como\");\n this.setPreviousStatement(true, null);\n this.setNextStatement(true, null);\n this.setColour(HUE_VARIAVEIS);\n this.setTooltip(\n \"Define o valor da variável chave (deslocamento da cifra)\",\n );\n this.setHelpUrl(\"\");\n },\n };\n\n // Bloco: Obter chave\n Blockly.Blocks[\"obter_chave\"] = {\n init: function () {\n this.appendDummyInput().appendField(\"chave\");\n this.setOutput(true, null);\n this.setColour(HUE_VARIAVEIS);\n this.setTooltip(\"Obtém o valor da variável chave\");\n this.setHelpUrl(\"\");\n },\n };\n\n // Bloco: Definir letra_secreta\n Blockly.Blocks[\"definir_letra_secreta\"] = {\n init: function () {\n this.appendValueInput(\"VALUE\")\n .setCheck(null)\n .appendField(\"definir LETRA_SECRETA como\");\n this.setPreviousStatement(true, null);\n this.setNextStatement(true, null);\n this.setColour(HUE_VARIAVEIS);\n this.setTooltip(\"Define o valor da variável LETRA_SECRETA\");\n this.setHelpUrl(\"\");\n },\n };\n\n // Bloco: Obter letra_secreta\n Blockly.Blocks[\"obter_letra_secreta\"] = {\n init: function () {\n this.appendDummyInput().appendField(\"LETRA_SECRETA\");\n this.setOutput(true, null);\n this.setColour(HUE_VARIAVEIS);\n this.setTooltip(\"Obtém o valor da variável LETRA_SECRETA\");\n this.setHelpUrl(\"\");\n },\n };\n\n // Bloco: Definir soma\n Blockly.Blocks[\"definir_soma\"] = {\n init: function () {\n this.appendValueInput(\"VALUE\")\n .setCheck(null)\n .appendField(\"definir SOMA como\");\n this.setPreviousStatement(true, null);\n this.setNextStatement(true, null);\n this.setColour(HUE_VARIAVEIS);\n this.setTooltip(\"Define o valor da variável SOMA (acumulador)\");\n this.setHelpUrl(\"\");\n },\n };\n\n // Bloco: Obter soma\n Blockly.Blocks[\"obter_soma\"] = {\n init: function () {\n this.appendDummyInput().appendField(\"SOMA\");\n this.setOutput(true, null);\n this.setColour(HUE_VARIAVEIS);\n this.setTooltip(\"Obtém o valor da variável SOMA\");\n this.setHelpUrl(\"\");\n },\n };\n};\n\nconst defineGenerators = () => {\n javascriptGenerator.STATEMENT_PREFIX = \"highlightBlock(%1);\\n\";\n javascriptGenerator.addReservedWords(\"highlightBlock\");\n\n // Gerador: Definir Entrada\n javascriptGenerator.forBlock[\"definir_entrada\"] = function (block) {\n const value =\n javascriptGenerator.valueToCode(\n block,\n \"VALUE\",\n javascriptGenerator.ORDER_ATOMIC,\n ) || \"''\";\n return \"definirEntrada(\" + value + \");\\n\";\n };\n\n // Gerador: Definir Saída\n javascriptGenerator.forBlock[\"definir_saida\"] = function (block) {\n const value =\n javascriptGenerator.valueToCode(\n block,\n \"VALUE\",\n javascriptGenerator.ORDER_ATOMIC,\n ) || \"''\";\n return \"definirSaida(\" + value + \");\\n\";\n };\n\n // Gerador: Definir Contador\n javascriptGenerator.forBlock[\"definir_contador\"] = function (block) {\n const value =\n javascriptGenerator.valueToCode(\n block,\n \"VALUE\",\n javascriptGenerator.ORDER_ATOMIC,\n ) || \"''\";\n return \"definirContador(\" + value + \");\\n\";\n };\n\n // Gerador: Concatenar Saída\n javascriptGenerator.forBlock[\"concatenar_saida\"] = function (block) {\n const value =\n javascriptGenerator.valueToCode(\n block,\n \"VALUE\",\n javascriptGenerator.ORDER_ATOMIC,\n ) || \"''\";\n return \"concatenarSaida(\" + value + \");\\n\";\n };\n\n // Gerador: Obter Entrada\n javascriptGenerator.forBlock[\"obter_entrada\"] = function (block) {\n const code = \"obterEntrada()\";\n return [code, javascriptGenerator.ORDER_FUNCTION_CALL];\n };\n\n // Gerador: Obter Saída\n javascriptGenerator.forBlock[\"obter_saida\"] = function (block) {\n const code = \"obterSaida()\";\n return [code, javascriptGenerator.ORDER_FUNCTION_CALL];\n };\n\n // Gerador: Obter Contador\n javascriptGenerator.forBlock[\"obter_contador\"] = function (block) {\n const code = \"obterContador()\";\n return [code, javascriptGenerator.ORDER_FUNCTION_CALL];\n };\n\n // Gerador: Alfabeto\n javascriptGenerator.forBlock[\"alfabeto\"] = function (block) {\n const code = '\"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"';\n return [code, javascriptGenerator.ORDER_ATOMIC];\n };\n\n // Gerador: Alfabeto Secreto\n javascriptGenerator.forBlock[\"alfabeto_secreto\"] = function (block) {\n const code = '\"QWERTYUIOPASDFGHJKLZXCVBNM\"';\n return [code, javascriptGenerator.ORDER_ATOMIC];\n };\n\n // Gerador customizado: text_charAt (0-based, não subtrai 1)\n // Assume que todos os índices fornecidos já são 0-based (compatível com CONTADOR = 0)\n javascriptGenerator.forBlock[\"text_charAt\"] = function (block) {\n const text =\n javascriptGenerator.valueToCode(\n block,\n \"VALUE\",\n javascriptGenerator.ORDER_MEMBER,\n ) || \"''\";\n const at =\n javascriptGenerator.valueToCode(\n block,\n \"AT\",\n javascriptGenerator.ORDER_NONE,\n ) || \"0\";\n const code = text + \".charAt(\" + at + \")\";\n return [code, javascriptGenerator.ORDER_MEMBER];\n };\n\n // Gerador customizado: text_indexOf (0-based, não adiciona 1)\n // Retorna o índice 0-based direto, compatível com charAt e arrays JavaScript\n javascriptGenerator.forBlock[\"text_indexOf\"] = function (block) {\n const text =\n javascriptGenerator.valueToCode(\n block,\n \"VALUE\",\n javascriptGenerator.ORDER_MEMBER,\n ) || \"''\";\n const search =\n javascriptGenerator.valueToCode(\n block,\n \"FIND\",\n javascriptGenerator.ORDER_NONE,\n ) || \"''\";\n const code = text + \".indexOf(\" + search + \")\";\n return [code, javascriptGenerator.ORDER_MEMBER];\n };\n\n // ============ GERADORES PARA VARIÁVEIS CUSTOMIZADAS ============\n\n // Geradores: letra\n javascriptGenerator.forBlock[\"definir_letra\"] = function (block) {\n const value =\n javascriptGenerator.valueToCode(\n block,\n \"VALUE\",\n javascriptGenerator.ORDER_ATOMIC,\n ) || \"''\";\n return \"var letra = \" + value + \";\\n\";\n };\n\n javascriptGenerator.forBlock[\"obter_letra\"] = function (block) {\n return [\"letra\", javascriptGenerator.ORDER_ATOMIC];\n };\n\n // Geradores: posicao\n javascriptGenerator.forBlock[\"definir_posicao\"] = function (block) {\n const value =\n javascriptGenerator.valueToCode(\n block,\n \"VALUE\",\n javascriptGenerator.ORDER_ATOMIC,\n ) || \"0\";\n return \"var posicao = \" + value + \";\\n\";\n };\n\n javascriptGenerator.forBlock[\"obter_posicao\"] = function (block) {\n return [\"posicao\", javascriptGenerator.ORDER_ATOMIC];\n };\n\n // Geradores: nova_posicao\n javascriptGenerator.forBlock[\"definir_nova_posicao\"] = function (block) {\n const value =\n javascriptGenerator.valueToCode(\n block,\n \"VALUE\",\n javascriptGenerator.ORDER_ATOMIC,\n ) || \"0\";\n return \"var nova_posicao = \" + value + \";\\n\";\n };\n\n javascriptGenerator.forBlock[\"obter_nova_posicao\"] = function (block) {\n return [\"nova_posicao\", javascriptGenerator.ORDER_ATOMIC];\n };\n\n // Geradores: nova_letra\n javascriptGenerator.forBlock[\"definir_nova_letra\"] = function (block) {\n const value =\n javascriptGenerator.valueToCode(\n block,\n \"VALUE\",\n javascriptGenerator.ORDER_ATOMIC,\n ) || \"''\";\n return \"var nova_letra = \" + value + \";\\n\";\n };\n\n javascriptGenerator.forBlock[\"obter_nova_letra\"] = function (block) {\n return [\"nova_letra\", javascriptGenerator.ORDER_ATOMIC];\n };\n\n // Geradores: chave\n javascriptGenerator.forBlock[\"definir_chave\"] = function (block) {\n const value =\n javascriptGenerator.valueToCode(\n block,\n \"VALUE\",\n javascriptGenerator.ORDER_ATOMIC,\n ) || \"0\";\n return \"var chave = \" + value + \";\\n\";\n };\n\n javascriptGenerator.forBlock[\"obter_chave\"] = function (block) {\n return [\"chave\", javascriptGenerator.ORDER_ATOMIC];\n };\n\n // Geradores: letra_secreta\n javascriptGenerator.forBlock[\"definir_letra_secreta\"] = function (block) {\n const value =\n javascriptGenerator.valueToCode(\n block,\n \"VALUE\",\n javascriptGenerator.ORDER_ATOMIC,\n ) || \"''\";\n return \"var letra_secreta = \" + value + \";\\n\";\n };\n\n javascriptGenerator.forBlock[\"obter_letra_secreta\"] = function (block) {\n return [\"letra_secreta\", javascriptGenerator.ORDER_ATOMIC];\n };\n\n // Geradores: soma\n javascriptGenerator.forBlock[\"definir_soma\"] = function (block) {\n const value =\n javascriptGenerator.valueToCode(\n block,\n \"VALUE\",\n javascriptGenerator.ORDER_ATOMIC,\n ) || \"0\";\n return \"var soma = \" + value + \";\\n\";\n };\n\n javascriptGenerator.forBlock[\"obter_soma\"] = function (block) {\n return [\"soma\", javascriptGenerator.ORDER_ATOMIC];\n };\n};\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/cripto/config/codeValidations.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/cripto/config/config.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/cripto/config/debugSolutions.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/cripto/config/starterBlocks.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/cripto/config/tourSteps.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/cripto/game.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/cripto/hooks/interpreterSetup.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/cripto/hooks/useCriptoTour.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/cripto/ui/CRTMonitor.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/cripto/ui/GridBackground.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/cripto/ui/MatrixEffect.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/cripto/ui/animations.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/cripto/ui/constants.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/cripto/ui/index.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/cripto/ui/layout.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/cripto/validation/validators.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/mole-mash/MoleMash.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'React' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":13,"suggestions":[{"messageId":"removeVar","data":{"varName":"React"},"fix":{"range":[7,13],"text":""},"desc":"Remove unused variable 'React'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'GameBase' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":16,"suggestions":[{"messageId":"removeVar","data":{"varName":"GameBase"},"fix":{"range":[58,72],"text":""},"desc":"Remove unused variable 'GameBase'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'GameEditor' is defined but never used. Allowed unused vars must match /^_/u.","line":3,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":3,"endColumn":18,"suggestions":[{"messageId":"removeVar","data":{"varName":"GameEditor"},"fix":{"range":[113,129],"text":""},"desc":"Remove unused variable 'GameEditor'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'BlocklyEditor' is defined but never used. Allowed unused vars must match /^_/u.","line":4,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":4,"endColumn":21,"suggestions":[{"messageId":"removeVar","data":{"varName":"BlocklyEditor"},"fix":{"range":[172,191],"text":""},"desc":"Remove unused variable 'BlocklyEditor'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'GameStateProvider' is defined but never used. Allowed unused vars must match /^_/u.","line":9,"column":3,"nodeType":"Identifier","messageId":"unusedVar","endLine":9,"endColumn":20,"suggestions":[{"messageId":"removeVar","data":{"varName":"GameStateProvider"},"fix":{"range":[406,424],"text":""},"desc":"Remove unused variable 'GameStateProvider'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'MoleMashContent' is defined but never used. Allowed unused vars must match /^_/u.","line":17,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":17,"endColumn":25,"suggestions":[{"messageId":"removeVar","data":{"varName":"MoleMashContent"},"fix":{"range":[687,1370],"text":""},"desc":"Remove unused variable 'MoleMashContent'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":6,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import React, { useEffect, useMemo } from \"react\";\nimport GameBase from \"../../components/game/GameBase\";\nimport GameEditor from \"../../components/game/GameEditor\";\nimport BlocklyEditor from \"../../components/game/editors/BlocklyEditor\";\nimport { createGame } from \"./game\";\nimport { gameConfig } from \"./config/config\";\nimport { registerBlocks, generateDynamicToolbox } from \"./blocks/blocks\";\nimport {\n GameStateProvider,\n useGameState,\n} from \"../../contexts/GameStateContext\";\nimport { useMoleMashTour } from \"./hooks/useMoleMashTour\";\nimport { debugSolutions } from \"./config/debugSolutions\";\nimport \"shepherd.js/dist/css/shepherd.css\";\nimport \"../../styles/shepherd-theme.css\";\n\nfunction MoleMashContent() {\n const { startTour } = useMoleMashTour();\n const { setFailureMessage, isDebugMode } = useGameState();\n\n useEffect(() => {\n registerBlocks();\n }, []);\n\n const toolboxGenerator = useMemo(() => {\n return (allowedBlocks) => generateDynamicToolbox(allowedBlocks);\n }, []);\n\n return (\n <GameBase\n gameFactory={createGame}\n gameConfig={gameConfig}\n onHelpClick={startTour}\n customFailureHandler={setFailureMessage}\n >\n <GameEditor>\n <BlocklyEditor\n toolboxGenerator={toolboxGenerator}\n debugSolutions={isDebugMode ? debugSolutions : null}\n />\n </GameEditor>\n </GameBase>\n );\n}\n\nexport default function MoleMashGame() {\n return (\n <GameStateProvider gameConfig={gameConfig}>\n <MoleMashContent />\n </GameStateProvider>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/mole-mash/__tests__/integration.test.js","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'l' is defined but never used. Allowed unused args must match /^_/u.","line":227,"column":17,"nodeType":"Identifier","messageId":"unusedVar","endLine":227,"endColumn":18,"suggestions":[{"messageId":"removeVar","data":{"varName":"l"},"fix":{"range":[5669,5671],"text":""},"desc":"Remove unused variable 'l'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'c' is defined but never used. Allowed unused args must match /^_/u.","line":227,"column":20,"nodeType":"Identifier","messageId":"unusedVar","endLine":227,"endColumn":21,"suggestions":[{"messageId":"removeVar","data":{"varName":"c"},"fix":{"range":[5670,5673],"text":""},"desc":"Remove unused variable 'c'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'segundos' is defined but never used. Allowed unused args must match /^_/u.","line":238,"column":12,"nodeType":"Identifier","messageId":"unusedVar","endLine":238,"endColumn":20,"suggestions":[{"messageId":"removeVar","data":{"varName":"segundos"},"fix":{"range":[5962,5970],"text":""},"desc":"Remove unused variable 'segundos'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":3,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { describe, it, expect, beforeEach, vi } from \"vitest\";\nimport { GameInterpreter } from \"../../../interpreters/GameInterpreter\";\nimport { setupMoleMashAPI } from \"../hooks/interpreterSetup\";\nimport { validateSolution } from \"../validation/validators\";\n\n// Mock de soluções para teste (movido de config/solutions.js)\nconst SOLUTIONS = {\n fase1: `\n moverToupeira(1, 1);\n moverToupeira(1, 2);\n moverToupeira(1, 3);\n `,\n fase1_fail: `\n moverToupeira(1, 1);\n moverToupeira(1, 3);\n `,\n fase2: `\n moverToupeira(1, 1);\n moverToupeira(1, 3);\n moverToupeira(3, 3);\n moverToupeira(3, 1);\n `,\n fase2_fail: `\n moverToupeira(1, 1);\n moverToupeira(2, 2);\n `,\n fase3: `\n for (var count = 0; count < 5; count++) {\n moverToupeira(2, 2);\n moverToupeira(1, 2);\n }\n `,\n fase3_fail: `\n for (var count = 0; count < 5; count++) {\n moverToupeira(2, 2);\n moverToupeira(2, 2); \n }\n `,\n fase4: `\n var linha, coluna;\n linha = 3;\n for (var count2 = 0; count2 < 3; count2++) {\n coluna = 3;\n for (var count = 0; count < 3; count++) {\n moverToupeira(linha, coluna);\n coluna = coluna - 1;\n }\n linha = (typeof linha === 'number' ? linha : 0) + -1;\n }\n `,\n fase4_fail: `\n for (var i = 0; i < 10; i++) {\n moverToupeira(1, 1);\n moverToupeira(1, 2);\n }\n `,\n fase5: `\n function mathRandomInt(a, b) {\n if (a > b) { var c = a; a = b; b = c; }\n return Math.floor(Math.random() * (b - a + 1) + a);\n }\n for (var count = 0; count < 10; count++) {\n moverToupeira(2, (mathRandomInt(1, 3)));\n }\n `,\n fase5_fail: `\n for (var count = 0; count < 5; count++) {\n moverToupeira(1, 2);\n }\n `,\n fase6: `\n function mathRandomInt(a, b) {\n if (a > b) { var c = a; a = b; b = c; }\n return Math.floor(Math.random() * (b - a + 1) + a);\n }\n for (var count = 0; count < 20; count++) {\n moverToupeira(mathRandomInt(1, 3), mathRandomInt(1, 3));\n }\n `,\n fase7: `\n var x, y;\n function mathRandomInt(a, b) {\n if (a > b) { var c = a; a = b; b = c; }\n return Math.floor(Math.random() * (b - a + 1) + a);\n }\n for (var count = 0; count < 20; count++) {\n x = mathRandomInt(1, 10);\n y = mathRandomInt(1, 3);\n if (x <= 5) {\n moverToupeira(1, y);\n } else {\n moverToupeira(3, y);\n }\n }\n `,\n fase8: `\n var linha_atual, coluna, ultima_linha;\n function mathRandomInt(a, b) {\n if (a > b) { var c = a; a = b; b = c; }\n return Math.floor(Math.random() * (b - a + 1) + a);\n }\n for (var count = 0; count < 10; count++) {\n linha_atual = mathRandomInt(1, 1);\n coluna = mathRandomInt(1, 3);\n if (linha_atual == ultima_linha) {\n moverToupeira(2, coluna);\n } else {\n moverToupeira(linha_atual, coluna);\n }\n ultima_linha = linha_atual;\n }\n `,\n fase8_fail: `\n moverToupeira(1, 1);\n moverToupeira(1, 2);\n `,\n fase9: `\n var linha, coluna, ultima_linha, ultima_coluna;\n function mathRandomInt(a, b) {\n if (a > b) { var c = a; a = b; b = c; }\n return Math.floor(Math.random() * (b - a + 1) + a);\n }\n for (var count = 0; count < 30; count++) {\n linha = mathRandomInt(1, 3);\n coluna = mathRandomInt(1, 3);\n while (linha == ultima_linha && coluna == ultima_coluna) {\n linha = mathRandomInt(1, 3);\n coluna = mathRandomInt(1, 3);\n }\n moverToupeira(linha, coluna);\n ultima_linha = linha;\n ultima_coluna = coluna;\n }\n `,\n fase10: `\n var l, c, ult_l, ult_c;\n function mathRandomInt(a, b) {\n if (a > b) { var c = a; a = b; b = c; }\n return Math.floor(Math.random() * (b - a + 1) + a);\n }\n for (var count = 0; count < 30; count++) {\n l = mathRandomInt(1, 3);\n c = mathRandomInt(1, 3);\n while (l == ult_l && c == ult_c) {\n l = mathRandomInt(1, 3);\n c = mathRandomInt(1, 3);\n }\n if (ult_l == 2) {\n if (mathRandomInt(1, 2) == 1) {\n l = 1;\n } else {\n l = 3;\n }\n }\n moverToupeira(l, c);\n ult_l = l;\n ult_c = c;\n aguardar(0.1);\n }\n `,\n fase10_fail: `\n moverToupeira(2, 1);\n moverToupeira(2, 3);\n `,\n};\n\n// MOCK DA CONFIGURAÇÃO (Para não depender do gameConfig externo)\n// Replicamos aqui apenas as regras que o validador consome.\nconst MOCK_GAME_CONFIG = {\n mensagens: {\n semMovimento: \"Sem mov\",\n foraTabuleiro: \"Fora!\",\n caminhoErrado: \"Caminho errado\",\n faltamSaltos: \"Falta salto\",\n saltoInsuficiente: \"Insuficiente\",\n saltoErrado: \"Padrão errado no salto {numero}: esperado {local}\",\n buracosFaltando: \"Não visitou tudo ({visitados} visitados)\",\n linhaErrada: \"Linha errada\",\n repeticaoLinha: \"Repetiu linha\",\n mesmoLugar: \"Mesmo lugar\",\n linhaCentral: \"Centro repetido\",\n },\n fases: [\n {\n id: 1,\n expectedSequence: [\n { l: 1, c: 1 },\n { l: 1, c: 2 },\n { l: 1, c: 3 },\n ],\n },\n {\n id: 2,\n expectedSequence: [\n { l: 1, c: 1 },\n { l: 1, c: 3 },\n { l: 3, c: 3 },\n { l: 3, c: 1 },\n ],\n },\n { id: 3 },\n { id: 4 },\n { id: 5 },\n { id: 6 },\n { id: 7 },\n { id: 8 },\n { id: 9 },\n { id: 10 },\n ],\n};\n\n// CENA HEADLESS (Simula o Phaser sem gráficos)\nclass HeadlessMoleMashScene {\n constructor() {\n this.historico = [];\n this.toupeira = {\n setPosition: vi.fn(),\n setVisible: vi.fn(),\n play: vi.fn(),\n scene: true,\n };\n // Mock do som\n this.sound = { play: vi.fn() };\n }\n\n // Utilitário usado pela API\n getGridPixels(l, c) {\n return { x: 0, y: 0 };\n }\n\n // Implementação da API Visual dentro da cena\n moverToupeira(linha, coluna) {\n this.historico.push({ l: linha, c: coluna, t: Date.now() });\n this.toupeira.setPosition(0, 0);\n }\n\n // Simula aguardar (resolve imediatamente no teste)\n aguardar(segundos) {\n return Promise.resolve();\n }\n}\n\ndescribe(\"Mole Mash - Integração de Lógica (Código -> Validação)\", () => {\n let scene;\n let interpreter;\n\n beforeEach(() => {\n scene = new HeadlessMoleMashScene();\n // StepDelay 0 para rodar instantâneo\n interpreter = new GameInterpreter({ stepDelay: 0, pauseExec: false });\n });\n\n const runFlow = async (code, phaseId) => {\n const api = setupMoleMashAPI(scene);\n\n // Executa o código (popula scene.historico)\n await interpreter.executeCode(code, api);\n\n const configFase = MOCK_GAME_CONFIG.fases.find((f) => f.id === phaseId);\n\n // Valida o histórico gerado\n return validateSolution(scene.historico, configFase, MOCK_GAME_CONFIG);\n };\n\n // --- TESTES DE FASES ---\n\n it(\"Fase 1: Deve aprovar solução correta\", async () => {\n const result = await runFlow(SOLUTIONS.fase1, 1);\n expect(result.success).toBe(true);\n });\n\n it(\"Fase 1: Deve reprovar solução errada\", async () => {\n const result = await runFlow(SOLUTIONS.fase1_fail, 1);\n expect(result.success).toBe(false);\n expect(result.reason).toBe(MOCK_GAME_CONFIG.mensagens.caminhoErrado);\n });\n\n it(\"Fase 2: Deve aprovar solução correta (Cantos)\", async () => {\n const result = await runFlow(SOLUTIONS.fase2, 2);\n expect(result.success).toBe(true);\n });\n\n it(\"Fase 2: Deve reprovar solução errada\", async () => {\n const result = await runFlow(SOLUTIONS.fase2_fail, 2);\n expect(result.success).toBe(false);\n });\n\n it(\"Fase 3: Deve aprovar loop correto\", async () => {\n const result = await runFlow(SOLUTIONS.fase3, 3);\n expect(result.success).toBe(true);\n expect(scene.historico.length).toBe(10);\n });\n\n it(\"Fase 3: Deve reprovar loop incorreto\", async () => {\n const result = await runFlow(SOLUTIONS.fase3_fail, 3);\n expect(result.success).toBe(false);\n expect(result.reason).toContain(\"Padrão errado\");\n });\n\n it(\"Fase 4: Deve aprovar varredura completa (Nested Loops)\", async () => {\n const result = await runFlow(SOLUTIONS.fase4, 4);\n expect(result.success).toBe(true);\n });\n\n it(\"Fase 4: Deve reprovar varredura incompleta\", async () => {\n const result = await runFlow(SOLUTIONS.fase4_fail, 4);\n expect(result.success).toBe(false);\n expect(result.reason).toContain(\"Não visitou tudo\");\n });\n\n it(\"Fase 5: Deve aprovar aleatório na linha 2\", async () => {\n const result = await runFlow(SOLUTIONS.fase5, 5);\n expect(result.success).toBe(true);\n });\n\n it(\"Fase 5: Deve reprovar linha errada\", async () => {\n const result = await runFlow(SOLUTIONS.fase5_fail, 5);\n expect(result.success).toBe(false);\n expect(result.reason).toBe(MOCK_GAME_CONFIG.mensagens.linhaErrada);\n });\n\n // Fase 6 e 7 e 9 não possuem validadores específicos complexos no momento,\n // validam apenas sanidade e limites. Testamos apenas o sucesso da execução.\n it(\"Fase 6: Deve executar com sucesso (Aleatório Total)\", async () => {\n const result = await runFlow(SOLUTIONS.fase6, 6);\n expect(result.success).toBe(true);\n });\n\n it(\"Fase 7: Deve executar com sucesso (Condicionais)\", async () => {\n const result = await runFlow(SOLUTIONS.fase7, 7);\n expect(result.success).toBe(true);\n });\n\n it(\"Fase 8: Deve aprovar lógica de não repetição de linha\", async () => {\n const result = await runFlow(SOLUTIONS.fase8, 8);\n expect(result.success).toBe(true);\n });\n\n it(\"Fase 8: Deve reprovar repetição explícita\", async () => {\n const result = await runFlow(SOLUTIONS.fase8_fail, 8);\n expect(result.success).toBe(false);\n expect(result.reason).toBe(MOCK_GAME_CONFIG.mensagens.repeticaoLinha);\n });\n\n it(\"Fase 9: Deve executar com sucesso (While loop)\", async () => {\n const result = await runFlow(SOLUTIONS.fase9, 9);\n expect(result.success).toBe(true);\n });\n\n it(\"Fase 10: Deve aprovar lógica complexa\", async () => {\n const result = await runFlow(SOLUTIONS.fase10, 10);\n expect(result.success).toBe(true);\n });\n\n it(\"Fase 10: Deve reprovar lógica errada (Centro Repetido)\", async () => {\n const result = await runFlow(SOLUTIONS.fase10_fail, 10);\n expect(result.success).toBe(false);\n expect(result.reason).toBe(MOCK_GAME_CONFIG.mensagens.linhaCentral);\n });\n}, 60000);\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/mole-mash/blocks/blocks.js","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'index' is defined but never used. Allowed unused args must match /^_/u.","line":261,"column":16,"nodeType":"Identifier","messageId":"unusedVar","endLine":261,"endColumn":21,"suggestions":[{"messageId":"removeVar","data":{"varName":"index"},"fix":{"range":[6792,6799],"text":""},"desc":"Remove unused variable 'index'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import * as Blockly from \"blockly/core\";\nimport \"blockly/blocks\";\nimport { javascriptGenerator } from \"blockly/javascript\";\n\nconst HUE_CONDICIONAIS = \"#5B80A5\";\nconst HUE_MOVIMENTO = \"#8B4513\";\nconst HUE_LOGICA = \"#59C059\";\nconst HUE_MATEMATICA = \"#8BC34A\";\nconst HUE_VARIAVEIS = \"#9C27B0\";\nconst HUE_REPETICAO = \"#FF9800\";\nconst HUE_TEMPO = \"#FF5722\";\n\nexport const registerBlocks = () => {\n defineBlocks();\n defineGenerators();\n};\n\nconst defineBlocks = () => {\n Blockly.Blocks[\"mover_toupeira\"] = {\n init: function () {\n this.jsonInit({\n message0: \"mover para Linha %1 Coluna %2\",\n args0: [\n { type: \"input_value\", name: \"LINHA\", check: \"Number\" },\n { type: \"input_value\", name: \"COLUNA\", check: \"Number\" },\n ],\n inputsInline: true,\n previousStatement: null,\n nextStatement: null,\n colour: HUE_MOVIMENTO,\n tooltip:\n \"Move a toupeira para uma posição específica (1 a 3) na matriz.\",\n });\n },\n };\n\n Blockly.Blocks[\"aguardar\"] = {\n init: function () {\n this.jsonInit({\n message0: \"aguardar %1 segundos\",\n args0: [\n {\n type: \"field_number\",\n name: \"TEMPO\",\n value: 1,\n min: 0,\n max: 10,\n },\n ],\n previousStatement: null,\n nextStatement: null,\n colour: HUE_TEMPO,\n tooltip: \"Pausa a execução antes do próximo movimento.\",\n });\n },\n };\n\n Blockly.Blocks[\"repetir_sempre\"] = {\n init: function () {\n this.appendDummyInput().appendField(\"repetir para sempre\");\n this.appendStatementInput(\"STACK\").setCheck(null).appendField(\"faça\");\n this.setPreviousStatement(true, null);\n this.setColour(HUE_REPETICAO);\n this.setTooltip(\"Executa os blocos dentro dele em um ciclo infinito.\");\n },\n };\n};\n\nconst defineGenerators = () => {\n javascriptGenerator.STATEMENT_PREFIX = \"highlightBlock(%1);\\n\";\n javascriptGenerator.addReservedWords(\"highlightBlock\");\n\n javascriptGenerator.forBlock[\"mover_toupeira\"] = (block) => {\n const linha =\n javascriptGenerator.valueToCode(\n block,\n \"LINHA\",\n javascriptGenerator.ORDER_ATOMIC,\n ) || \"1\";\n const coluna =\n javascriptGenerator.valueToCode(\n block,\n \"COLUNA\",\n javascriptGenerator.ORDER_ATOMIC,\n ) || \"1\";\n return `moverToupeira(${linha}, ${coluna});\\n`;\n };\n\n javascriptGenerator.forBlock[\"aguardar\"] = (block) => {\n const tempo = block.getFieldValue(\"TEMPO\");\n return `aguardar(${tempo * 1000});\\n`;\n };\n\n javascriptGenerator.forBlock[\"repetir_sempre\"] = (block) => {\n const branch = javascriptGenerator.statementToCode(block, \"STACK\");\n return `while (true) {\\n${branch} await esperar(10); // Segurança para não travar\\n}\\n`;\n };\n};\n\nexport const generateDynamicToolbox = (allowedBlocks = []) => {\n const blockDefinitions = {\n // Movimento\n mover_toupeira: {\n kind: \"block\",\n type: \"mover_toupeira\",\n },\n // Repetição\n repetir_sempre: {\n kind: \"block\",\n type: \"repetir_sempre\",\n },\n controls_repeat_ext: {\n kind: \"block\",\n type: \"controls_repeat_ext\",\n },\n controls_whileUntil: {\n kind: \"block\",\n type: \"controls_whileUntil\",\n },\n // Tempo\n aguardar: {\n kind: \"block\",\n type: \"aguardar\",\n },\n // Lógica\n logic_compare: {\n kind: \"block\",\n type: \"logic_compare\",\n },\n logic_operation: {\n kind: \"block\",\n type: \"logic_operation\",\n },\n logic_negate: {\n kind: \"block\",\n type: \"logic_negate\",\n },\n logic_boolean: {\n kind: \"block\",\n type: \"logic_boolean\",\n },\n controls_if: {\n kind: \"block\",\n type: \"controls_if\",\n },\n controls_ifelse: {\n kind: \"block\",\n type: \"controls_ifelse\",\n },\n // Matemática\n math_number: {\n kind: \"block\",\n type: \"math_number\",\n },\n math_arithmetic: {\n kind: \"block\",\n type: \"math_arithmetic\",\n },\n math_random_int: {\n kind: \"block\",\n type: \"math_random_int\",\n inputs: {\n FROM: { shadow: { type: \"math_number\", fields: { NUM: 1 } } },\n TO: { shadow: { type: \"math_number\", fields: { NUM: 3 } } },\n },\n },\n };\n\n const toolboxContents = {\n kind: \"categoryToolbox\",\n contents: [\n {\n kind: \"category\",\n name: \"Movimento\",\n colour: HUE_MOVIMENTO,\n contents: [],\n cssConfig: { container: \"movimento\" },\n },\n {\n kind: \"category\",\n name: \"Repetição\",\n colour: HUE_REPETICAO,\n contents: [],\n cssConfig: { container: \"repeticao\" },\n },\n {\n kind: \"category\",\n name: \"Lógica\",\n colour: HUE_LOGICA,\n contents: [],\n cssConfig: { container: \"logica\" },\n },\n {\n kind: \"category\",\n name: \"Condicionais\",\n colour: HUE_CONDICIONAIS,\n contents: [],\n cssConfig: { container: \"condicionais\" },\n },\n {\n kind: \"category\",\n name: \"Matemática\",\n colour: HUE_MATEMATICA,\n contents: [],\n cssConfig: { container: \"matematica\" },\n },\n {\n kind: \"category\",\n name: \"Tempo\",\n colour: HUE_TEMPO,\n contents: [],\n cssConfig: { container: \"tempo\" },\n },\n {\n kind: \"category\",\n name: \"Variáveis\",\n colour: HUE_VARIAVEIS,\n custom: \"VARIABLE\",\n cssConfig: { container: \"variaveis\" },\n },\n ],\n };\n\n allowedBlocks.forEach((blockId) => {\n const blockDef = blockDefinitions[blockId];\n\n if (blockId === \"mover_toupeira\") {\n toolboxContents.contents[0].contents.push(blockDef);\n } else if (\n [\"repetir_sempre\", \"controls_repeat_ext\", \"controls_whileUntil\"].includes(\n blockId,\n )\n ) {\n toolboxContents.contents[1].contents.push(blockDef);\n } else if (\n [\n \"logic_compare\",\n \"logic_operation\",\n \"logic_negate\",\n \"logic_boolean\",\n ].includes(blockId)\n ) {\n toolboxContents.contents[2].contents.push(blockDef);\n } else if ([\"controls_if\", \"controls_ifelse\"].includes(blockId)) {\n toolboxContents.contents[3].contents.push(blockDef);\n } else if (\n [\"math_number\", \"math_random_int\", \"math_arithmetic\"].includes(blockId)\n ) {\n toolboxContents.contents[4].contents.push(blockDef);\n } else if (blockId === \"aguardar\") {\n toolboxContents.contents[5].contents.push(blockDef);\n }\n });\n\n const hasVariables =\n allowedBlocks.includes(\"variables_set\") ||\n allowedBlocks.includes(\"variables_get\") ||\n allowedBlocks.includes(\"variables\");\n\n toolboxContents.contents = toolboxContents.contents.filter(\n (category, index) => {\n if (category.custom === \"VARIABLE\") {\n return hasVariables;\n }\n return category.contents && category.contents.length > 0;\n },\n );\n\n return toolboxContents;\n};\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/mole-mash/config/config.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/mole-mash/config/debugSolutions.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/mole-mash/config/tourSteps.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/mole-mash/game.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/mole-mash/hooks/interpreterSetup.js","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'options' is assigned a value but never used. Allowed unused args must match /^_/u.","line":3,"column":49,"nodeType":"Identifier","messageId":"unusedVar","endLine":3,"endColumn":56,"suggestions":[{"messageId":"removeVar","data":{"varName":"options"},"fix":{"range":[113,127],"text":""},"desc":"Remove unused variable 'options'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ApiHelpers } from \"../../../interpreters/ApiHelpers.js\";\n\nexport const setupMoleMashAPI = (sceneInstance, options = {}) => {\n return (interpreter, globalObject) => {\n const highlightWrapper = function (id, callback) {\n sceneInstance.highlightBlock(id).then(callback);\n };\n\n const moverToupeiraWrapper = function (linha, coluna, callback) {\n sceneInstance.moverToupeira(linha, coluna);\n setTimeout(callback, 800);\n };\n\n const aguardarWrapper = function (ms, callback) {\n setTimeout(callback, ms);\n };\n\n ApiHelpers.registerMultipleFunctions(interpreter, globalObject, [\n { name: \"highlightBlock\", wrapper: highlightWrapper, isAsync: true },\n { name: \"moverToupeira\", wrapper: moverToupeiraWrapper, isAsync: true },\n { name: \"aguardar\", wrapper: aguardarWrapper, isAsync: true },\n ]);\n };\n};\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/mole-mash/hooks/useMoleMashTour.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/mole-mash/validation/validators.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/semaforo/SemaforoGame.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'React' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":13,"suggestions":[{"messageId":"removeVar","data":{"varName":"React"},"fix":{"range":[7,13],"text":""},"desc":"Remove unused variable 'React'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'GameBase' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":16,"suggestions":[{"messageId":"removeVar","data":{"varName":"GameBase"},"fix":{"range":[58,72],"text":""},"desc":"Remove unused variable 'GameBase'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'GameEditor' is defined but never used. Allowed unused vars must match /^_/u.","line":3,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":3,"endColumn":18,"suggestions":[{"messageId":"removeVar","data":{"varName":"GameEditor"},"fix":{"range":[113,129],"text":""},"desc":"Remove unused variable 'GameEditor'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'BlocklyEditor' is defined but never used. Allowed unused vars must match /^_/u.","line":4,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":4,"endColumn":21,"suggestions":[{"messageId":"removeVar","data":{"varName":"BlocklyEditor"},"fix":{"range":[172,191],"text":""},"desc":"Remove unused variable 'BlocklyEditor'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'GameStateProvider' is defined but never used. Allowed unused vars must match /^_/u.","line":9,"column":3,"nodeType":"Identifier","messageId":"unusedVar","endLine":9,"endColumn":20,"suggestions":[{"messageId":"removeVar","data":{"varName":"GameStateProvider"},"fix":{"range":[406,424],"text":""},"desc":"Remove unused variable 'GameStateProvider'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'SemaforoContent' is defined but never used. Allowed unused vars must match /^_/u.","line":17,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":17,"endColumn":25,"suggestions":[{"messageId":"removeVar","data":{"varName":"SemaforoContent"},"fix":{"range":[687,1370],"text":""},"desc":"Remove unused variable 'SemaforoContent'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":6,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import React, { useEffect, useMemo } from \"react\";\nimport GameBase from \"../../components/game/GameBase\";\nimport GameEditor from \"../../components/game/GameEditor\";\nimport BlocklyEditor from \"../../components/game/editors/BlocklyEditor\";\nimport { createGame } from \"./game\";\nimport { gameConfig } from \"./config/config\";\nimport { generateDynamicToolbox, registerBlocks } from \"./blocks/blocks\";\nimport {\n GameStateProvider,\n useGameState,\n} from \"../../contexts/GameStateContext\";\nimport { useSemaforoTour } from \"./hooks/useSemaforoTour\";\nimport { debugSolutions } from \"./config/debugSolutions\";\nimport \"shepherd.js/dist/css/shepherd.css\";\nimport \"../../styles/shepherd-theme.css\";\n\nfunction SemaforoContent() {\n const { startTour } = useSemaforoTour();\n const { setFailureMessage, isDebugMode } = useGameState();\n\n useEffect(() => {\n registerBlocks();\n }, []);\n\n const toolboxGenerator = useMemo(() => {\n return (allowedBlocks) => generateDynamicToolbox(allowedBlocks);\n }, []);\n\n return (\n <GameBase\n gameFactory={createGame}\n gameConfig={gameConfig}\n onHelpClick={startTour}\n customFailureHandler={setFailureMessage}\n >\n <GameEditor>\n <BlocklyEditor\n toolboxGenerator={toolboxGenerator}\n debugSolutions={isDebugMode ? debugSolutions : null}\n />\n </GameEditor>\n </GameBase>\n );\n}\n\nexport default function SemaforoGame() {\n return (\n <GameStateProvider gameConfig={gameConfig}>\n <SemaforoContent />\n </GameStateProvider>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/semaforo/__tests__/integration.test.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/semaforo/blocks/blocks.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/semaforo/config/config.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/semaforo/config/debugSolutions.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/semaforo/config/tourSteps.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/semaforo/game.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/semaforo/hooks/interpreterSetup.js","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'config' is assigned a value but never used. Allowed unused args must match /^_/u.","line":3,"column":41,"nodeType":"Identifier","messageId":"unusedVar","endLine":3,"endColumn":47,"suggestions":[{"messageId":"removeVar","data":{"varName":"config"},"fix":{"range":[105,118],"text":""},"desc":"Remove unused variable 'config'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ApiHelpers } from \"../../../interpreters/ApiHelpers.js\";\n\nexport const setupSemaforoAPI = (scene, config = {}) => {\n const getAnimationDelay = () => {\n if (scene.executionSpeed >= 75) return 1;\n if (scene.executionSpeed >= 50) return 2;\n if (scene.executionSpeed >= 25) return 5;\n return 10;\n };\n\n return (interpreter, globalScope) => {\n ApiHelpers.registerFunction(\n interpreter,\n globalScope,\n \"mudarSemaforo\",\n ApiHelpers.createActionWrapper(\n scene,\n \"mudarSemaforoCarros\",\n getAnimationDelay(),\n ),\n true,\n );\n\n ApiHelpers.registerFunction(\n interpreter,\n globalScope,\n \"mudarSemaforoPedestre\",\n ApiHelpers.createActionWrapper(\n scene,\n \"mudarSemaforoPedestre\",\n getAnimationDelay(),\n ),\n true,\n );\n\n ApiHelpers.registerFunction(\n interpreter,\n globalScope,\n \"aguardarSegundos\",\n ApiHelpers.createActionWrapper(scene, \"aguardarSegundos\"),\n true,\n );\n\n ApiHelpers.registerFunction(\n interpreter,\n globalScope,\n \"piscarLuzPedestre\",\n ApiHelpers.createActionWrapper(scene, \"piscarLuzPedestre\"),\n true,\n );\n\n ApiHelpers.registerFunction(\n interpreter,\n globalScope,\n \"tocarSom\",\n ApiHelpers.createActionWrapper(scene, \"tocarSom\"),\n true,\n );\n\n ApiHelpers.registerFunction(\n interpreter,\n globalScope,\n \"pararSom\",\n ApiHelpers.createActionWrapper(scene, \"pararSom\"),\n true,\n );\n\n ApiHelpers.registerFunction(\n interpreter,\n globalScope,\n \"highlightBlock\",\n ApiHelpers.createHighlightWrapper(scene),\n false,\n );\n };\n};\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/semaforo/hooks/useSemaforoTour.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/semaforo/validation/validators.js","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'sceneRef' is defined but never used. Allowed unused args must match /^_/u.","line":32,"column":46,"nodeType":"Identifier","messageId":"unusedVar","endLine":32,"endColumn":54,"suggestions":[{"messageId":"removeVar","data":{"varName":"sceneRef"},"fix":{"range":[1299,1309],"text":""},"desc":"Remove unused variable 'sceneRef'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { BaseGameValidator } from \"../../../shared/BaseGameValidator\";\n\n/**\n * Validador para o jogo Semáforo\n *\n * @class SemaforoValidator\n * @extends BaseGameValidator\n *\n * @description\n * Responsável por validar as ações do usuário em cada fase do jogo.\n * Utiliza validação baseada em histórico de comandos executados,\n * verificando sequências de cores, tempos de espera, sincronização\n * de semáforos, piscar de luzes e controle de sons.\n *\n * Validações por fase:\n * - Fase 1: Sequência básica de cores (verde → amarelo → vermelho)\n * - Fase 2: Sequência com pausas entre mudanças\n * - Fase 3: Sincronização de semáforos de carros e pedestres\n * - Fase 4: Adiciona piscar da luz de pedestre\n * - Fase 5: Adiciona controle de sons\n */\nexport class SemaforoValidator extends BaseGameValidator {\n /**\n * Método principal de validação - delega para métodos específicos por fase\n *\n * @param {Array<Object>} history - Histórico de comandos executados\n * @param {Object} config - Configuração da fase atual\n * @param {Object} gameConfig - Configuração geral do jogo (inclui mensagens)\n * @param {Object} sceneRef - Referência à cena do jogo (opcional)\n * @returns {Object} Resultado da validação {success: boolean, reason?: string}\n */\n validatePhase(history, config, gameConfig, sceneRef) {\n // Verificar se tem histórico\n if (!history || history.length === 0) {\n return this.failure(\n gameConfig.mensagens?.semComandos || \"Nenhum comando executado\",\n );\n }\n\n // Roteamento por fase\n const phaseMethod = this[`validateFase${config.id}`];\n if (phaseMethod) {\n return phaseMethod.call(this, history, config, gameConfig);\n }\n\n return this.success();\n }\n\n /**\n * Fase 1: Sequência básica de cores do semáforo\n * Valida: verde → amarelo → vermelho\n *\n * @param {Array<Object>} history - Histórico de comandos\n * @param {Object} config - Configuração da fase (contém expectedSequence)\n * @param {Object} gameConfig - Config do jogo (contém mensagens)\n * @returns {Object} Resultado da validação\n *\n * @note Fase 1 injeta automaticamente aguardarSegundos(2) entre cada mudança\n * para melhorar a visualização, então ignoramos comandos 'aguardar' na validação\n */\n validateFase1(history, config, gameConfig) {\n // Extrair apenas comandos de semáforo (ignorar aguardares automáticos)\n const cores = history\n .filter((h) => h.tipo === \"semaforo\")\n .map((h) => h.cor);\n\n const esperado = config.expectedSequence || [\n \"vermelho\",\n \"amarelo\",\n \"verde\",\n ];\n\n if (cores.length !== esperado.length) {\n return this.failure(\n gameConfig.mensagens?.sequenciaIncorreta || \"Sequência incompleta\",\n );\n }\n\n const match = cores.every((cor, i) => cor === esperado[i]);\n return match\n ? this.success()\n : this.failure(\n gameConfig.mensagens?.sequenciaIncorreta ||\n \"Sequência de cores incorreta\",\n );\n }\n\n /**\n * Fase 2: Sequência com aguardar\n * Valida: sequência + pausas entre mudanças\n */\n validateFase2(history, config, gameConfig) {\n const esperado = config.expectedCommands;\n\n if (history.length !== esperado.length) {\n return this.failure(\n gameConfig.mensagens?.faltaAguardar ||\n \"Comandos faltando ou em excesso\",\n );\n }\n\n for (let i = 0; i < esperado.length; i++) {\n if (history[i].tipo !== esperado[i].tipo) {\n return this.failure(\n gameConfig.mensagens?.sequenciaIncorreta ||\n \"Sequência de comandos incorreta\",\n );\n }\n\n if (\n esperado[i].tipo === \"semaforo\" &&\n history[i].cor !== esperado[i].cor\n ) {\n return this.failure(\n gameConfig.mensagens?.sequenciaIncorreta ||\n \"Cor do semáforo incorreta\",\n );\n }\n }\n\n return this.success();\n }\n\n /**\n * Fase 3: Carros + pedestres\n * Valida: sincronização entre semáforos de carros e pedestres\n */\n validateFase3(history, config, gameConfig) {\n const esperado = config.expectedCommands;\n\n if (history.length !== esperado.length) {\n return this.failure(\n gameConfig.mensagens?.pedestreErrado ||\n \"Comandos faltando ou em excesso\",\n );\n }\n\n for (let i = 0; i < esperado.length; i++) {\n if (history[i].tipo !== esperado[i].tipo) {\n return this.failure(\n gameConfig.mensagens?.sequenciaIncorreta ||\n \"Tipo de comando incorreto\",\n );\n }\n\n if (\n (history[i].tipo === \"semaforo\" || history[i].tipo === \"pedestre\") &&\n history[i].cor !== esperado[i].cor\n ) {\n return this.failure(\n gameConfig.mensagens?.pedestreErrado || \"Cor incorreta\",\n );\n }\n\n if (\n history[i].tipo === \"aguardar\" &&\n history[i].seg !== esperado[i].seg\n ) {\n return this.failure(\n gameConfig.mensagens?.faltaAguardar || \"Tempo de espera incorreto\",\n );\n }\n }\n\n return this.success();\n }\n\n /**\n * Fase 4: + piscar\n * Valida: inclui piscar da luz do pedestre\n */\n validateFase4(history, config, gameConfig) {\n const esperado = config.expectedCommands;\n\n if (history.length !== esperado.length) {\n return this.failure(\n gameConfig.mensagens?.semPiscar || \"Comandos faltando ou em excesso\",\n );\n }\n\n for (let i = 0; i < esperado.length; i++) {\n if (history[i].tipo !== esperado[i].tipo) {\n return this.failure(\n gameConfig.mensagens?.sequenciaIncorreta ||\n \"Tipo de comando incorreto\",\n );\n }\n\n if (\n (history[i].tipo === \"semaforo\" ||\n history[i].tipo === \"pedestre\" ||\n history[i].tipo === \"piscar\") &&\n history[i].cor !== esperado[i].cor\n ) {\n return this.failure(gameConfig.mensagens?.semPiscar || \"Cor incorreta\");\n }\n\n if (\n (history[i].tipo === \"aguardar\" || history[i].tipo === \"piscar\") &&\n history[i].seg !== esperado[i].seg\n ) {\n return this.failure(\n gameConfig.mensagens?.faltaAguardar || \"Tempo incorreto\",\n );\n }\n }\n\n return this.success();\n }\n\n /**\n * Fase 5: + sons\n * Valida: inclui controle de sons (tocar e parar)\n */\n validateFase5(history, config, gameConfig) {\n const esperado = config.expectedCommands;\n\n if (history.length !== esperado.length) {\n return this.failure(\n gameConfig.mensagens?.semSom || \"Comandos faltando ou em excesso\",\n );\n }\n\n for (let i = 0; i < esperado.length; i++) {\n if (history[i].tipo !== esperado[i].tipo) {\n return this.failure(\n gameConfig.mensagens?.sequenciaIncorreta ||\n \"Tipo de comando incorreto\",\n );\n }\n\n if (\n (history[i].tipo === \"semaforo\" ||\n history[i].tipo === \"pedestre\" ||\n history[i].tipo === \"piscar\") &&\n history[i].cor !== esperado[i].cor\n ) {\n return this.failure(\n gameConfig.mensagens?.sequenciaIncorreta || \"Cor incorreta\",\n );\n }\n\n if (\n (history[i].tipo === \"aguardar\" || history[i].tipo === \"piscar\") &&\n history[i].seg !== esperado[i].seg\n ) {\n return this.failure(\n gameConfig.mensagens?.faltaAguardar || \"Tempo incorreto\",\n );\n }\n\n if (\n (history[i].tipo === \"tocar\" || history[i].tipo === \"parar\") &&\n history[i].som !== esperado[i].som\n ) {\n return this.failure(gameConfig.mensagens?.semSom || \"Som incorreto\");\n }\n }\n\n return this.success();\n }\n}\n\n/**\n * Função de validação exportada para uso no gameController\n */\nexport function validateSolution(history, config, gameConfig, sceneRef) {\n const validator = new SemaforoValidator();\n return validator.validatePhase(history, config, gameConfig, sceneRef);\n}\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/turtle/TurtleGame.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'React' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":13,"suggestions":[{"messageId":"removeVar","data":{"varName":"React"},"fix":{"range":[7,13],"text":""},"desc":"Remove unused variable 'React'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'GameBase' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":16,"suggestions":[{"messageId":"removeVar","data":{"varName":"GameBase"},"fix":{"range":[58,72],"text":""},"desc":"Remove unused variable 'GameBase'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'GameEditor' is defined but never used. Allowed unused vars must match /^_/u.","line":3,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":3,"endColumn":18,"suggestions":[{"messageId":"removeVar","data":{"varName":"GameEditor"},"fix":{"range":[113,129],"text":""},"desc":"Remove unused variable 'GameEditor'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'BlocklyEditor' is defined but never used. Allowed unused vars must match /^_/u.","line":4,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":4,"endColumn":21,"suggestions":[{"messageId":"removeVar","data":{"varName":"BlocklyEditor"},"fix":{"range":[172,191],"text":""},"desc":"Remove unused variable 'BlocklyEditor'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'CodeEditor' is defined but never used. Allowed unused vars must match /^_/u.","line":5,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":5,"endColumn":18,"suggestions":[{"messageId":"removeVar","data":{"varName":"CodeEditor"},"fix":{"range":[245,261],"text":""},"desc":"Remove unused variable 'CodeEditor'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'GameStateProvider' is defined but never used. Allowed unused vars must match /^_/u.","line":14,"column":3,"nodeType":"Identifier","messageId":"unusedVar","endLine":14,"endColumn":20,"suggestions":[{"messageId":"removeVar","data":{"varName":"GameStateProvider"},"fix":{"range":[495,513],"text":""},"desc":"Remove unused variable 'GameStateProvider'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'TurtleGameContent' is defined but never used. Allowed unused vars must match /^_/u.","line":22,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":22,"endColumn":27,"suggestions":[{"messageId":"removeVar","data":{"varName":"TurtleGameContent"},"fix":{"range":[772,2081],"text":""},"desc":"Remove unused variable 'TurtleGameContent'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":7,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import React, { useEffect, useMemo } from \"react\";\nimport GameBase from \"../../components/game/GameBase\";\nimport GameEditor from \"../../components/game/GameEditor\";\nimport BlocklyEditor from \"../../components/game/editors/BlocklyEditor\";\nimport CodeEditor from \"../../components/game/editors/CodeEditor\";\nimport { createGame } from \"./game\";\nimport { gameConfig } from \"./config/config\";\nimport {\n registerBlocks,\n turtleToolbox,\n generateDynamicToolbox,\n} from \"./blocks/blocks\";\nimport {\n GameStateProvider,\n useGameState,\n} from \"../../contexts/GameStateContext\";\nimport { useTurtleTour } from \"./hooks/useTurtleTour\";\nimport { debugSolutions } from \"./config/debugSolutions\";\nimport \"shepherd.js/dist/css/shepherd.css\";\nimport \"../../styles/shepherd-theme.css\";\n\nfunction TurtleGameContent() {\n const { currentPhase, setEditorType, isDebugMode, setFailureMessage } =\n useGameState();\n const { startTour } = useTurtleTour();\n\n useEffect(() => {\n registerBlocks();\n }, []);\n\n useEffect(() => {\n if (currentPhase === 10) {\n setEditorType(\"code\");\n } else {\n setEditorType(\"blockly\");\n }\n }, [currentPhase, setEditorType]);\n\n const toolboxGenerator = useMemo(() => {\n const currentPhaseConfig = gameConfig.fases.find(\n (fase) => fase.id === currentPhase,\n );\n\n return () => {\n if (\n currentPhaseConfig?.allowedBlocks &&\n currentPhaseConfig.allowedBlocks.length > 0\n ) {\n return generateDynamicToolbox(currentPhaseConfig.allowedBlocks);\n }\n return turtleToolbox;\n };\n }, [currentPhase]);\n\n const renderEditor = () => {\n if (currentPhase === 10) {\n return <CodeEditor />;\n }\n return (\n <BlocklyEditor\n toolboxGenerator={toolboxGenerator}\n debugSolutions={isDebugMode ? debugSolutions : null}\n />\n );\n };\n\n return (\n <GameBase\n gameFactory={createGame}\n gameConfig={gameConfig}\n onHelpClick={startTour}\n customFailureHandler={setFailureMessage}\n >\n <GameEditor>{renderEditor()}</GameEditor>\n </GameBase>\n );\n}\n\nexport default function TurtleGame() {\n return (\n <GameStateProvider gameConfig={gameConfig}>\n <TurtleGameContent />\n </GameStateProvider>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/turtle/__tests__/integration.test.js","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'id' is defined but never used. Allowed unused args must match /^_/u.","line":556,"column":18,"nodeType":"Identifier","messageId":"unusedVar","endLine":556,"endColumn":20,"suggestions":[{"messageId":"removeVar","data":{"varName":"id"},"fix":{"range":[12264,12266],"text":""},"desc":"Remove unused variable 'id'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'freeDrawCode' is assigned a value but never used. Allowed unused vars must match /^_/u.","line":869,"column":13,"nodeType":"Identifier","messageId":"unusedVar","endLine":869,"endColumn":25,"suggestions":[{"messageId":"removeVar","data":{"varName":"freeDrawCode"},"fix":{"range":[22309,22424],"text":""},"desc":"Remove unused variable 'freeDrawCode'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { describe, it, expect, beforeEach, vi } from \"vitest\";\nimport { GameInterpreter } from \"../../../interpreters/GameInterpreter\";\nimport { setupTurtleAPI } from \"../hooks/interpreterSetup\";\nimport { validateSolution } from \"../validation/validators\";\n\n// Mock de soluções para teste (movido de config/solutions.js)\nconst SOLUTIONS = {\n fase1: `\n var count0 = 0;\n while (count0 < 4) {\n move(100);\n turn(90);\n count0 = count0 + 1;\n }\n `,\n fase1_fail: `\n var count0 = 0;\n while (count0 < 3) {\n move(100);\n turn(90);\n count0 = count0 + 1;\n }\n `,\n fase2: `\n var count1 = 0;\n while (count1 < 5) {\n move(100);\n turn(72);\n count1 = count1 + 1;\n }\n `,\n fase2_fail: `\n var count1 = 0;\n while (count1 < 5) {\n move(100);\n turn(90);\n count1 = count1 + 1;\n }\n `,\n fase3: `\n penColour('#ffff00');\n var count5 = 0;\n while (count5 < 5) {\n move(100);\n turn(144);\n count5 = count5 + 1;\n }\n `,\n fase3_fail: `\n penColour('#ff0000');\n var count5 = 0;\n while (count5 < 5) {\n move(100);\n turn(144);\n count5 = count5 + 1;\n }\n `,\n fase4: `\n penColour('#ffff00');\n var count7 = 0;\n while (count7 < 5) {\n move(50);\n turn(144);\n count7 = count7 + 1;\n }\n penDown(false);\n move(150);\n penDown(true);\n move(20);\n `,\n fase4_fail: `\n penColour('#ffff00');\n var count7 = 0;\n while (count7 < 5) {\n move(50);\n turn(144);\n count7 = count7 + 1;\n }\n move(20);\n `,\n fase5: `\n penColour('#ffff00');\n var count9 = 0;\n while (count9 < 4) {\n var count8 = 0;\n while (count8 < 5) {\n move(50);\n turn(144);\n count8 = count8 + 1;\n }\n penDown(false);\n move(150);\n turn(90);\n penDown(true);\n count9 = count9 + 1;\n }\n `,\n fase5_fail: `\n penColour('#ffff00');\n var count9 = 0;\n while (count9 < 3) {\n var count8 = 0;\n while (count8 < 5) {\n move(50);\n turn(144);\n count8 = count8 + 1;\n }\n penDown(false);\n move(150);\n turn(90);\n penDown(true);\n count9 = count9 + 1;\n }\n `,\n fase6: `\n penColour('#ffff00');\n var count20 = 0;\n while (count20 < 3) {\n var count19 = 0;\n while (count19 < 5) {\n move(50);\n turn(144);\n count19 = count19 + 1;\n }\n penDown(false);\n move(150);\n turn(120);\n penDown(true);\n count20 = count20 + 1;\n }\n penColour('#ffffff');\n penDown(false);\n var count21 = 0;\n while (count21 < 3) {\n turn(90);\n count21 = count21 + 1;\n }\n move(100);\n penDown(true);\n move(50);\n `,\n fase6_fail: `\n penColour('#ffff00');\n var count20 = 0;\n while (count20 < 3) {\n var count19 = 0;\n while (count19 < 5) {\n move(50);\n turn(144);\n count19 = count19 + 1;\n }\n penDown(false);\n move(150);\n turn(120);\n penDown(true);\n count20 = count20 + 1;\n }\n `,\n fase7: `\n penColour('#ffff00');\n var countStars = 0;\n while (countStars < 3) {\n var countPoints = 0;\n while (countPoints < 5) {\n move(50);\n turn(144);\n countPoints = countPoints + 1;\n }\n penDown(false);\n move(150);\n turn(120);\n penDown(true);\n countStars = countStars + 1;\n }\n penDown(false);\n var countTurn = 0;\n while (countTurn < 3) {\n turn(90);\n countTurn = countTurn + 1;\n }\n move(100);\n penDown(true);\n penColour('#ffffff');\n var countRays = 0;\n while (countRays < 4) {\n move(50);\n turn(90);\n turn(90);\n move(50);\n turn(90);\n turn(90);\n turn(45);\n countRays = countRays + 1;\n }\n `,\n fase7_fail: `\n penColour('#ffff00');\n var countStars = 0;\n while (countStars < 3) {\n var countPoints = 0;\n while (countPoints < 5) {\n move(50);\n turn(144);\n countPoints = countPoints + 1;\n }\n penDown(false);\n move(150);\n turn(120);\n penDown(true);\n countStars = countStars + 1;\n }\n `,\n fase8: `\n penColour('#ffff00');\n var countStars = 0;\n while (countStars < 3) {\n var countPoints = 0;\n while (countPoints < 5) {\n move(50);\n turn(144);\n countPoints = countPoints + 1;\n }\n penDown(false);\n move(150);\n turn(120);\n penDown(true);\n countStars = countStars + 1;\n }\n penDown(false);\n var countTurn = 0;\n while (countTurn < 3) {\n turn(90);\n countTurn = countTurn + 1;\n }\n move(100);\n penDown(true);\n penColour('#ffffff');\n var countCircle = 0;\n while (countCircle < 360) {\n move(50);\n turn(90);\n turn(90);\n move(50);\n turn(90);\n turn(90);\n turn(1);\n countCircle = countCircle + 1;\n }\n `,\n fase8_fail: `\n penColour('#ffff00');\n var countStars = 0;\n while (countStars < 3) {\n var countPoints = 0;\n while (countPoints < 5) {\n move(50);\n turn(144);\n countPoints = countPoints + 1;\n }\n penDown(false);\n move(150);\n turn(120);\n penDown(true);\n countStars = countStars + 1;\n }\n penDown(false);\n var countTurn = 0;\n while (countTurn < 3) {\n turn(90);\n countTurn = countTurn + 1;\n }\n move(100);\n penDown(true);\n penColour('#ffffff');\n var countCircle = 0;\n while (countCircle < 180) {\n move(50);\n turn(90);\n turn(90);\n move(50);\n turn(90);\n turn(90);\n turn(1);\n countCircle = countCircle + 1;\n }\n `,\n fase9: `\n penColour('#ffff00');\n var countStars = 0;\n while (countStars < 3) {\n var countPoints = 0;\n while (countPoints < 5) {\n move(50);\n turn(144);\n countPoints = countPoints + 1;\n }\n penDown(false);\n move(150);\n turn(120);\n penDown(true);\n countStars = countStars + 1;\n }\n penDown(false);\n var countTurn = 0;\n while (countTurn < 3) {\n turn(90);\n countTurn = countTurn + 1;\n }\n move(100);\n penDown(true);\n penColour('#ffffff');\n var countCircle1 = 0;\n while (countCircle1 < 360) {\n move(50);\n turn(90);\n turn(90);\n move(50);\n turn(90);\n turn(90);\n turn(1);\n countCircle1 = countCircle1 + 1;\n }\n turn(120);\n move(20);\n penColour('#000000');\n var countCircle2 = 0;\n while (countCircle2 < 360) {\n move(50);\n turn(90);\n turn(90);\n move(50);\n turn(90);\n turn(90);\n turn(1);\n countCircle2 = countCircle2 + 1;\n }\n `,\n fase9_fail: `\n penColour('#ffff00');\n var countStars = 0;\n while (countStars < 2) {\n var countPoints = 0;\n while (countPoints < 5) {\n move(50);\n turn(144);\n countPoints = countPoints + 1;\n }\n penDown(false);\n move(150);\n turn(120);\n penDown(true);\n countStars = countStars + 1;\n }\n penDown(false);\n var countTurn = 0;\n while (countTurn < 3) {\n turn(90);\n countTurn = countTurn + 1;\n }\n move(100);\n penDown(true);\n penColour('#ffffff');\n var countCircle1 = 0;\n while (countCircle1 < 360) {\n move(50);\n turn(90);\n turn(90);\n move(50);\n turn(90);\n turn(90);\n turn(1);\n countCircle1 = countCircle1 + 1;\n }\n `,\n fase10: null,\n fase10_fail: null,\n};\n\n// MOCK DA CONFIGURAÇÃO DO JOGO\n// Replicamos aqui apenas as regras que o validador consome\nconst MOCK_GAME_CONFIG = {\n mensagens: {\n semDesenho:\n \"Você não desenhou nada! Certifique-se de usar os comandos move() com a caneta abaixada.\",\n desenhoNaoConfere:\n \"O desenho não está correto. Verifique a forma, posição e cores.\",\n erroGeral: \"Erro técnico durante a validação.\",\n sucessoGenerico: \"Perfeito! Seu desenho está correto.\",\n timeoutExcedido: \"Tempo limite excedido!\",\n },\n fases: [\n { id: 1, nome: \"O Quadrado\", requiredDrawingMatch: true },\n { id: 2, nome: \"O Pentágono\", requiredDrawingMatch: true },\n { id: 3, nome: \"A Estrela\", requiredDrawingMatch: true },\n { id: 4, nome: \"Estrela Colorida\", requiredDrawingMatch: true },\n { id: 5, nome: \"Três Estrelas\", requiredDrawingMatch: true },\n { id: 6, nome: \"Composição\", requiredDrawingMatch: true },\n { id: 7, nome: \"Três Estrelas e Raios\", requiredDrawingMatch: true },\n { id: 8, nome: \"Três Estrelas e um Círculo\", requiredDrawingMatch: true },\n { id: 9, nome: \"Lua Crescente\", requiredDrawingMatch: true },\n { id: 10, nome: \"Desafio Livre\", requiredDrawingMatch: false },\n ],\n};\n\n/**\n * Mock do Graphics do Phaser\n * Simula o comportamento de desenho sem renderizar na tela\n */\nclass MockGraphics {\n constructor() {\n this.commandBuffer = [];\n this.lineStyle = vi.fn();\n this.fillStyle = vi.fn();\n this.beginPath = vi.fn();\n this.moveTo = vi.fn((x, y) => {\n this.commandBuffer.push({ type: \"moveTo\", x, y });\n });\n this.lineTo = vi.fn((x, y) => {\n this.commandBuffer.push({ type: \"lineTo\", x, y });\n });\n this.strokePath = vi.fn();\n this.fillRect = vi.fn();\n this.clear = vi.fn(() => {\n this.commandBuffer = [];\n });\n }\n\n // Simula extração de pontos para validação\n getPoints() {\n return this.commandBuffer\n .filter((cmd) => cmd.type === \"lineTo\" || cmd.type === \"moveTo\")\n .map((cmd) => ({ x: cmd.x, y: cmd.y }));\n }\n}\n\n/**\n * Cena Headless do Turtle Game (sem renderização gráfica)\n * Simula toda a lógica de desenho e estado necessária para os testes\n */\nclass HeadlessTurtleScene {\n constructor() {\n // Estado da tartaruga\n this.turtleX = 400;\n this.turtleY = 300;\n this.turtleAngle = -90; // Começa apontando para cima\n this.penIsDown = true;\n this.penColor = \"#FFFFFF\";\n\n // Graphics\n this.playerGraphics = new MockGraphics();\n this.validationGraphics = new MockGraphics();\n this.activeGraphics = this.playerGraphics;\n\n // Histórico de ações\n this.historico = [];\n\n // Velocidade de execução\n this.executionSpeed = 50;\n\n // Mock do som\n this.sound = { play: vi.fn() };\n }\n\n // ==================== API PÚBLICA ====================\n\n /**\n * Move a tartaruga para frente\n */\n move(distance) {\n this.historico.push({\n action: \"move\",\n distance,\n timestamp: Date.now(),\n });\n\n if (this.penIsDown) {\n // Calcula nova posição\n const radians = (this.turtleAngle * Math.PI) / 180;\n const startX = this.turtleX;\n const startY = this.turtleY;\n const endX = this.turtleX + Math.cos(radians) * distance;\n const endY = this.turtleY + Math.sin(radians) * distance;\n\n // Desenha linha\n this.activeGraphics.moveTo(startX, startY);\n this.activeGraphics.lineTo(endX, endY);\n\n // Atualiza posição\n this.turtleX = endX;\n this.turtleY = endY;\n } else {\n // Move sem desenhar\n const radians = (this.turtleAngle * Math.PI) / 180;\n this.turtleX += Math.cos(radians) * distance;\n this.turtleY += Math.sin(radians) * distance;\n }\n\n // Retorna promise resolvida imediatamente (sem delay, como no jogo real quando gera solução)\n return Promise.resolve();\n }\n\n /**\n * Gira a tartaruga\n */\n turn(angle) {\n this.historico.push({\n action: \"turn\",\n angle,\n timestamp: Date.now(),\n });\n\n this.turtleAngle = (this.turtleAngle + angle) % 360;\n return Promise.resolve();\n }\n\n /**\n * Controla se a caneta está abaixada (desenha) ou levantada (não desenha)\n */\n penDown(isDown) {\n this.historico.push({\n action: \"penDown\",\n isDown,\n timestamp: Date.now(),\n });\n\n this.penIsDown = isDown;\n }\n\n /**\n * Define a cor da caneta\n */\n penColour(color) {\n this.historico.push({\n action: \"penColour\",\n color,\n timestamp: Date.now(),\n });\n\n this.penColor = color;\n }\n\n /**\n * Pinta o fundo (não usado na validação)\n */\n pintarFundo(color) {\n this.historico.push({\n action: \"pintarFundo\",\n color,\n timestamp: Date.now(),\n });\n }\n\n /**\n * Mock do highlightBlock (não faz nada nos testes)\n */\n highlightBlock(id) {\n // No-op nos testes\n }\n\n // ==================== VALIDAÇÃO ====================\n\n /**\n * Compara o desenho do jogador com o desenho de validação (solução)\n * Retorna true se os desenhos são suficientemente similares\n */\n compareDrawings() {\n const playerPoints = this.playerGraphics.getPoints();\n const validationPoints = this.validationGraphics.getPoints();\n\n // Se não há pontos no desenho do jogador, falha\n if (playerPoints.length === 0) {\n return false;\n }\n\n // Se não há desenho de validação, assume que não há comparação necessária\n if (validationPoints.length === 0) {\n return playerPoints.length > 0;\n }\n\n // Verifica se ambos têm aproximadamente o mesmo número de pontos\n // (tolerância de 20% para variações de implementação)\n const pointRatio = playerPoints.length / validationPoints.length;\n if (pointRatio < 0.8 || pointRatio > 1.2) {\n return false;\n }\n\n // Compara assinaturas topológicas simplificadas\n const playerSignature = this._getSimpleSignature(playerPoints);\n const validationSignature = this._getSimpleSignature(validationPoints);\n\n return this._compareSignatures(playerSignature, validationSignature);\n }\n\n /**\n * Gera uma assinatura simplificada do desenho\n * (bounding box + contagem de segmentos)\n */\n _getSimpleSignature(points) {\n if (points.length === 0)\n return { minX: 0, maxX: 0, minY: 0, maxY: 0, segments: 0 };\n\n const xs = points.map((p) => p.x);\n const ys = points.map((p) => p.y);\n\n return {\n minX: Math.min(...xs),\n maxX: Math.max(...xs),\n minY: Math.min(...ys),\n maxY: Math.max(...ys),\n segments: points.length - 1,\n };\n }\n\n /**\n * Compara duas assinaturas com tolerância\n */\n _compareSignatures(sig1, sig2) {\n const tolerance = 0.15; // 15% de tolerância\n\n // Compara dimensões\n const width1 = sig1.maxX - sig1.minX;\n const height1 = sig1.maxY - sig1.minY;\n const width2 = sig2.maxX - sig2.minX;\n const height2 = sig2.maxY - sig2.minY;\n\n const widthRatio = Math.abs(width1 - width2) / Math.max(width1, width2);\n const heightRatio =\n Math.abs(height1 - height2) / Math.max(height1, height2);\n\n if (widthRatio > tolerance || heightRatio > tolerance) {\n return false;\n }\n\n // Compara número de segmentos\n const segmentRatio =\n Math.abs(sig1.segments - sig2.segments) /\n Math.max(sig1.segments, sig2.segments);\n if (segmentRatio > tolerance) {\n return false;\n }\n\n return true;\n }\n\n /**\n * Gera o desenho de validação executando o código da solução\n */\n async generateValidationDrawing(solutionCode) {\n if (!solutionCode) return;\n\n // Salva estado atual\n const savedGraphics = this.activeGraphics;\n const savedX = this.turtleX;\n const savedY = this.turtleY;\n const savedAngle = this.turtleAngle;\n const savedPenDown = this.penIsDown;\n\n // Reseta para desenho de validação\n this.activeGraphics = this.validationGraphics;\n this.validationGraphics.clear();\n this.turtleX = 400;\n this.turtleY = 300;\n this.turtleAngle = -90;\n this.penIsDown = true;\n\n // Executa o código da solução\n const api = setupTurtleAPI(this);\n const interpreter = new GameInterpreter({ stepDelay: 0, pauseExec: false });\n\n try {\n await interpreter.executeCode(solutionCode, api);\n } catch (error) {\n console.error(\"Erro ao gerar desenho de validação:\", error);\n }\n\n // Restaura estado\n this.activeGraphics = savedGraphics;\n this.turtleX = savedX;\n this.turtleY = savedY;\n this.turtleAngle = savedAngle;\n this.penIsDown = savedPenDown;\n }\n\n /**\n * Reseta o estado da tartaruga\n */\n resetTurtle() {\n this.turtleX = 400;\n this.turtleY = 300;\n this.turtleAngle = -90;\n this.penIsDown = true;\n this.penColor = \"#FFFFFF\";\n this.playerGraphics.clear();\n this.historico = [];\n }\n}\n\n// ==================== SUITE DE TESTES ====================\n\ndescribe(\"Turtle Game - Integração de Lógica (Código -> Desenho -> Validação)\", () => {\n let scene;\n let interpreter;\n\n beforeEach(() => {\n scene = new HeadlessTurtleScene();\n interpreter = new GameInterpreter({ stepDelay: 0, pauseExec: false });\n });\n\n /**\n * Fluxo completo de teste:\n * 1. Executa código do aluno (popula playerGraphics)\n * 2. Gera desenho de validação (solução)\n * 3. Valida comparando os desenhos\n */\n const runFlow = async (playerCode, solutionCode, phaseId) => {\n const api = setupTurtleAPI(scene);\n\n // 1. Executa código do aluno (desenha no playerGraphics)\n scene.activeGraphics = scene.playerGraphics;\n scene.resetTurtle(); // Garante estado limpo\n await interpreter.executeCode(playerCode, api);\n\n // 2. Gera desenho de validação\n await scene.generateValidationDrawing(solutionCode);\n\n // 3. Valida\n const configFase = MOCK_GAME_CONFIG.fases.find((f) => f.id === phaseId);\n return validateSolution(\n scene.historico,\n configFase,\n MOCK_GAME_CONFIG,\n scene,\n );\n };\n\n // ==================== TESTES DE FASES ====================\n\n describe(\"Fase 1: O Quadrado\", () => {\n it(\"Deve aprovar solução correta\", async () => {\n const result = await runFlow(SOLUTIONS.fase1, SOLUTIONS.fase1, 1);\n expect(result.success).toBe(true);\n });\n\n it(\"Deve reprovar solução incorreta (3 lados ao invés de 4)\", async () => {\n const result = await runFlow(SOLUTIONS.fase1_fail, SOLUTIONS.fase1, 1);\n expect(result.success).toBe(false);\n expect(result.reason).toContain(\"não está correto\");\n });\n\n it(\"Deve registrar ações no histórico\", async () => {\n const api = setupTurtleAPI(scene);\n await interpreter.executeCode(SOLUTIONS.fase1, api);\n\n expect(scene.historico.length).toBeGreaterThan(0);\n expect(scene.historico.some((h) => h.action === \"move\")).toBe(true);\n expect(scene.historico.some((h) => h.action === \"turn\")).toBe(true);\n });\n });\n\n describe(\"Fase 2: O Pentágono\", () => {\n it(\"Deve aprovar solução correta\", async () => {\n const result = await runFlow(SOLUTIONS.fase2, SOLUTIONS.fase2, 2);\n expect(result.success).toBe(true);\n });\n\n it(\"Deve reprovar solução incorreta (ângulos errados)\", async () => {\n const result = await runFlow(SOLUTIONS.fase2_fail, SOLUTIONS.fase2, 2);\n expect(result.success).toBe(false);\n expect(result.reason).toContain(\"não está correto\");\n });\n });\n\n describe(\"Fase 3: A Estrela\", () => {\n it(\"Deve aprovar solução correta\", async () => {\n const result = await runFlow(SOLUTIONS.fase3, SOLUTIONS.fase3, 3);\n expect(result.success).toBe(true);\n });\n\n it(\"Deve reprovar solução incorreta (4 pontas ao invés de 5)\", async () => {\n // Desenha uma estrela com 4 pontas ao invés de 5\n const wrongCode = `\n var count = 0;\n while (count < 4) {\n move(100);\n turn(144);\n count = count + 1;\n }\n `;\n const result = await runFlow(wrongCode, SOLUTIONS.fase3, 3);\n expect(result.success).toBe(false);\n });\n });\n\n describe(\"Fase 4: Estrela Colorida\", () => {\n it(\"Deve aprovar solução correta\", async () => {\n const result = await runFlow(SOLUTIONS.fase4, SOLUTIONS.fase4, 4);\n expect(result.success).toBe(true);\n });\n\n it(\"Deve reprovar solução incorreta\", async () => {\n const result = await runFlow(SOLUTIONS.fase4_fail, SOLUTIONS.fase4, 4);\n expect(result.success).toBe(false);\n });\n });\n\n describe(\"Fase 5: Três Estrelas\", () => {\n it(\"Deve aprovar solução correta\", async () => {\n const result = await runFlow(SOLUTIONS.fase5, SOLUTIONS.fase5, 5);\n expect(result.success).toBe(true);\n });\n\n it(\"Deve reprovar solução incorreta\", async () => {\n const result = await runFlow(SOLUTIONS.fase5_fail, SOLUTIONS.fase5, 5);\n expect(result.success).toBe(false);\n });\n }, 8000);\n describe(\"Fase 6: Composição\", () => {\n it(\"Deve aprovar solução correta\", async () => {\n const result = await runFlow(SOLUTIONS.fase6, SOLUTIONS.fase6, 6);\n expect(result.success).toBe(true);\n });\n\n it(\"Deve reprovar solução incorreta\", async () => {\n const result = await runFlow(SOLUTIONS.fase6_fail, SOLUTIONS.fase6, 6);\n expect(result.success).toBe(false);\n });\n });\n\n // Fases 7, 8, 9 agora têm soluções implementadas\n\n describe(\"Fase 7: Três Estrelas e Raios\", () => {\n it(\"Deve aprovar solução correta\", async () => {\n const result = await runFlow(SOLUTIONS.fase7, SOLUTIONS.fase7, 7);\n expect(result.success).toBe(true);\n }, 8000);\n\n it(\"Deve reprovar solução incorreta (sem raios brancos)\", async () => {\n const result = await runFlow(SOLUTIONS.fase7_fail, SOLUTIONS.fase7, 7);\n expect(result.success).toBe(false);\n }, 8000);\n });\n\n describe(\"Fase 8: Três Estrelas e um Círculo\", () => {\n it(\"Deve aprovar solução correta\", async () => {\n const result = await runFlow(SOLUTIONS.fase8, SOLUTIONS.fase8, 8);\n expect(result.success).toBe(true);\n }, 180000); // 3 minutos - círculo com 360 iterações\n\n it(\"Deve reprovar solução incorreta (meio círculo)\", async () => {\n const result = await runFlow(SOLUTIONS.fase8_fail, SOLUTIONS.fase8, 8);\n expect(result.success).toBe(false);\n }, 180000);\n });\n\n describe(\"Fase 9: Lua Crescente\", () => {\n it(\"Deve aprovar solução correta\", async () => {\n const result = await runFlow(SOLUTIONS.fase9, SOLUTIONS.fase9, 9);\n expect(result.success).toBe(true);\n }, 180000); // 3 minutos - dois círculos com 360 iterações cada\n\n it(\"Deve reprovar solução incorreta (2 estrelas ao invés de 3)\", async () => {\n const result = await runFlow(SOLUTIONS.fase9_fail, SOLUTIONS.fase9, 9);\n expect(result.success).toBe(false);\n }, 180000);\n });\n\n describe(\"Fase 10: Desafio Livre\", () => {\n it(\"Deve sempre aprovar qualquer código (sem validação)\", async () => {\n const freeDrawCode = `\n move(50);\n turn(45);\n move(50);\n `;\n\n const configFase = MOCK_GAME_CONFIG.fases.find((f) => f.id === 10);\n const result = validateSolution(\n scene.historico,\n configFase,\n MOCK_GAME_CONFIG,\n scene,\n );\n\n expect(result.success).toBe(true);\n });\n\n it(\"Deve aprovar mesmo sem desenho algum\", async () => {\n const configFase = MOCK_GAME_CONFIG.fases.find((f) => f.id === 10);\n const result = validateSolution([], configFase, MOCK_GAME_CONFIG, scene);\n\n expect(result.success).toBe(true);\n });\n });\n\n // ==================== TESTES DE COMPORTAMENTO ====================\n\n describe(\"Comportamento da API\", () => {\n it(\"Deve desenhar apenas quando caneta está abaixada\", async () => {\n const api = setupTurtleAPI(scene);\n\n // Caneta levantada\n scene.penDown(false);\n await interpreter.executeCode(\"move(100);\", api);\n const pointsWithPenUp = scene.playerGraphics.commandBuffer.length;\n\n // Limpa e testa com caneta abaixada\n scene.resetTurtle();\n scene.penDown(true);\n await interpreter.executeCode(\"move(100);\", api);\n const pointsWithPenDown = scene.playerGraphics.commandBuffer.length;\n\n expect(pointsWithPenUp).toBe(0);\n expect(pointsWithPenDown).toBeGreaterThan(0);\n });\n\n it(\"Deve atualizar posição da tartaruga corretamente\", async () => {\n const api = setupTurtleAPI(scene);\n const initialY = scene.turtleY;\n\n // Move para cima (ângulo -90 = norte), então Y deve diminuir\n await interpreter.executeCode(\"move(100);\", api);\n\n // X permanece o mesmo (movimento vertical), Y muda\n expect(scene.turtleY).not.toBe(initialY);\n expect(scene.turtleY).toBeLessThan(initialY); // Moveu para cima\n });\n\n it(\"Deve registrar todas as ações no histórico\", async () => {\n const api = setupTurtleAPI(scene);\n\n await interpreter.executeCode(\n `\n move(100);\n turn(90);\n penDown(false);\n penColour('red');\n `,\n api,\n );\n\n expect(scene.historico).toHaveLength(4);\n expect(scene.historico[0].action).toBe(\"move\");\n expect(scene.historico[1].action).toBe(\"turn\");\n expect(scene.historico[2].action).toBe(\"penDown\");\n expect(scene.historico[3].action).toBe(\"penColour\");\n });\n });\n\n describe(\"Validação Visual\", () => {\n it(\"Deve falhar quando não há desenho do jogador\", async () => {\n const configFase = MOCK_GAME_CONFIG.fases.find((f) => f.id === 1);\n\n // Não executa código, então não há desenho\n const result = validateSolution([], configFase, MOCK_GAME_CONFIG, scene);\n\n expect(result.success).toBe(false);\n expect(result.reason).toContain(\"não desenhou nada\");\n });\n\n it(\"Deve comparar desenhos usando geometria simplificada\", async () => {\n // Desenha dois quadrados idênticos em locais diferentes do código\n const code1 = `\n for (var i = 0; i < 4; i++) {\n move(100);\n turn(90);\n }\n `;\n\n const code2 = `\n var count = 0;\n while (count < 4) {\n move(100);\n turn(90);\n count++;\n }\n `;\n\n const result = await runFlow(code1, code2, 1);\n expect(result.success).toBe(true);\n });\n });\n\n describe(\"Casos Especiais\", () => {\n it(\"Deve tratar configuração de fase ausente\", () => {\n const result = validateSolution([], {}, MOCK_GAME_CONFIG, scene);\n\n expect(result.success).toBe(false);\n expect(result.reason).toContain(\"Erro técnico\");\n });\n\n it(\"Deve tratar ausência de referência à cena\", () => {\n const configFase = MOCK_GAME_CONFIG.fases.find((f) => f.id === 1);\n const result = validateSolution([], configFase, MOCK_GAME_CONFIG, null);\n\n expect(result.success).toBe(false);\n expect(result.reason).toContain(\"Erro técnico\");\n });\n });\n});\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/turtle/blocks/blocks.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/turtle/config/config.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/turtle/config/debugSolutions.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/turtle/config/tourSteps.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/turtle/game.js","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'e' is defined but never used.","line":586,"column":14,"nodeType":"Identifier","messageId":"unusedVar","endLine":586,"endColumn":15},{"ruleId":"no-unused-vars","severity":1,"message":"'e' is defined but never used.","line":617,"column":14,"nodeType":"Identifier","messageId":"unusedVar","endLine":617,"endColumn":15}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import Phaser from \"phaser\";\nimport { BaseGameScene } from \"../../shared/BaseGameScene.js\";\nimport { GameInterpreter } from \"../../interpreters/GameInterpreter.js\";\nimport { setupTurtleAPI } from \"./hooks/interpreterSetup.js\";\nimport { validateSolution } from \"./validation/validators.js\";\nimport turtleImg1 from \"./assets/1.png\";\nimport turtleImg2 from \"./assets/2.png\";\nimport win from \"./assets/win.mp3\";\nimport fail from \"./assets/fail.mp3\";\n\nconst ASSETS = {\n IMG: { TURTLE1: \"turtle1\", TURTLE2: \"turtle2\" },\n AUDIO: { WIN: \"win\", FAIL: \"fail\" },\n};\n\nconst CONSTANTES = {\n LARGURA: 640,\n ALTURA: 480,\n};\n\nclass TurtleScene extends BaseGameScene {\n constructor() {\n super(\"TurtleScene\");\n\n this.turtleState = null;\n this.turtleSprite = null;\n this.solutionGraphics = null;\n this.playerGraphics = null;\n this.validationGraphics = null;\n this.activeGraphics = null;\n this.backgroundRect = null;\n this.playerColors = [];\n this.solutionColors = [];\n this.executionSpeed = 50;\n this.sliderWidth = 0;\n this.sliderFill = null;\n this.sliderHandle = null;\n this.speedText = null;\n this.playerRT = null;\n this.validationRT = null;\n this.resultadoJogada = \"em_andamento\";\n }\n\n preload() {\n this.preloadGlobalAssets();\n this.load.image(ASSETS.IMG.TURTLE1, turtleImg1);\n this.load.image(ASSETS.IMG.TURTLE2, turtleImg2);\n this.load.audio(ASSETS.AUDIO.WIN, win);\n this.load.audio(ASSETS.AUDIO.FAIL, fail);\n }\n\n init(data) {\n super.init(data);\n this.executionSpeed = 50;\n this.turtleState = {\n x: 0,\n y: 0,\n angle: 0,\n penDown: true,\n penColour: 0xffffff,\n };\n this.playerColors = [];\n this.solutionColors = [];\n this.resultadoJogada = \"em_andamento\";\n }\n\n setExecutionSpeed(speed) {\n this.executionSpeed = Math.max(1, Math.min(100, speed));\n\n if (typeof this.updateSpeedUI === \"function\") {\n this.updateSpeedUI();\n }\n }\n\n updateSpeedUI() {\n if (this.speedText) {\n this.speedText.setText(`Velocidade: ${this.executionSpeed}%`);\n }\n if (this.sliderFill) {\n this.sliderFill.setSize(\n (this.executionSpeed / 100) * this.sliderWidth,\n 12,\n );\n }\n if (this.sliderHandle) {\n this.sliderHandle.x =\n (this.executionSpeed / 100 - 0.5) * this.sliderWidth;\n }\n }\n\n createSpeedControl() {\n const gameWidth = this.game.config.width;\n const gameHeight = this.game.config.height;\n\n this.sliderWidth = Math.min(gameWidth - 40, 300);\n const controlHeight = 50;\n const centerX = gameWidth / 2;\n const bottomY = gameHeight - 35;\n\n const controlsContainer = this.add.container(centerX, bottomY);\n\n const bg = this.add.rectangle(\n 0,\n 0,\n this.sliderWidth + 80,\n controlHeight,\n 0x000000,\n 0.85,\n );\n bg.setOrigin(0.5, 0.5);\n controlsContainer.add(bg);\n\n const sliderBg = this.add.rectangle(0, 8, this.sliderWidth, 12, 0x444444);\n controlsContainer.add(sliderBg);\n\n this.sliderFill = this.add.rectangle(\n -this.sliderWidth / 2,\n 8,\n (this.executionSpeed / 100) * this.sliderWidth,\n 28,\n 0x00ff88,\n );\n this.sliderFill.setOrigin(0, 0.5);\n controlsContainer.add(this.sliderFill);\n\n this.sliderHandle = this.add.circle(\n (this.executionSpeed / 100 - 0.5) * this.sliderWidth,\n 8,\n 16,\n 0xffffff,\n );\n this.sliderHandle.setInteractive({ useHandCursor: true });\n this.sliderHandle.setStrokeStyle(2, 0x333333);\n controlsContainer.add(this.sliderHandle);\n\n this.speedText = this.add.text(\n 0,\n -12,\n `Velocidade: ${this.executionSpeed}%`,\n {\n fontSize: \"14px\",\n fill: \"#ffffff\",\n fontFamily: \"Arial\",\n },\n );\n this.speedText.setOrigin(0.5, 0.5);\n controlsContainer.add(this.speedText);\n\n let isDragging = false;\n\n this.sliderHandle.on(\"pointerdown\", () => {\n isDragging = true;\n });\n\n this.input.on(\"pointermove\", (pointer) => {\n if (isDragging) {\n const localX = pointer.x - centerX;\n const clampedX = Math.max(\n -this.sliderWidth / 2,\n Math.min(this.sliderWidth / 2, localX),\n );\n const newSpeed = Math.round(\n ((clampedX + this.sliderWidth / 2) / this.sliderWidth) * 100,\n );\n this.setExecutionSpeed(newSpeed);\n }\n });\n\n this.input.on(\"pointerup\", () => {\n isDragging = false;\n });\n\n sliderBg.setInteractive();\n sliderBg.on(\"pointerdown\", (pointer) => {\n const localX = pointer.x - centerX;\n const clampedX = Math.max(\n -this.sliderWidth / 2,\n Math.min(this.sliderWidth / 2, localX),\n );\n const newSpeed = Math.round(\n ((clampedX + this.sliderWidth / 2) / this.sliderWidth) * 100,\n );\n this.setExecutionSpeed(newSpeed);\n });\n\n controlsContainer.setDepth(9999);\n }\n\n calcDuration() {\n return 500 + ((1 - 500) / (100 - 1)) * (this.executionSpeed - 1);\n }\n\n resetTurtle() {\n this.turtleState.x = this.game.config.width / 2;\n this.turtleState.y = this.game.config.height / 2;\n this.turtleState.angle = 0;\n this.turtleState.penDown = true;\n this.turtleState.penColour = 0xffffff;\n\n if (this.turtleSprite) {\n this.turtleSprite.setPosition(this.turtleState.x, this.turtleState.y);\n this.turtleSprite.setAngle(0);\n this.turtleSprite.stop();\n this.turtleSprite.setTexture(ASSETS.IMG.TURTLE1);\n }\n }\n\n pintarFundo(color) {\n let colorValue;\n\n if (typeof color === \"string\") {\n if (color.startsWith(\"#\")) {\n colorValue = parseInt(color.substring(1), 16);\n } else {\n const colorMap = {\n vermelho: 0xff0000,\n azul: 0x0000ff,\n verde: 0x00ff00,\n amarelo: 0xffff00,\n branco: 0xffffff,\n preto: 0x000000,\n };\n colorValue = colorMap[color.toLowerCase()] || 0x000000;\n }\n } else {\n colorValue = color;\n }\n\n if (this.backgroundRect) {\n this.backgroundRect.destroy();\n }\n\n this.backgroundRect = this.add.rectangle(\n this.game.config.width / 2,\n this.game.config.height / 2,\n this.game.config.width,\n this.game.config.height,\n colorValue,\n );\n this.backgroundRect.setDepth(-1000);\n }\n\n addColorToActiveGraphics() {\n const colorArray =\n this.activeGraphics === this.playerGraphics\n ? this.playerColors\n : this.solutionColors;\n\n if (!colorArray.includes(this.turtleState.penColour)) {\n colorArray.push(this.turtleState.penColour);\n }\n }\n\n _moveInstant(distance) {\n const lastX = this.turtleState.x;\n const lastY = this.turtleState.y;\n const angleRad = Phaser.Math.DegToRad(this.turtleState.angle);\n\n this.turtleState.x += Math.cos(angleRad) * distance;\n this.turtleState.y -= Math.sin(angleRad) * distance;\n\n if (this.turtleState.penDown && this.activeGraphics) {\n this.activeGraphics.lineStyle(4, this.turtleState.penColour, 1);\n this.activeGraphics.lineBetween(\n lastX,\n lastY,\n this.turtleState.x,\n this.turtleState.y,\n );\n this.addColorToActiveGraphics();\n }\n }\n\n _turnInstant(angle) {\n this.turtleState.angle += angle;\n this.turtleState.angle = this.turtleState.angle % 360;\n if (this.turtleState.angle < 0) {\n this.turtleState.angle += 360;\n }\n }\n\n highlightBlock(id) {\n if (this.workspace) this.workspace.highlightBlock(id);\n this.highlightPause = true;\n }\n\n move(distance) {\n this.historico.push({ tipo: \"move\", distancia: distance });\n\n return new Promise((resolve) => {\n const duration = this.calcDuration();\n const startX = this.turtleState.x;\n const startY = this.turtleState.y;\n const angleRad = Phaser.Math.DegToRad(this.turtleState.angle);\n const targetX = this.turtleState.x + Math.cos(angleRad) * distance;\n const targetY = this.turtleState.y - Math.sin(angleRad) * distance;\n\n this.turtleState.x = targetX;\n this.turtleState.y = targetY;\n\n this.turtleSprite.play(\"turtle_walk\");\n\n this.tweens.add({\n targets: this.turtleSprite,\n x: targetX,\n y: targetY,\n duration: duration,\n ease: \"Linear\",\n onComplete: () => {\n this.turtleSprite.stop();\n this.turtleSprite.setTexture(ASSETS.IMG.TURTLE1);\n\n if (this.turtleState.penDown && this.activeGraphics) {\n this.activeGraphics.lineStyle(4, this.turtleState.penColour, 1);\n this.activeGraphics.lineBetween(\n startX,\n startY,\n this.turtleState.x,\n this.turtleState.y,\n );\n this.addColorToActiveGraphics();\n }\n\n resolve();\n },\n });\n });\n }\n\n turn(angle) {\n this.historico.push({ tipo: \"turn\", angulo: angle });\n\n return new Promise((resolve) => {\n const duration = this.calcDuration();\n this._turnInstant(angle);\n\n const currentSpriteAngle = this.turtleSprite.angle;\n const targetSpriteAngle = -this.turtleState.angle;\n\n let angleDiff = targetSpriteAngle - currentSpriteAngle;\n\n while (angleDiff > 180) angleDiff -= 360;\n while (angleDiff < -180) angleDiff += 360;\n\n const finalAngle = currentSpriteAngle + angleDiff;\n\n this.tweens.add({\n targets: this.turtleSprite,\n angle: finalAngle,\n duration: duration,\n ease: \"Linear\",\n onComplete: () => {\n resolve();\n },\n });\n });\n }\n\n penDown(isDown) {\n this.historico.push({ tipo: \"penDown\", valor: isDown });\n this.turtleState.penDown = isDown;\n }\n\n penColour(color) {\n this.historico.push({ tipo: \"penColour\", cor: color });\n\n if (typeof color === \"string\") {\n if (color.startsWith(\"#\")) {\n this.turtleState.penColour = parseInt(color.substring(1), 16);\n } else {\n const colorMap = {\n vermelho: 0xff0000,\n azul: 0x0000ff,\n verde: 0x00ff00,\n amarelo: 0xffff00,\n branco: 0xffffff,\n preto: 0x000000,\n };\n this.turtleState.penColour = colorMap[color.toLowerCase()] || 0xffffff;\n }\n } else {\n this.turtleState.penColour = color;\n }\n\n this.addColorToActiveGraphics();\n }\n\n extractPointsFromGraphics(graphics) {\n const points = [];\n let i = 0;\n\n while (i < graphics.commandBuffer.length) {\n const cmd = graphics.commandBuffer[i];\n if (\n cmd === 6 &&\n i + 11 < graphics.commandBuffer.length &&\n graphics.commandBuffer[i + 1] === 4 &&\n graphics.commandBuffer[i + 5] === 5 &&\n graphics.commandBuffer[i + 8] === 4 &&\n graphics.commandBuffer[i + 11] === 9\n ) {\n const x1 = graphics.commandBuffer[i + 6];\n const y1 = graphics.commandBuffer[i + 7];\n const x2 = graphics.commandBuffer[i + 9];\n const y2 = graphics.commandBuffer[i + 10];\n\n points.push({ x: x1, y: y1 });\n points.push({ x: x2, y: y2 });\n i += 12;\n } else {\n i++;\n }\n }\n\n return points;\n }\n\n getDrawingSignature(graphics) {\n if (!graphics.commandBuffer || graphics.commandBuffer.length === 0) {\n return null;\n }\n\n const points = this.extractPointsFromGraphics(graphics);\n if (points.length === 0) {\n return null;\n }\n\n let sumX = 0,\n sumY = 0;\n let minX = Infinity,\n minY = Infinity;\n let maxX = -Infinity,\n maxY = -Infinity;\n\n points.forEach((p) => {\n sumX += p.x;\n sumY += p.y;\n minX = Math.min(minX, p.x);\n minY = Math.min(minY, p.y);\n maxX = Math.max(maxX, p.x);\n maxY = Math.max(maxY, p.y);\n });\n\n const pointCount = points.length;\n const centroid = { x: sumX / pointCount, y: sumY / pointCount };\n const normalizedCentroid = {\n x: centroid.x - minX,\n y: centroid.y - minY,\n };\n\n return {\n bounds: { x: minX, y: minY, width: maxX - minX, height: maxY - minY },\n centroid: normalizedCentroid,\n };\n }\n\n compareColors(playerColors, solutionColors) {\n if (playerColors.length !== solutionColors.length) return false;\n\n const sortedPlayer = [...playerColors].sort();\n const sortedSolution = [...solutionColors].sort();\n\n return sortedSolution.every(\n (color, index) => sortedPlayer[index] === color,\n );\n }\n\n getTopologicalSignature(graphics) {\n if (!graphics.commandBuffer || graphics.commandBuffer.length === 0) {\n return null;\n }\n\n const points = this.extractPointsFromGraphics(graphics);\n\n if (points.length < 3) {\n return { curvatureSum: 0, pointCount: points.length };\n }\n\n let curvatureSum = 0;\n for (let j = 1; j < points.length - 1; j++) {\n const p0 = points[j - 1];\n const p1 = points[j];\n const p2 = points[j + 1];\n\n const v1 = { x: p1.x - p0.x, y: p1.y - p0.y };\n const v2 = { x: p2.x - p1.x, y: p2.y - p1.y };\n\n const crossProduct = v1.x * v2.y - v1.y * v2.x;\n curvatureSum += crossProduct;\n }\n\n return {\n curvatureSum: curvatureSum,\n pointCount: points.length,\n isClockwise: curvatureSum < 0,\n absoluteCurvature: Math.abs(curvatureSum),\n };\n }\n\n compareDrawings() {\n if (!this.compareColors(this.playerColors, this.solutionColors)) {\n return false;\n }\n\n const playerSig = this.getDrawingSignature(this.playerGraphics);\n const solutionSig = this.getDrawingSignature(this.validationGraphics);\n\n if (!playerSig || !solutionSig) {\n return false;\n }\n\n const pb = playerSig.bounds;\n const sb = solutionSig.bounds;\n\n const dimensionTolerance = Math.max(sb.width, sb.height, 1) * 0.05;\n if (\n Math.abs(pb.width - sb.width) > dimensionTolerance ||\n Math.abs(pb.height - sb.height) > dimensionTolerance\n ) {\n return false;\n }\n\n const posX_diff = Math.abs(pb.x - sb.x);\n const posY_diff = Math.abs(pb.y - sb.y);\n if (posX_diff > 1.5 || posY_diff > 1.5) {\n return false;\n }\n\n const centroidTolerance = 1.5;\n const centroidDeltaX = Math.abs(\n playerSig.centroid.x - solutionSig.centroid.x,\n );\n const centroidDeltaY = Math.abs(\n playerSig.centroid.y - solutionSig.centroid.y,\n );\n\n if (\n centroidDeltaX > centroidTolerance ||\n centroidDeltaY > centroidTolerance\n ) {\n return false;\n }\n\n const playerTopo = this.getTopologicalSignature(this.playerGraphics);\n const solutionTopo = this.getTopologicalSignature(this.validationGraphics);\n\n if (!playerTopo || !solutionTopo) {\n return false;\n }\n\n if (\n playerTopo.isClockwise !== solutionTopo.isClockwise &&\n Math.abs(playerTopo.absoluteCurvature) > 10\n ) {\n return false;\n }\n\n return true;\n }\n\n drawSolution() {\n const configFase = this.registry.get(\"configFase\");\n if (!configFase || !configFase.solutionCode) {\n return;\n }\n\n this.solutionColors = [];\n this.activeGraphics = this.solutionGraphics;\n this.resetTurtle();\n this.turtleState.penColour = 0xffffff;\n\n const move = this._moveInstant.bind(this);\n const turn = this._turnInstant.bind(this);\n const penDown = this.penDown.bind(this);\n const penColour = this.penColour.bind(this);\n\n try {\n const fn = new Function(\n \"move\",\n \"turn\",\n \"penDown\",\n \"penColour\",\n configFase.solutionCode,\n );\n fn(move, turn, penDown, penColour);\n } catch (e) {\n // Erro silencioso - desenho de solução não é crítico\n }\n }\n\n generateValidationDrawing() {\n const configFase = this.registry.get(\"configFase\");\n if (!configFase || !configFase.solutionCode) {\n return;\n }\n\n this.validationGraphics.clear();\n this.solutionColors = [];\n this.activeGraphics = this.validationGraphics;\n this.resetTurtle();\n this.turtleState.penColour = 0xffffff;\n\n const move = this._moveInstant.bind(this);\n const turn = this._turnInstant.bind(this);\n const penDown = this.penDown.bind(this);\n const penColour = this.penColour.bind(this);\n\n try {\n const fn = new Function(\n \"move\",\n \"turn\",\n \"penDown\",\n \"penColour\",\n configFase.solutionCode,\n );\n fn(move, turn, penDown, penColour);\n } catch (e) {\n // Erro silencioso - desenho de validação não é crítico\n }\n }\n\n onBeforeRun() {\n this.playerGraphics.clear();\n this.activeGraphics = this.playerGraphics;\n this.resetTurtle();\n this.playerColors = [];\n this.resultadoJogada = \"em_andamento\";\n this.solutionGraphics.clear();\n this.drawSolution();\n this.generateValidationDrawing();\n this.activeGraphics = this.playerGraphics;\n this.resetTurtle();\n }\n\n onReset() {\n this.playerGraphics.clear();\n this.solutionGraphics.clear();\n this.resetTurtle();\n this.activeGraphics = this.playerGraphics;\n this.drawSolution();\n this.playerColors = [];\n this.solutionColors = [];\n this.resultadoJogada = \"em_andamento\";\n }\n\n onSuccess() {\n const configFase = this.registry.get(\"configFase\");\n if (configFase && configFase.id === 10) return;\n this.playAudio(ASSETS.AUDIO.WIN);\n }\n\n onFailure() {\n this.playAudio(ASSETS.AUDIO.FAIL);\n }\n\n create() {\n this.createSpeedControl();\n\n this.anims.create({\n key: \"turtle_walk\",\n frames: [{ key: ASSETS.IMG.TURTLE1 }, { key: ASSETS.IMG.TURTLE2 }],\n frameRate: 8,\n repeat: -1,\n });\n\n this.solutionGraphics = this.add.graphics();\n this.playerGraphics = this.add.graphics();\n this.solutionGraphics.setAlpha(0.3);\n this.validationGraphics = this.add.graphics().setVisible(false);\n this.playerRT = this.add\n .renderTexture(0, 0, CONSTANTES.LARGURA, CONSTANTES.ALTURA)\n .setVisible(false);\n this.validationRT = this.add\n .renderTexture(0, 0, CONSTANTES.LARGURA, CONSTANTES.ALTURA)\n .setVisible(false);\n this.activeGraphics = this.playerGraphics;\n\n this.turtleSprite = this.add.sprite(\n CONSTANTES.LARGURA / 2,\n CONSTANTES.ALTURA / 2,\n ASSETS.IMG.TURTLE1,\n );\n this.turtleSprite.setOrigin(0.5, 0.5);\n this.turtleSprite.setAngle(0);\n this.turtleSprite.setDepth(1000);\n\n this.gameInterpreter = new GameInterpreter({ stepDelay: 500 });\n this.setExecutionSpeed(this.executionSpeed);\n this.drawSolution();\n\n this.setupStandardController(\n setupTurtleAPI,\n (history, config, gameConfig) =>\n validateSolution(history, config, gameConfig, this),\n );\n }\n}\n\nexport const createGame = (\n parentElement,\n configFaseAtual,\n customFailureHandler = null,\n idFaseAtual = null,\n gameConfig = null,\n) => {\n const config =\n idFaseAtual && gameConfig\n ? gameConfig.fases[idFaseAtual - 1]\n : configFaseAtual;\n\n return {\n type: Phaser.AUTO,\n width: CONSTANTES.LARGURA,\n height: CONSTANTES.ALTURA,\n backgroundColor: \"#171616\",\n antialias: true,\n roundPixels: true,\n pixelArt: false,\n parent: parentElement,\n scale: {\n mode: Phaser.Scale.FIT,\n autoCenter: Phaser.Scale.CENTER_BOTH,\n zoom: 5,\n },\n scene: [TurtleScene],\n callbacks: {\n preBoot: (game) => {\n game.registry.merge({\n configFase: config,\n gameConfig: gameConfig,\n customFailureHandler: customFailureHandler,\n stepDelay: 500,\n });\n },\n },\n };\n};\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/turtle/hooks/interpreterSetup.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/turtle/hooks/useTurtleTour.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/games/turtle/validation/validators.js","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'error' is defined but never used.","line":83,"column":14,"nodeType":"Identifier","messageId":"unusedVar","endLine":83,"endColumn":19}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { BaseGameValidator } from \"../../../shared/BaseGameValidator\";\n\n/**\n * Validador específico do Turtle Game\n *\n * Diferente de outros jogos, o Turtle valida principalmente através de\n * comparação visual de desenhos, não apenas por sequência de ações.\n */\nclass TurtleValidator extends BaseGameValidator {\n /**\n * Valida a solução do aluno baseado no desenho produzido\n *\n * @param {Array} history - Histórico de ações (para debug/estatísticas)\n * @param {Object} config - Configuração da fase atual\n * @param {Object} gameConfig - Configuração global do jogo\n * @param {Object} sceneRef - Referência à cena Phaser (para acessar métodos de comparação)\n * @returns {Object} { success: boolean, reason?: string }\n */\n validatePhase(history, config, gameConfig, sceneRef) {\n // 1. Fail-Safe: Verificar se a configuração foi passada\n if (!config || Object.keys(config).length === 0) {\n return this.failure(\n gameConfig?.mensagens?.erroGeral ||\n \"Erro técnico: Fase não configurada.\",\n );\n }\n\n // 2. Fase 10 (Desafio Livre) - Sempre sucesso\n // Também aceita fases com requiredDrawingMatch = false\n if (config.id === 10 || config.requiredDrawingMatch === false) {\n return this.success();\n }\n\n // 3. Verificar se há referência à cena (necessária para comparação visual)\n if (!sceneRef) {\n return this.failure(\n gameConfig?.mensagens?.erroGeral || \"Erro técnico na validação.\",\n );\n }\n\n // 4. Fases 1-9: Validar desenho produzido\n return this._validateDrawing(sceneRef, config, gameConfig);\n }\n\n /**\n * Valida se o desenho do jogador corresponde à solução esperada\n * Método privado chamado apenas por validatePhase() para fases 1-9\n *\n * @param {Object} scene - Referência à cena Phaser\n * @param {Object} config - Configuração da fase\n * @param {Object} gameConfig - Configuração global\n * @returns {Object} { success: boolean, reason?: string }\n * @private\n */\n _validateDrawing(scene, config, gameConfig) {\n try {\n // 1. Verificar se o jogador desenhou algo\n const hasPlayerDrawing = this._hasPlayerDrawing(scene);\n\n if (!hasPlayerDrawing) {\n return this.failure(\n config?.mensagens?.semDesenho ||\n gameConfig?.mensagens?.semDesenho ||\n \"Você não desenhou nada! Certifique-se de usar os comandos move() com a caneta abaixada.\",\n );\n }\n\n // 2. Comparar desenhos usando método da scene (compareDrawings)\n // O método scene.compareDrawings() acessa validationGraphics e playerGraphics\n // que já foram preparados no onBeforeRun() através de generateValidationDrawing()\n const drawingsMatch = scene.compareDrawings();\n\n if (!drawingsMatch) {\n return this.failure(\n config?.mensagens?.desenhoNaoConfere ||\n gameConfig?.mensagens?.desenhoNaoConfere ||\n \"O desenho não está correto. Verifique a forma, posição e cores.\",\n );\n }\n\n // 3. Sucesso - desenho corresponde à solução!\n return this.success();\n } catch (error) {\n return this.failure(\n gameConfig?.mensagens?.erroGeral ||\n \"Erro inesperado durante a validação.\",\n );\n }\n }\n\n /**\n * Verifica se o jogador produziu algum desenho\n * Checa se o commandBuffer do playerGraphics possui comandos de desenho\n *\n * @param {Object} scene - Referência à cena Phaser\n * @returns {boolean} true se há desenho, false caso contrário\n * @private\n */\n _hasPlayerDrawing(scene) {\n return (\n scene.playerGraphics &&\n scene.playerGraphics.commandBuffer &&\n scene.playerGraphics.commandBuffer.length > 0\n );\n }\n\n // --- Métodos Herdados da Base ---\n // success() - retorna { success: true }\n // failure(reason) - retorna { success: false, reason }\n}\n\n// Singleton para reutilização\nconst validatorInstance = new TurtleValidator();\n\n/**\n * Função exportada para validação de soluções do Turtle Game\n *\n * @param {Array} history - Histórico de ações\n * @param {Object} config - Configuração da fase\n * @param {Object} gameConfig - Configuração global\n * @param {Object} sceneRef - Referência à cena (necessário para comparação visual)\n * @returns {Object} { success: boolean, reason?: string }\n */\nexport const validateSolution = (history, config, gameConfig, sceneRef) => {\n return validatorInstance.validatePhase(history, config, gameConfig, sceneRef);\n};\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/hooks/useGameTour.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/hooks/useIsMobile.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/hooks/useTour.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/interpreters/ApiHelpers.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/interpreters/GameInterpreter.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/main.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'React' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":13,"suggestions":[{"messageId":"removeVar","data":{"varName":"React"},"fix":{"range":[7,18],"text":""},"desc":"Remove unused variable 'React'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'App' is defined but never used. Allowed unused vars must match /^_/u.","line":4,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":4,"endColumn":11,"suggestions":[{"messageId":"removeVar","data":{"varName":"App"},"fix":{"range":[160,169],"text":""},"desc":"Remove unused variable 'App'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import React from \"react\";\nimport ReactDOM from \"react-dom/client\";\nimport \"./config/blocklyConfig.js\"; // Configuração global do Blockly (locale PT-BR)\nimport App from \"./App.jsx\";\n\nReactDOM.createRoot(document.getElementById(\"root\")).render(<App />);\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/About/About.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'ArrowRight' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":20,"suggestions":[{"messageId":"removeVar","data":{"varName":"ArrowRight"},"fix":{"range":[9,20],"text":""},"desc":"Remove unused variable 'ArrowRight'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Users' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":22,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":27,"suggestions":[{"messageId":"removeVar","data":{"varName":"Users"},"fix":{"range":[19,26],"text":""},"desc":"Remove unused variable 'Users'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Heart' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":29,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":34,"suggestions":[{"messageId":"removeVar","data":{"varName":"Heart"},"fix":{"range":[26,33],"text":""},"desc":"Remove unused variable 'Heart'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Lightbulb' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":36,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":45,"suggestions":[{"messageId":"removeVar","data":{"varName":"Lightbulb"},"fix":{"range":[33,44],"text":""},"desc":"Remove unused variable 'Lightbulb'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Globe' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":47,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":52,"suggestions":[{"messageId":"removeVar","data":{"varName":"Globe"},"fix":{"range":[44,51],"text":""},"desc":"Remove unused variable 'Globe'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Navbar' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":14,"suggestions":[{"messageId":"removeVar","data":{"varName":"Navbar"},"fix":{"range":[82,94],"text":""},"desc":"Remove unused variable 'Navbar'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Footer' is defined but never used. Allowed unused vars must match /^_/u.","line":3,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":3,"endColumn":14,"suggestions":[{"messageId":"removeVar","data":{"varName":"Footer"},"fix":{"range":[128,140],"text":""},"desc":"Remove unused variable 'Footer'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":7,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ArrowRight, Users, Heart, Lightbulb, Globe } from \"lucide-react\";\nimport Navbar from \"../../components/Navbar\";\nimport Footer from \"../HomePage/Footer\";\n\nexport default function About() {\n return (\n <>\n {/* Navegação */}\n <Navbar />\n\n {/* Background Gradient */}\n <div\n className=\"isolate -z-10 fixed h-full w-full top-0 left-0\"\n style={{\n background: \"#EAEEF5\",\n }}\n ></div>\n\n <section className=\"relative mt-32 lg:mt-[195px]\">\n {/* Container principal */}\n <div className=\"mx-auto max-w-7xl px-6 py-24 sm:py-24 lg:py-32 lg:px-8\">\n <div className=\"flex flex-col gap-8 sm:gap-12 xl:gap-16 xl:flex-row xl:items-start \">\n {/* Conteúdo textual */}\n <div className=\"xl:w-[60%]\">\n <h2 className=\"font-bold text-gray-900 tracking-tight text-4xl sm:text-5xl\">\n Somos Educadores do Núcleo de Tecnologia do MTST\n </h2>\n\n <p className=\"mt-6 font-normal text-gray-700 text-lg\">\n Somos parte do Núcleo de Tecnologia do MTST, e somos\n comprometidos com a democratização do ensino de programação e\n tecnologia. Acreditamos que a educação digital deve ser\n acessível, gratuita e transformadora para todas as pessoas.\n </p>\n\n <p className=\"mt-4 font-normal text-gray-700 text-lg\">\n Nossa missão é empoderar estudantes e professores através de\n ferramentas pedagógicas abertas, baseadas em metodologias\n críticas e participativas. Trabalhamos para que a tecnologia\n seja um instrumento de inclusão social e desenvolvimento\n comunitário.\n </p>\n\n <p className=\"mt-4 font-normal text-gray-700 text-lg\">\n O Decoda nasceu dessa visão: uma plataforma educacional que\n valoriza o pensamento computacional como ferramenta de\n transformação social, respeitando a diversidade de saberes e\n promovendo a autonomia pedagógica.\n </p>\n\n {/* Valores */}\n <div className=\"mt-8 space-y-4\">\n <div className=\"flex items-start gap-3\">\n <Users className=\"w-6 h-6 text-blue-600 flex-shrink-0 mt-1\" />\n <div>\n <h3 className=\"font-semibold text-gray-900 text-base\">\n Educação Popular\n </h3>\n <p className=\"text-gray-700 text-sm\">\n Valorizamos o diálogo, a participação coletiva e a\n construção colaborativa do conhecimento.\n </p>\n </div>\n </div>\n\n <div className=\"flex items-start gap-3\">\n <Heart className=\"w-6 h-6 text-purple-600 flex-shrink-0 mt-1\" />\n <div>\n <h3 className=\"font-semibold text-gray-900 text-base\">\n Compromisso Social\n </h3>\n <p className=\"text-gray-700 text-sm\">\n Lutamos por uma tecnologia inclusiva que reduza\n desigualdades e promova justiça social.\n </p>\n </div>\n </div>\n\n <div className=\"flex items-start gap-3\">\n <Lightbulb className=\"w-6 h-6 text-yellow-600 flex-shrink-0 mt-1\" />\n <div>\n <h3 className=\"font-semibold text-gray-900 text-base\">\n Inovação Pedagógica\n </h3>\n <p className=\"text-gray-700 text-sm\">\n Criamos métodos de ensino contextualizados, críticos e\n adaptados à realidade brasileira.\n </p>\n </div>\n </div>\n </div>\n\n {/* CTA */}\n <div className=\"mt-8\">\n <a\n href=\"https://www.nucleodetecnologia.com.br/sobrenos\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"inline-flex items-center cursor-pointer bg-brand-500 text-white gap-x-3 px-6 py-3 text-base md:text-lg rounded-full transition-all duration-300 shadow-lg hover:shadow-xl font-bold transform hover:scale-105\"\n >\n <span className=\"whitespace-nowrap\">\n Conheça Nossa História\n </span>\n <ArrowRight className=\"w-5 h-5\" />\n </a>\n </div>\n </div>\n\n {/* Grid de imagens representativas */}\n <div className=\"grid w-full grid-cols-1 gap-4 xl:w-[40%] shrink-0 grid-cols-2 \">\n {/* Card 1: Educação Popular */}\n <div className=\"relative overflow-hidden rounded-lg group\">\n <img\n className=\"object-cover w-full h-[320px] lg:h-auto scale-100 ease-in duration-300 group-hover:scale-110\"\n src=\"https://images.unsplash.com/photo-1427504494785-3a9ca7044f45?q=80&w=1740&auto=format&fit=crop\"\n alt=\"Educação Popular e Colaborativa\"\n />\n <div className=\"absolute inset-0 grid items-end justify-center p-4 bg-gradient-to-b from-transparent to-black/70\">\n <div className=\"text-center\">\n <p className=\"text-xl font-bold text-white\">\n Educação Popular\n </p>\n <p className=\"text-base font-medium text-gray-200\">\n Construção coletiva do saber\n </p>\n </div>\n </div>\n </div>\n\n {/* Card 2: Inclusão Digital */}\n <div className=\"relative overflow-hidden rounded-lg group\">\n <img\n className=\"object-cover w-full h-[320px] lg:h-auto scale-100 ease-in duration-300 group-hover:scale-110\"\n src=\"https://images.unsplash.com/photo-1509062522246-3755977927d7?q=80&w=1732&auto=format&fit=crop\"\n alt=\"Inclusão Digital\"\n />\n <div className=\"absolute inset-0 grid items-end justify-center p-4 bg-gradient-to-b from-transparent to-black/70\">\n <div className=\"text-center\">\n <p className=\"text-xl font-bold text-white\">\n Inclusão Digital\n </p>\n <p className=\"text-base font-medium text-gray-200\">\n Tecnologia para todos\n </p>\n </div>\n </div>\n </div>\n\n {/* Card 3: Pensamento Crítico */}\n <div className=\"relative overflow-hidden rounded-lg group\">\n <img\n className=\"object-cover w-full h-[320px] lg:h-auto scale-100 ease-in duration-300 group-hover:scale-110\"\n src=\"https://images.unsplash.com/photo-1503676260728-1c00da094a0b?q=80&w=1722&auto=format&fit=crop\"\n alt=\"Pensamento Crítico\"\n />\n <div className=\"absolute inset-0 grid items-end justify-center p-4 bg-gradient-to-b from-transparent to-black/70\">\n <div className=\"text-center\">\n <p className=\"text-xl font-bold text-white\">\n Pensamento Crítico\n </p>\n <p className=\"text-base font-medium text-gray-200\">\n Autonomia e reflexão\n </p>\n </div>\n </div>\n </div>\n\n {/* Card 4: Programação Visual */}\n <div className=\"relative overflow-hidden rounded-lg group\">\n <img\n className=\"object-cover w-full h-[320px] lg:h-auto scale-100 ease-in duration-300 group-hover:scale-110\"\n src=\"https://images.unsplash.com/photo-1522202176988-66273c2fd55f?q=80&w=1742&auto=format&fit=crop\"\n alt=\"Programação Visual Colaborativa\"\n />\n <div className=\"absolute inset-0 grid items-end justify-center p-4 bg-gradient-to-b from-transparent to-black/70\">\n <div className=\"text-center\">\n <p className=\"text-xl font-bold text-white\">\n Programação Visual\n </p>\n <p className=\"text-base font-medium text-gray-200\">\n Aprender fazendo\n </p>\n </div>\n </div>\n </div>\n\n {/* Card 5: Transformação Social */}\n <div className=\"relative overflow-hidden rounded-lg group\">\n <img\n className=\"object-cover w-full h-[320px] lg:h-auto scale-100 ease-in duration-300 group-hover:scale-110\"\n src=\"https://images.unsplash.com/photo-1529070538774-1843cb3265df?q=80&w=1740&auto=format&fit=crop\"\n alt=\"Transformação Social\"\n />\n <div className=\"absolute inset-0 grid items-end justify-center p-4 bg-gradient-to-b from-transparent to-black/70\">\n <div className=\"text-center\">\n <p className=\"text-xl font-bold text-white\">\n Transformação Social\n </p>\n <p className=\"text-base font-medium text-gray-200\">\n Tecnologia para mudar\n </p>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </section>\n\n {/* Footer */}\n <Footer />\n </>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/Atividades/Atividades.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'Navbar' is defined but never used. Allowed unused vars must match /^_/u.","line":3,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":3,"endColumn":14,"suggestions":[{"messageId":"removeVar","data":{"varName":"Navbar"},"fix":{"range":[105,117],"text":""},"desc":"Remove unused variable 'Navbar'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Footer' is defined but never used. Allowed unused vars must match /^_/u.","line":4,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":4,"endColumn":14,"suggestions":[{"messageId":"removeVar","data":{"varName":"Footer"},"fix":{"range":[151,163],"text":""},"desc":"Remove unused variable 'Footer'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'ChevronLeft' is defined but never used. Allowed unused vars must match /^_/u.","line":9,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":9,"endColumn":21,"suggestions":[{"messageId":"removeVar","data":{"varName":"ChevronLeft"},"fix":{"range":[419,431],"text":""},"desc":"Remove unused variable 'ChevronLeft'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'ChevronRight' is defined but never used. Allowed unused vars must match /^_/u.","line":9,"column":23,"nodeType":"Identifier","messageId":"unusedVar","endLine":9,"endColumn":35,"suggestions":[{"messageId":"removeVar","data":{"varName":"ChevronRight"},"fix":{"range":[430,444],"text":""},"desc":"Remove unused variable 'ChevronRight'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":4,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import React, { useState, useMemo } from \"react\";\nimport { useNavigate } from \"react-router-dom\";\nimport Navbar from \"../../components/Navbar\";\nimport Footer from \"../HomePage/Footer\";\nimport { gameRegistryUtils } from \"../../config/gameRegistry\";\nimport { difficultyLevels } from \"../../config/difficulty\";\nimport { gameCategory } from \"../../config/categories\";\nimport { gameType } from \"../../config/type\";\nimport { ChevronLeft, ChevronRight } from \"lucide-react\";\nimport { useAtividadesTour } from \"./hooks/useAtividadesTour\";\nimport \"shepherd.js/dist/css/shepherd.css\";\nimport \"../../styles/shepherd-theme.css\";\nimport { calculateProgress } from \"./utils/progress\";\n\nconst Atividades = () => {\n const navigate = useNavigate();\n\n // Inicializa o tour guiado\n useAtividadesTour();\n const [selectedCategory, setSelectedCategory] = useState(\"all\");\n const [selectedDifficulty, setSelectedDifficulty] = useState(\"all\");\n const [selectedType, setSelectedType] = useState(\"all\");\n const [searchQuery, setSearchQuery] = useState(\"\");\n const [currentPage, setCurrentPage] = useState(1);\n const [itemsPerPage, setItemsPerPage] = useState(10);\n\n // Obter jogos filtrados\n const filteredGames = useMemo(() => {\n const games = gameRegistryUtils.getActiveGames();\n\n // Mapeamos os jogos injetando o progresso calculado ANTES de filtrar\n const gamesWithProgress = games.map((game) => ({\n ...game,\n progress: calculateProgress(game.gameId || game.id, game.fases),\n }));\n\n return gamesWithProgress.filter((game) => {\n const matchesCategory =\n selectedCategory === \"all\" || game.categoria === selectedCategory;\n const matchesDifficulty =\n selectedDifficulty === \"all\" || game.dificuldade === selectedDifficulty;\n const matchesType = selectedType === \"all\" || game.type === selectedType;\n\n const query = searchQuery.toLowerCase().trim();\n const matchesSearch =\n !query ||\n game.gameName.toLowerCase().includes(query) ||\n game.descricao.toLowerCase().includes(query) ||\n (game.conceitos &&\n game.conceitos.some((c) => c.toLowerCase().includes(query)));\n\n return (\n matchesCategory && matchesDifficulty && matchesType && matchesSearch\n );\n });\n }, [selectedCategory, selectedDifficulty, selectedType, searchQuery]);\n\n // Paginação\n const totalPages = Math.ceil(filteredGames.length / itemsPerPage);\n const startIndex = (currentPage - 1) * itemsPerPage;\n const endIndex = startIndex + itemsPerPage;\n const currentGames = filteredGames.slice(startIndex, endIndex);\n\n // Reset para página 1 quando filtros mudam\n React.useEffect(() => {\n setCurrentPage(1);\n }, [\n selectedCategory,\n selectedDifficulty,\n selectedType,\n searchQuery,\n itemsPerPage,\n ]);\n\n const getDifficultyColor = (difficulty) => {\n return difficultyLevels[difficulty]?.color || \"#6c757d\";\n };\n\n const getCategoryColor = (category) => {\n return gameCategory[category]?.color || \"#6c757d\";\n };\n\n const getTypeColor = (type) => {\n return gameType[type]?.color || \"#6c757d\";\n };\n\n const getNameFromType = (type) => {\n return gameType[type]?.name || type;\n };\n\n const handleGameSelect = (game) => {\n if (game.route) {\n navigate(game.route);\n } else {\n navigate(`/games/${game.gameId || game.id}`);\n }\n };\n\n const handleClearFilters = () => {\n setSelectedCategory(\"all\");\n setSelectedDifficulty(\"all\");\n setSelectedType(\"all\");\n setSearchQuery(\"\");\n };\n\n return (\n <>\n <Navbar />\n\n {/* Background Gradient */}\n <div\n className=\"isolate -z-10 fixed h-full w-full top-0 left-0\"\n style={{\n background: \"#EAEEF5\",\n }}\n ></div>\n\n <section className=\"flex flex-col relative mt-32 lg:mt-[195px]\">\n {/* Container principal */}\n <div className=\"mx-auto max-w-7xl px-6 py-24 sm:py-24 lg:py-32 lg:px-8\">\n {/* Header */}\n <div className=\"mb-8 flex flex-col\">\n <div className=\"flex flex-col justify-start gap-4\">\n <h3 className=\"font-bold text-gray-900 tracking-tight text-4xl sm:text-5xl\">\n Atividades e Jogos\n </h3>\n <p className=\"mt-2 font-normal text-gray-700 text-base\">\n Explore nossos jogos educativos para aprender programação.\n </p>\n </div>\n </div>\n\n {/* Filtros */}\n <div className=\"mb-12 relative bg-white/50 backdrop-blur-lg shadow-2xl rounded-xl border border-gray-200 p-6\">\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6\">\n {/* Busca */}\n <div className=\"lg:col-span-4\">\n <label className=\"block text-sm font-semibold text-gray-900 mb-3\">\n Buscar atividades\n </label>\n <div className=\"relative\">\n <input\n type=\"text\"\n placeholder=\"Digite o nome ou conceito...\"\n className=\"w-full px-6 py-3 pr-12 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition-all bg-white text-gray-900\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n />\n <div className=\"absolute inset-y-0 right-0 flex items-center pr-4\">\n <svg\n className=\"w-5 h-5 text-gray-400\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth=\"2\"\n d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\"\n />\n </svg>\n </div>\n </div>\n </div>\n\n {/* Categoria */}\n <div>\n <label className=\"block text-sm font-semibold text-gray-900 mb-3\">\n Categoria\n </label>\n <select\n className=\"w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition-all bg-white text-gray-900\"\n value={selectedCategory}\n onChange={(e) => setSelectedCategory(e.target.value)}\n >\n <option value=\"all\">Todas as categorias</option>\n {Object.entries(gameCategory).map(([key, category]) => (\n <option key={key} value={key}>\n {category.name}\n </option>\n ))}\n </select>\n </div>\n\n {/* Tipo */}\n <div>\n <label className=\"block text-sm font-semibold text-gray-900 mb-3\">\n Tipo\n </label>\n <select\n className=\"w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition-all bg-white text-gray-900\"\n value={selectedType}\n onChange={(e) => setSelectedType(e.target.value)}\n >\n <option value=\"all\">Todos os tipos</option>\n {Object.entries(gameType).map(([key, type]) => (\n <option key={key} value={key}>\n {type.name}\n </option>\n ))}\n </select>\n </div>\n\n {/* Dificuldade */}\n <div>\n <label className=\"block text-sm font-semibold text-gray-900 mb-3\">\n Dificuldade\n </label>\n <select\n className=\"w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition-all bg-white text-gray-900\"\n value={selectedDifficulty}\n onChange={(e) => setSelectedDifficulty(e.target.value)}\n >\n <option value=\"all\">Todas as dificuldades</option>\n {Object.entries(difficultyLevels).map(([key, level]) => (\n <option key={key} value={key}>\n {level.name}\n </option>\n ))}\n </select>\n </div>\n\n {/* Itens por página */}\n <div>\n <label className=\"block text-sm font-semibold text-gray-900 mb-3\">\n Itens por página\n </label>\n <select\n className=\"w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition-all bg-white text-gray-900\"\n value={itemsPerPage}\n onChange={(e) => setItemsPerPage(Number(e.target.value))}\n >\n <option value={10}>10 atividades</option>\n <option value={20}>20 atividades</option>\n <option value={30}>30 atividades</option>\n </select>\n </div>\n </div>\n\n {/* Botão limpar filtros */}\n {(selectedCategory !== \"all\" ||\n selectedDifficulty !== \"all\" ||\n selectedType !== \"all\" ||\n searchQuery) && (\n <div className=\"mt-6 text-center\">\n <button\n onClick={handleClearFilters}\n className=\"px-6 py-3 bg-gradient-to-r from-red-600 via-pink-600 to-purple-600 hover:from-red-700 hover:via-pink-700 hover:to-purple-700 text-white font-bold rounded-full shadow-lg transition-all duration-300 transform hover:scale-105\"\n >\n Limpar todos os filtros\n </button>\n </div>\n )}\n </div>\n\n {/* Resultado e contador */}\n <div className=\"flex items-center justify-between mb-8\">\n <h4 className=\"text-2xl font-bold text-gray-900\">\n {searchQuery\n ? `Resultados para \"${searchQuery}\"`\n : \"Todas as Atividades\"}\n </h4>\n <div className=\"bg-brand-500 text-white px-6 py-2 rounded-full text-sm font-bold\">\n {filteredGames.length}{\" \"}\n {filteredGames.length === 1 ? \"atividade\" : \"atividades\"}\n </div>\n </div>\n\n {/* Grid de Cards */}\n {currentGames.length > 0 ? (\n <>\n <div className=\"my-3 grid grid-cols-1 gap-4 md:gap-7 sm:grid-cols-2 lg:grid-cols-2\">\n {currentGames.map((game) => (\n <div\n key={game.gameId || game.id}\n onClick={() => handleGameSelect(game)}\n className=\"relative bg-white/50 backdrop-blur-lg shadow-md hover:shadow-xl rounded-xl border border-gray-200 p-6 flex flex-col md:flex-row gap-6 cursor-pointer transition-all duration-300 hover:scale-[1.02] group data-[prd-ready=false]:opacity-50\"\n >\n {/* Ícone */}\n <div className=\"flex-shrink-0 rounded-lg h-16 w-16 md:h-20 md:w-20\">\n <div className=\"bg-gradient-to-br from-blue-100 to-purple-100 relative w-full h-full rounded-xl flex items-center justify-center overflow-hidden text-4xl md:text-5xl group-hover:scale-110 transition-transform duration-300\">\n {game.icon}\n </div>\n </div>\n\n {/* Conteúdo */}\n <div className=\"flex h-full w-full flex-col items-start gap-3 justify-between\">\n <div className=\"flex w-full flex-col gap-3\">\n <div className=\"flex w-full flex-row items-start justify-between gap-3\">\n <h3 className=\"max-w-[400px] overflow-hidden truncate text-ellipsis font-semibold text-gray-900 tracking-tight text-lg md:text-xl\">\n {game.gameName}\n </h3>\n\n {/* Badges */}\n <div className=\"flex flex-wrap gap-2 justify-end\">\n <span\n className=\"px-3 py-1 text-xs font-bold rounded-full text-white whitespace-nowrap\"\n style={{\n backgroundColor: getCategoryColor(\n game.categoria,\n ),\n }}\n >\n {gameCategory[game.categoria]?.name}\n </span>\n </div>\n </div>\n\n <p className=\"line-clamp-2 font-normal text-gray-700 text-sm md:text-base\">\n {game.descricao}\n </p>\n\n {/* Informações adicionais */}\n <div className=\"flex flex-wrap gap-3 text-xs text-gray-600\">\n <span className=\"flex items-center gap-1\">\n ⏱️ {game.tempoEstimado}\n </span>\n <span className=\"flex items-center gap-1\">\n 🎯 {game.faixaEtaria}\n </span>\n <span className=\"flex items-center gap-1\">\n 📊 {game.fases?.length || 0} fases\n </span>\n </div>\n\n {/* Conceitos */}\n {game.conceitos && game.conceitos.length > 0 && (\n <div className=\"flex flex-wrap gap-2\">\n {game.conceitos.slice(0, 3).map((concept) => (\n <span\n key={concept}\n className=\"px-3 py-1 bg-gray-100 text-gray-700 text-xs rounded-full\"\n >\n {concept}\n </span>\n ))}\n {game.conceitos.length > 3 && (\n <span className=\"px-3 py-1 bg-gray-100 text-gray-700 text-xs rounded-full\">\n +{game.conceitos.length - 3}\n </span>\n )}\n </div>\n )}\n </div>\n\n {/* Badges de tipo e dificuldade */}\n <div className=\"flex flex-wrap gap-2 w-full\">\n <div className=\"relative bg-white/50 backdrop-blur-lg shadow-lg rounded-md border border-gray-200 inline-flex items-center\">\n <span\n className=\"px-3 py-1 text-xs font-bold text-white rounded-l-md\"\n style={{ backgroundColor: getTypeColor(game.type) }}\n >\n {getNameFromType(game.type)}\n </span>\n <span\n className=\"px-3 py-1 text-xs font-bold text-white rounded-r-md\"\n style={{\n backgroundColor: getDifficultyColor(\n game.dificuldade,\n ),\n }}\n >\n {difficultyLevels[game.dificuldade]?.name}\n </span>\n </div>\n </div>\n </div>\n {/* BARRA DE PROGRESSO SUTIL NA BORDA INFERIOR */}\n {game.progress > 0 && (\n <div className=\"absolute bottom-0 left-0 w-full h-1.5 bg-gray-200/50\">\n <div\n className=\"h-full bg-green-500 transition-all duration-1000 ease-out\"\n style={{\n width: `${game.progress}%`,\n boxShadow:\n game.progress === 100\n ? \"0 0 10px rgba(34, 197, 94, 0.5)\"\n : \"none\",\n }}\n />\n </div>\n )}\n </div>\n ))}\n </div>\n\n {/* Paginação */}\n {totalPages > 1 && (\n <div className=\"mt-12 flex items-center justify-center gap-4\">\n <button\n onClick={() =>\n setCurrentPage((prev) => Math.max(1, prev - 1))\n }\n disabled={currentPage === 1}\n className=\"p-2 rounded-lg bg-white border border-gray-200 hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed transition-all\"\n >\n <ChevronLeft className=\"w-5 h-5 text-gray-900\" />\n </button>\n\n <div className=\"flex items-center gap-2\">\n {Array.from({ length: totalPages }, (_, i) => i + 1).map(\n (page) => (\n <button\n key={page}\n onClick={() => setCurrentPage(page)}\n className={`px-4 py-2 rounded-lg font-semibold transition-all ${\n currentPage === page\n ? \"bg-gradient-to-r from-red-600 via-pink-600 to-purple-600 text-white shadow-lg\"\n : \"bg-white border border-gray-200 text-gray-900 hover:bg-gray-50\"\n }`}\n >\n {page}\n </button>\n ),\n )}\n </div>\n\n <button\n onClick={() =>\n setCurrentPage((prev) => Math.min(totalPages, prev + 1))\n }\n disabled={currentPage === totalPages}\n className=\"p-2 rounded-lg bg-white border border-gray-200 hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed transition-all\"\n >\n <ChevronRight className=\"w-5 h-5 text-gray-900\" />\n </button>\n </div>\n )}\n </>\n ) : (\n // Estado vazio\n <div className=\"text-center py-20\">\n <div className=\"text-8xl mb-8\">🎮</div>\n <h3 className=\"text-3xl font-bold text-gray-900 mb-6\">\n Nenhuma atividade encontrada\n </h3>\n <p className=\"text-xl text-gray-700 mb-8 max-w-md mx-auto\">\n Não encontramos atividades que correspondam aos seus critérios.\n Que tal tentar uma busca diferente?\n </p>\n <button\n onClick={handleClearFilters}\n className=\"px-8 py-4 bg-gradient-to-r from-red-600 via-pink-600 to-purple-600 hover:from-red-700 hover:via-pink-700 hover:to-purple-700 text-white font-bold rounded-full shadow-lg transition-all duration-300 transform hover:scale-105\"\n >\n Ver todas as atividades\n </button>\n </div>\n )}\n </div>\n </section>\n\n {/* Footer */}\n <Footer />\n </>\n );\n};\n\nexport default Atividades;\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/Atividades/config/tourSteps.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/Atividades/hooks/useAtividadesTour.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/Atividades/utils/progress.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/Educadores/Educadores.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'Navbar' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":14,"suggestions":[{"messageId":"removeVar","data":{"varName":"Navbar"},"fix":{"range":[7,19],"text":""},"desc":"Remove unused variable 'Navbar'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Footer' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":14,"suggestions":[{"messageId":"removeVar","data":{"varName":"Footer"},"fix":{"range":[53,65],"text":""},"desc":"Remove unused variable 'Footer'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Gamepad2' is defined but never used. Allowed unused vars must match /^_/u.","line":4,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":4,"endColumn":18,"suggestions":[{"messageId":"removeVar","data":{"varName":"Gamepad2"},"fix":{"range":[149,158],"text":""},"desc":"Remove unused variable 'Gamepad2'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'BookOpen' is defined but never used. Allowed unused vars must match /^_/u.","line":4,"column":20,"nodeType":"Identifier","messageId":"unusedVar","endLine":4,"endColumn":28,"suggestions":[{"messageId":"removeVar","data":{"varName":"BookOpen"},"fix":{"range":[157,167],"text":""},"desc":"Remove unused variable 'BookOpen'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Gift' is defined but never used. Allowed unused vars must match /^_/u.","line":4,"column":30,"nodeType":"Identifier","messageId":"unusedVar","endLine":4,"endColumn":34,"suggestions":[{"messageId":"removeVar","data":{"varName":"Gift"},"fix":{"range":[167,173],"text":""},"desc":"Remove unused variable 'Gift'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Users' is defined but never used. Allowed unused vars must match /^_/u.","line":4,"column":36,"nodeType":"Identifier","messageId":"unusedVar","endLine":4,"endColumn":41,"suggestions":[{"messageId":"removeVar","data":{"varName":"Users"},"fix":{"range":[173,180],"text":""},"desc":"Remove unused variable 'Users'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":6,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import Navbar from \"../../components/Navbar\";\nimport Footer from \"../HomePage/Footer\";\nimport educadoresImg from \"./assets/educadores.png\";\nimport { Gamepad2, BookOpen, Gift, Users } from \"lucide-react\";\n\nconst stepsData = [\n {\n id: 1,\n title: \"Explore a Plataforma\",\n description:\n \"Navegue pelos jogos educativos e atividades. Teste você mesmo para entender a experiência dos alunos.\",\n },\n {\n id: 2,\n title: \"Acesse a Documentação\",\n description:\n \"Consulte nossos guias pedagógicos completos com planos de aula, estratégias de ensino e atividades desplugadas.\",\n },\n {\n id: 3,\n title: \"Prepare sua Aula\",\n description:\n \"Escolha as atividades adequadas ao nível da turma e planeje a sequência didática usando nossos recursos.\",\n },\n {\n id: 4,\n title: \"Ensine e Inspire\",\n description:\n \"Aplique em sala de aula, acompanhe o progresso dos alunos e ajuste conforme necessário. Sua turma vai adorar!\",\n },\n];\n\nexport default function Educadores() {\n return (\n <>\n {/* Navegação */}\n <Navbar />\n\n <section className=\"relative mt-32 lg:mt-[195px] bg-gray-100\">\n <div className=\"max-w-6xl mx-auto px-4 sm:px-6\">\n <div className=\"py-12 md:py-20\">\n {/* Título Principal */}\n <div className=\"max-w-3xl mx-auto text-center pb-12 md:pb-16 pt-8 md:pt-12\">\n <h2 className=\"font-bold text-gray-900 tracking-tight text-4xl sm:text-5xl\">\n Seja um Educador Popular de Tecnologia\n </h2>\n <p className=\"mt-6 text-lg text-gray-700\">\n Saiba como utilizar o Decoda para transformar suas aulas de\n programação em experiências envolventes e eficazes.\n </p>\n </div>\n\n <div className=\"flex justify-center pb-12 md:pb-16\">\n <div className=\"relative flex-1 overflow-hidden justify-center items-center max-w-4xl\">\n <div className=\"w-full h-[400px] md:h-[500px]\">\n <img\n src={educadoresImg}\n alt=\"Educadores ensinando programação\"\n className=\"w-full h-full object-cover rounded-lg shadow-2xl\"\n />\n </div>\n </div>\n </div>\n\n <div className=\"relative pb-12\">\n <div\n className=\"hidden lg:block absolute top-4 left-32 right-32 mt-px h-0.5 bg-gradient-to-r from-red-300 via-pink-300 to-purple-300 -z-10\"\n aria-hidden=\"true\"\n ></div>\n\n <div className=\"max-w-sm mx-auto grid gap-12 sm:grid-cols-2 sm:max-w-3xl lg:grid-cols-4 lg:max-w-none items-start\">\n {stepsData.map((step) => (\n <div key={step.id} className=\"text-center\">\n <div className=\"w-9 h-9 bg-brand-500 text-white text-[15px] font-bold rounded-full inline-flex items-center justify-center mb-3 shadow-lg\">\n {step.id}\n </div>\n <h3 className=\"font-semibold text-gray-900 tracking-tight text-lg mt-3\">\n {step.title}\n </h3>\n <div className=\"font-normal text-gray-700 text-base mt-3\">\n {step.description}\n </div>\n </div>\n ))}\n </div>\n </div>\n\n {/* CTA Button */}\n <div className=\"flex justify-center\">\n <a\n href=\"/docs\"\n target=\"_blank\"\n className=\"inline-flex items-center bg-brand-500 text-white gap-x-3 px-6 py-3 text-lg rounded-full transition-all duration-300 shadow-lg hover:shadow-xl font-bold transform hover:scale-105\"\n >\n Acesse a Documentação\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"w-5 h-5\"\n viewBox=\"0 0 24 24\"\n >\n <path\n fill=\"currentColor\"\n d=\"M17.079 12.5H5.5q-.213 0-.357-.143T5 12t.143-.357t.357-.143h11.579L11.64 6.062q-.147-.146-.153-.345t.158-.363q.165-.16.354-.163q.189-.002.354.163l6.08 6.08q.131.132.184.268q.053.137.053.298t-.053.298q-.052.137-.183.268l-6.081 6.08q-.14.14-.341.15q-.202.01-.367-.15q-.165-.165-.165-.356q0-.192.165-.357z\"\n />\n </svg>\n </a>\n </div>\n\n {/* Seção de Benefícios Extras */}\n <div className=\"max-w-3xl mx-auto mt-20\">\n <h3 className=\"font-bold text-brand-500 tracking-tight text-3xl text-center mb-8\">\n Por que escolher o Decoda?\n </h3>\n <div className=\"grid gap-6 sm:grid-cols-2\">\n <div className=\"bg-white/70 backdrop-blur-sm p-6 rounded-lg shadow-md border border-pink-100 hover:shadow-xl transition-shadow duration-300\">\n <div className=\"flex items-center gap-3 mb-2\">\n <div className=\"p-2 rounded-lg bg-gradient-to-r from-red-100 to-pink-100\">\n <Gamepad2 className=\"w-6 h-6 text-red-600\" />\n </div>\n <h4 className=\"font-semibold text-gray-900 text-lg\">\n Jogos Educativos Envolventes\n </h4>\n </div>\n <p className=\"text-gray-700 text-base\">\n Aprenda programação através de desafios divertidos e\n contextualizados que mantêm os alunos motivados.\n </p>\n </div>\n <div className=\"bg-white/70 backdrop-blur-sm p-6 rounded-lg shadow-md border border-pink-100 hover:shadow-xl transition-shadow duration-300\">\n <div className=\"flex items-center gap-3 mb-2\">\n <div className=\"p-2 rounded-lg bg-gradient-to-r from-pink-100 to-purple-100\">\n <BookOpen className=\"w-6 h-6 text-pink-600\" />\n </div>\n <h4 className=\"font-semibold text-gray-900 text-lg\">\n Material Pedagógico Completo\n </h4>\n </div>\n <p className=\"text-gray-700 text-base\">\n Guias detalhados, planos de aula prontos e atividades\n desplugadas para complementar suas aulas.\n </p>\n </div>\n <div className=\"bg-white/70 backdrop-blur-sm p-6 rounded-lg shadow-md border border-pink-100 hover:shadow-xl transition-shadow duration-300\">\n <div className=\"flex items-center gap-3 mb-2\">\n <div className=\"p-2 rounded-lg bg-gradient-to-r from-purple-100 to-pink-100\">\n <Gift className=\"w-6 h-6 text-purple-600\" />\n </div>\n <h4 className=\"font-semibold text-gray-900 text-lg\">\n 100% Gratuito\n </h4>\n </div>\n <p className=\"text-gray-700 text-base\">\n Sem cadastro, sem instalação, sem custos. Acesse direto do\n navegador e comece a ensinar hoje mesmo.\n </p>\n </div>\n <div className=\"bg-white/70 backdrop-blur-sm p-6 rounded-lg shadow-md border border-pink-100 hover:shadow-xl transition-shadow duration-300\">\n <div className=\"flex items-center gap-3 mb-2\">\n <div className=\"p-2 rounded-lg bg-gradient-to-r from-red-100 to-purple-100\">\n <Users className=\"w-6 h-6 text-purple-700\" />\n </div>\n <h4 className=\"font-semibold text-gray-900 text-lg\">\n Aprendizagem Colaborativa\n </h4>\n </div>\n <p className=\"text-gray-700 text-base\">\n Promova discussões em grupo e desenvolvimento do pensamento\n crítico através de desafios compartilhados.\n </p>\n </div>\n </div>\n </div>\n </div>\n </div>\n </section>\n\n {/* Footer */}\n <Footer />\n </>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/Faq/Faq.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'Navbar' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":14,"suggestions":[{"messageId":"removeVar","data":{"varName":"Navbar"},"fix":{"range":[7,19],"text":""},"desc":"Remove unused variable 'Navbar'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Footer' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":14,"suggestions":[{"messageId":"removeVar","data":{"varName":"Footer"},"fix":{"range":[53,65],"text":""},"desc":"Remove unused variable 'Footer'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import Navbar from \"../../components/Navbar\";\nimport Footer from \"../HomePage/Footer\";\n\nconst faqData = [\n {\n id: 1,\n question: \"O que é o Decoda?\",\n answer:\n \"O Decoda é uma plataforma educacional de programação visual desenvolvida especialmente para o ensino de lógica de programação. Utilizamos blocos visuais arrastar-e-soltar baseados no Google Blockly, eliminando a barreira da sintaxe complexa e focando no raciocínio lógico através de jogos educativos contextualizados.\",\n },\n {\n id: 2,\n question: \"Por que usar programação com blocos ao invés de código texto?\",\n answer:\n 'Os blocos visuais eliminam erros de sintaxe (não há como \"escrever errado\"), permitem foco total na lógica de programação, são intuitivos e acessíveis para todas as idades, e oferecem feedback visual imediato. Os blocos só encaixam onde fazem sentido logicamente, permitindo que estudantes se concentrem em resolver problemas ao invés de decorar sintaxe.',\n },\n {\n id: 3,\n question: \"A plataforma é gratuita?\",\n answer:\n \"Sim! O Decoda é 100% gratuito. Não requer cadastro, instalação ou pagamento. Todos as atividades, recursos pedagógicos e documentação estão disponíveis gratuitamente para educadores e alunos. Acreditamos que a educação em programação deve ser acessível a todos.\",\n },\n {\n id: 4,\n question: \"O que é Computação Desplugada?\",\n answer:\n \"É uma metodologia que ensina conceitos de programação e pensamento computacional sem usar computadores, através de atividades práticas, jogos e brincadeiras. Nossa documentação oferece diversas atividades desplugadas que educadores podem aplicar antes de usar a plataforma digital, facilitando o entendimento dos conceitos e reduzindo a ansiedade tecnológica.\",\n },\n {\n id: 5,\n question: \"Quais conceitos de programação são ensinados?\",\n answer:\n \"Baseamos nosso ensino nos 4 fundamentos da programação e nos pilares do pensamento computacional:\",\n list: [\n \"<strong>Sequências:</strong> Ordem lógica de comandos\",\n \"<strong>Condicionais:</strong> Decisões SE/ENTÃO\",\n \"<strong>Repetição:</strong> Estruturas de repetição\",\n \"<strong>Funções:</strong> Agrupamento e reutilização\",\n \"<strong>Variáveis:</strong> Armazenamento de dados\",\n \"<strong>Eventos:</strong> Interações do usuário\",\n ],\n },\n {\n id: 6,\n question: \"Preciso instalar algum programa?\",\n answer:\n \"Não! O Decoda é uma plataforma web que funciona diretamente no navegador. Basta acessar o site e começar a programar. Não requer instalação, downloads ou configurações complexas. Funciona em computadores, tablets e smartphones com qualquer navegador moderno.\",\n },\n {\n id: 7,\n question: \"A plataforma oferece recursos para educadores?\",\n answer: \"Sim! Oferecemos documentação completa para educadores com:\",\n list: [\n \"Guias pedagógicos detalhados\",\n \"Estratégias de ensino para sala de aula\",\n \"Planos de aula prontos\",\n \"Atividades de computação desplugada\",\n \"Ferramentas de avaliação de aprendizado\",\n \"Dicas de gestão e organização da sala\",\n \"Resolução de problemas comuns\",\n ],\n },\n {\n id: 8,\n question: \"Meu progresso é salvo automaticamente?\",\n answer:\n \"Sim! O Decoda salva automaticamente seu progresso no navegador. Você pode fechar a aba e voltar depois que seu trabalho estará preservado. Isso funciona sem necessidade de cadastro ou login, mantendo a privacidade e simplicidade da plataforma.\",\n },\n {\n id: 9,\n question: \"Posso usar o Decoda em sala de aula?\",\n answer:\n \"Absolutamente! O Decoda foi desenvolvido especificamente para uso educacional em sala de aula. A plataforma promove aprendizagem colaborativa, discussão coletiva de estratégias e desenvolvimento do pensamento crítico. Não requer cadastro de alunos, facilitando a adoção em ambientes escolares.\",\n },\n {\n id: 10,\n question: \"A plataforma funciona em dispositivos móveis?\",\n answer:\n \"Sim! O Decoda possui design responsivo e funciona em tablets e smartphones. A interface se adapta automaticamente ao tamanho da tela, permitindo programação por toque em dispositivos móveis. Recomendamos tablets para uma experiência mais confortável, mas smartphones também são suportados.\",\n },\n];\n\nexport default function Faq() {\n return (\n <>\n {/* Navegação */}\n <Navbar />\n\n <section className=\"mt-32 lg:mt-[195px] bg-gray-100 px-10 md:px-0\">\n {/* Container principal */}\n <div className=\"mx-auto max-w-7xl py-24 sm:py-32 lg:py-40\">\n <div className=\"mx-auto max-w-4xl\">\n <h2 className=\"font-bold text-gray-900 tracking-tight text-4xl sm:text-5xl\">\n Perguntas Frequentes\n </h2>\n\n <dl className=\"mt-10 space-y-4 divide-y divide-gray-300\">\n {faqData.map((faq, index) => (\n <div\n key={faq.id}\n className={index === 0 ? \"pt-3 pb-6\" : \"pt-4 pb-6\"}\n >\n <input\n type=\"checkbox\"\n id={`faq-toggle-${faq.id}`}\n className=\"peer hidden\"\n />\n <label\n htmlFor={`faq-toggle-${faq.id}`}\n className=\"flex w-full items-start justify-between text-left cursor-pointer\"\n >\n <span className=\"font-semibold text-gray-900 tracking-tight text-base\">\n {faq.question}\n </span>\n <span className=\"ml-6 flex h-7 items-center text-blue-600\">\n <svg\n className=\"size-6 peer-checked:hidden\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n strokeWidth=\"1.5\"\n stroke=\"currentColor\"\n aria-hidden=\"true\"\n width=\"16\"\n height=\"16\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n d=\"M12 6v12m6-6H6\"\n />\n </svg>\n <svg\n className=\"hidden size-6 peer-checked:block\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n strokeWidth=\"1.5\"\n stroke=\"currentColor\"\n aria-hidden=\"true\"\n width=\"16\"\n height=\"16\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n d=\"M18 12H6\"\n />\n </svg>\n </span>\n </label>\n <dd className=\"pr-12 max-h-0 overflow-hidden peer-checked:max-h-[1000px] peer-checked:pt-6 transition-all duration-300\">\n <p className=\"font-normal text-gray-700 text-base\">\n {faq.answer}\n </p>\n {faq.list && (\n <ul className=\"list-disc list-inside space-y-1 text-gray-700 text-base mt-2\">\n {faq.list.map((item, idx) => (\n <li\n key={idx}\n dangerouslySetInnerHTML={{ __html: item }}\n />\n ))}\n </ul>\n )}\n </dd>\n </div>\n ))}\n </dl>\n </div>\n </div>\n </section>\n\n {/* Footer */}\n <Footer />\n </>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/HomePage/About.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'Link' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":14,"suggestions":[{"messageId":"removeVar","data":{"varName":"Link"},"fix":{"range":[0,40],"text":""},"desc":"Remove unused variable 'Link'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'SectionTitle' is defined but never used. Allowed unused vars must match /^_/u.","line":3,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":3,"endColumn":20,"suggestions":[{"messageId":"removeVar","data":{"varName":"SectionTitle"},"fix":{"range":[93,111],"text":""},"desc":"Remove unused variable 'SectionTitle'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { Link } from \"react-router-dom\";\nimport GroupImage from \"./assets/group.png\";\nimport SectionTitle from \"./SectionTitle\";\nconst About = () => {\n return (\n <div id=\"about-section\" className=\" py-10 lg:pt-[72px] px-10 lg:px-[220px]\">\n <SectionTitle title=\"Quem somos?\" squaresColor=\"green-100\" />\n <div className=\"flex flex-row mt-8\">\n <div className=\"hidden lg:block w-1/2\">\n <img\n src={GroupImage}\n alt=\"Imagem com um grupo de pessoas na frente de computadores em reunião\"\n />\n </div>\n <div className=\"w-full lg:w-1/2\">\n <h2 className=\"text-[32px] font-bold font-title\">\n <span className=\"text-[#FE0002]\">DECODA</span> é a plataforma do\n NT-MTST que facilita o processo de educação popular em programação\n </h2>\n <p className=\"text-[18px] mt-5\">\n Voltada tanto para alunos, quanto para professores com interesse em\n aprender e ensinar programação. Inspirada na pedagogia freiriana,\n tem como origem ser material de apoio as aulas correntes do Núcleo\n de Tecnologia do MTST. Desde 2020, o Núcleo de Tecnologia do\n MTST promove mutirões e formações nas periferias, ensinando\n programação, inclusão digital e acesso a direitos.\n </p>\n <div className=\"w-full mt-5\">\n <Link to=\"/sobre\">\n <button className=\"bg-brand-400 text-white py-4 px-8 rounded-[32px] text-[20px] font-bold hover:scale-110\">\n Saiba mais sobre quem somos\n </button>\n </Link>\n <a href=\"https://www.nucleodetecnologia.com.br\" target=\"_blank\">\n <button className=\"mt-5 bg-white border-2 border-brand-400 text-black py-4 px-8 rounded-[32px] text-[20px] font-bold hover:scale-110\">\n Saiba mais sobre o NT-MTST\n </button>\n </a>\n </div>\n </div>\n </div>\n </div>\n );\n};\n\nexport default About;\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/HomePage/CtaPlayground.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'React' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":13,"suggestions":[{"messageId":"removeVar","data":{"varName":"React"},"fix":{"range":[7,13],"text":""},"desc":"Remove unused variable 'React'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'inComment' is assigned a value but never used. Allowed unused vars must match /^_/u.","line":78,"column":9,"nodeType":"Identifier","messageId":"unusedVar","endLine":78,"endColumn":18,"suggestions":[{"messageId":"removeVar","data":{"varName":"inComment"},"fix":{"range":[1836,1858],"text":""},"desc":"Remove unused variable 'inComment'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import React, { useState, useEffect, useRef } from \"react\";\nimport fatorialSvg from \"./assets/fatorial.svg\";\n\nconst CtaPlayground = () => {\n const [activeTab, setActiveTab] = useState(\"blocos\");\n const [displayedCode, setDisplayedCode] = useState(\"\");\n const [isTyping, setIsTyping] = useState(true);\n const typingTimeoutRef = useRef(null);\n const terminalRef = useRef(null);\n\n // Função para aplicar syntax highlighting\n const highlightCode = (code, language) => {\n if (language === \"console\") {\n // Highlight para console\n return code.split(\"\\n\").map((line, i) => {\n if (line.startsWith(\">\")) {\n return (\n <div key={i}>\n <span className=\"text-green-400\">></span>\n <span className=\"text-gray-300\">{line.substring(1)}</span>\n </div>\n );\n }\n return (\n <div key={i} className=\"text-gray-300\">\n {line}\n </div>\n );\n });\n }\n\n // Palavras-chave por linguagem\n const keywords = {\n javascript: [\n \"var\",\n \"for\",\n \"count\",\n \"if\",\n \"else\",\n \"function\",\n \"return\",\n \"const\",\n \"let\",\n \"typeof\",\n \"window\",\n \"alert\",\n ],\n python: [\n \"from\",\n \"import\",\n \"for\",\n \"in\",\n \"range\",\n \"int\",\n \"if\",\n \"else\",\n \"def\",\n \"return\",\n \"print\",\n \"isinstance\",\n \"str\",\n ],\n };\n\n const types = {\n javascript: [\"Number\"],\n python: [\"Number\", \"numbers\"],\n };\n\n const currentKeywords = keywords[language] || [];\n const currentTypes = types[language] || [];\n\n // Tokenizar o código\n const tokens = [];\n let currentToken = \"\";\n let inString = false;\n let stringChar = \"\";\n let inComment = false;\n\n for (let i = 0; i < code.length; i++) {\n const char = code[i];\n const nextChar = code[i + 1];\n\n // Detectar início/fim de string\n if ((char === '\"' || char === \"'\") && code[i - 1] !== \"\\\\\") {\n if (!inString) {\n if (currentToken) {\n tokens.push({ type: \"text\", value: currentToken });\n currentToken = \"\";\n }\n inString = true;\n stringChar = char;\n currentToken = char;\n } else if (char === stringChar) {\n currentToken += char;\n tokens.push({ type: \"string\", value: currentToken });\n currentToken = \"\";\n inString = false;\n stringChar = \"\";\n } else {\n currentToken += char;\n }\n continue;\n }\n\n if (inString) {\n currentToken += char;\n continue;\n }\n\n // Detectar comentários\n if (char === \"/\" && nextChar === \"/\" && language === \"javascript\") {\n if (currentToken) {\n tokens.push({ type: \"text\", value: currentToken });\n currentToken = \"\";\n }\n // Pegar resto da linha\n const endOfLine = code.indexOf(\"\\n\", i);\n const comment = code.substring(\n i,\n endOfLine !== -1 ? endOfLine : code.length,\n );\n tokens.push({ type: \"comment\", value: comment });\n i = endOfLine !== -1 ? endOfLine - 1 : code.length;\n continue;\n }\n\n if (char === \"#\" && language === \"python\") {\n if (currentToken) {\n tokens.push({ type: \"text\", value: currentToken });\n currentToken = \"\";\n }\n const endOfLine = code.indexOf(\"\\n\", i);\n const comment = code.substring(\n i,\n endOfLine !== -1 ? endOfLine : code.length,\n );\n tokens.push({ type: \"comment\", value: comment });\n i = endOfLine !== -1 ? endOfLine - 1 : code.length;\n continue;\n }\n\n // Separadores\n if (/\\s|[(){}.,;]/.test(char) || char === \"[\" || char === \"]\") {\n if (currentToken) {\n tokens.push({ type: \"text\", value: currentToken });\n currentToken = \"\";\n }\n tokens.push({ type: \"separator\", value: char });\n continue;\n }\n\n // Operadores\n if (/[+\\-*/<>=!:]/.test(char)) {\n if (currentToken) {\n tokens.push({ type: \"text\", value: currentToken });\n currentToken = \"\";\n }\n tokens.push({ type: \"operator\", value: char });\n continue;\n }\n\n currentToken += char;\n }\n\n if (currentToken) {\n if (inString) {\n tokens.push({ type: \"string\", value: currentToken });\n } else {\n tokens.push({ type: \"text\", value: currentToken });\n }\n }\n\n // Renderizar tokens com cores\n return tokens.map((token, i) => {\n if (token.type === \"string\") {\n return (\n <span key={i} className=\"text-green-400\">\n {token.value}\n </span>\n );\n }\n if (token.type === \"comment\") {\n return (\n <span key={i} className=\"text-gray-500 italic\">\n {token.value}\n </span>\n );\n }\n if (token.type === \"operator\") {\n return (\n <span key={i} className=\"text-pink-400\">\n {token.value}\n </span>\n );\n }\n if (token.type === \"separator\") {\n return (\n <span key={i} className=\"text-gray-300\">\n {token.value}\n </span>\n );\n }\n if (token.type === \"text\") {\n // Verificar se é palavra-chave\n if (currentKeywords.includes(token.value)) {\n return (\n <span key={i} className=\"text-blue-400 font-semibold\">\n {token.value}\n </span>\n );\n }\n // Verificar se é tipo\n if (currentTypes.includes(token.value)) {\n return (\n <span key={i} className=\"text-cyan-400\">\n {token.value}\n </span>\n );\n }\n // Verificar se é número\n if (/^\\d+$/.test(token.value)) {\n return (\n <span key={i} className=\"text-orange-400\">\n {token.value}\n </span>\n );\n }\n // Verificar se é booleano ou null\n if (\n [\n \"True\",\n \"False\",\n \"None\",\n \"null\",\n \"undefined\",\n \"true\",\n \"false\",\n ].includes(token.value)\n ) {\n return (\n <span key={i} className=\"text-purple-400\">\n {token.value}\n </span>\n );\n }\n return (\n <span key={i} className=\"text-gray-300\">\n {token.value}\n </span>\n );\n }\n return <span key={i}>{token.value}</span>;\n });\n };\n\n // Código do exemplo de fatorial\n const codes = {\n javascript: `var numero, fatorial, i;\n\nnumero = 5;\nfatorial = 1;\ni = 1;\nfor (var count = 0; count < numero; count++) {\n fatorial = fatorial * i;\n i = (typeof i === 'number' ? i : 0) + 1;\n}\nwindow.alert([numero,'! = ',fatorial].join(''));`,\n\n python: `from numbers import Number\n\nnumero = None\nfatorial = None\ni = None\n\nnumero = 5\nfatorial = 1\ni = 1\nfor count in range(int(numero)):\n fatorial = fatorial * i\n i = (i if isinstance(i, Number) else 0) + 1\nprint(''.join([str(x) for x in [numero, '! = ', fatorial]]))`,\n\n console: `> Executando programa...\n> \n> Calculando 5!\n> 5! = 120\n> \n> Programa finalizado com sucesso.`,\n };\n\n // Efeito de digitação (typing effect) - apenas para abas de código\n useEffect(() => {\n if (activeTab === \"blocos\") {\n setIsTyping(false);\n setDisplayedCode(\"\");\n return;\n }\n\n setDisplayedCode(\"\");\n setIsTyping(true);\n\n const currentCode = codes[activeTab];\n let currentIndex = 0;\n\n const typeCharacter = () => {\n if (currentIndex < currentCode.length) {\n setDisplayedCode(currentCode.substring(0, currentIndex + 1));\n currentIndex++;\n\n // Auto-scroll para acompanhar o texto\n if (terminalRef.current) {\n terminalRef.current.scrollTop = terminalRef.current.scrollHeight;\n }\n\n // Velocidade variável para simular digitação humana\n const delay = Math.random() * 30 + 10; // 10-40ms entre caracteres\n typingTimeoutRef.current = setTimeout(typeCharacter, delay);\n } else {\n setIsTyping(false);\n }\n };\n\n // Pequeno delay antes de começar a digitar\n const startDelay = setTimeout(() => {\n typeCharacter();\n }, 300);\n\n // Cleanup\n return () => {\n clearTimeout(startDelay);\n if (typingTimeoutRef.current) {\n clearTimeout(typingTimeoutRef.current);\n }\n };\n }, [activeTab]);\n\n return (\n <section className=\"relative overflow-hidden\">\n {/* Keyframe animation for cursor blink */}\n <style>{`\n @keyframes blink-cursor {\n 0%, 49% { opacity: 1; }\n 50%, 100% { opacity: 0; }\n }\n .cursor-blink {\n animation: blink-cursor 1s infinite;\n }\n `}</style>\n\n {/* Background com gradiente igual ao Hero */}\n <div\n className=\"absolute inset-0 h-full w-full -z-10\"\n style={{\n background: \"linear-gradient(135deg, #dbeafe 0%, #faf5ff 100%)\",\n }}\n ></div>\n\n {/* Container principal */}\n <div className=\" w-full\">\n <div className=\"w-full overflow-hidden\">\n {/* Terminal de Código (Esquerda) */}\n <div className=\"flex flex-col justify-center text-sm min-w-0\">\n <div className=\"overflow-hidden\">\n <div className=\"relative flex-1 bg-white/50 bg-gray-50/50 backdrop-blur-lg shadow-2xl rounded-xl border border-gray-200 border-gray-200\">\n {/* Header do Terminal */}\n <div className=\"flex items-center justify-between p-3 border-b border-gray-200 border-gray-200\">\n <div className=\"flex items-center gap-2 pl-2\">\n <div className=\"w-3 h-3 bg-red-500 rounded-full\"></div>\n <div className=\"w-3 h-3 bg-yellow-500 rounded-full\"></div>\n <div className=\"w-3 h-3 bg-green-500 rounded-full\"></div>\n </div>\n\n {/* Tabs */}\n <div className=\"flex flex-wrap gap-2\">\n <button\n onClick={() => setActiveTab(\"blocos\")}\n className={`px-4 py-1 rounded-md transition-colors font-medium text-sm ${\n activeTab === \"blocos\"\n ? \"bg-blue-600 text-white\"\n : \"text-gray-600 text-gray-600 hover:bg-blue-600/20 hover:text-blue-600 dark:hover:text-blue-400\"\n }`}\n >\n Blocos\n </button>\n <button\n onClick={() => setActiveTab(\"javascript\")}\n className={`px-4 py-1 rounded-md transition-colors font-medium text-sm ${\n activeTab === \"javascript\"\n ? \"bg-blue-600 text-white\"\n : \"text-gray-600 text-gray-600 hover:bg-blue-600/20 hover:text-blue-600 dark:hover:text-blue-400\"\n }`}\n >\n JavaScript\n </button>\n <button\n onClick={() => setActiveTab(\"python\")}\n className={`px-4 py-1 rounded-md transition-colors font-medium text-sm ${\n activeTab === \"python\"\n ? \"bg-blue-600 text-white\"\n : \"text-gray-600 text-gray-600 hover:bg-blue-600/20 hover:text-blue-600 dark:hover:text-blue-400\"\n }`}\n >\n Python\n </button>\n <button\n onClick={() => setActiveTab(\"console\")}\n className={`px-4 py-1 rounded-md transition-colors font-medium text-sm ${\n activeTab === \"console\"\n ? \"bg-blue-600 text-white\"\n : \"text-gray-600 text-gray-600 hover:bg-blue-600/20 hover:text-blue-600 dark:hover:text-blue-400\"\n }`}\n >\n Console\n </button>\n </div>\n </div>\n\n {/* Conteúdo do Terminal */}\n <div\n ref={terminalRef}\n className={`relative p-6 h-96 overflow-auto scroll-smooth ${\n activeTab === \"blocos\"\n ? \"bg-gradient-to-br from-blue-50 to-purple-50\"\n : \"bg-gray-50 dark:bg-gray-900\"\n }`}\n >\n {activeTab === \"blocos\" ? (\n <div className=\"flex flex-col items-center justify-center h-full\">\n <img\n src={fatorialSvg}\n alt=\"Exemplo de blocos visuais - Cálculo de fatorial\"\n className=\"w-full max-w-md h-auto object-contain\"\n />\n </div>\n ) : (\n <pre className=\"text-sm font-mono leading-relaxed whitespace-pre-wrap\">\n <code>\n {highlightCode(displayedCode, activeTab)}\n {isTyping && (\n <span className=\"inline-block w-2 h-4 ml-0.5 bg-blue-400 cursor-blink\"></span>\n )}\n </code>\n </pre>\n )}\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </section>\n );\n};\n\nexport default CtaPlayground;\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/HomePage/CtaVideoPreview.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'React' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":13,"suggestions":[{"messageId":"removeVar","data":{"varName":"React"},"fix":{"range":[7,18],"text":""},"desc":"Remove unused variable 'React'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import React from \"react\";\nimport atividadesVideo from \"./assets/atividades.mp4\";\n\nconst CtaVideoPreview = () => {\n return (\n <section className=\"relative overflow-hidden\">\n {/* Background com gradiente igual ao CtaPlayground */}\n <div\n className=\"absolute inset-0 h-full w-full -z-10\"\n style={{\n background: \"linear-gradient(135deg, #dbeafe 0%, #faf5ff 100%)\",\n }}\n ></div>\n\n {/* Container principal */}\n <div className=\"w-full\">\n <div className=\"w-full overflow-hidden\">\n {/* Terminal de Vídeo */}\n <div className=\"flex flex-col justify-center text-sm min-w-0\">\n <div className=\"overflow-hidden\">\n <div className=\"relative flex-1 bg-white/50 bg-gray-50/50 backdrop-blur-lg shadow-2xl rounded-xl border border-gray-200\">\n {/* Header do Terminal */}\n <div className=\"flex items-center justify-between p-3 border-b border-gray-200\">\n <div className=\"flex items-center gap-2 pl-2\">\n <div className=\"w-3 h-3 bg-red-500 rounded-full\"></div>\n <div className=\"w-3 h-3 bg-yellow-500 rounded-full\"></div>\n <div className=\"w-3 h-3 bg-green-500 rounded-full\"></div>\n </div>\n </div>\n\n {/* Conteúdo do Vídeo */}\n <div className=\"relative bg-gray-50 dark:bg-gray-900\">\n <video\n autoPlay\n loop\n muted\n playsInline\n className=\"w-full h-96 object-cover\"\n >\n <source src={atividadesVideo} type=\"video/mp4\" />\n Seu navegador não suporta a reprodução de vídeos.\n </video>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </section>\n );\n};\n\nexport default CtaVideoPreview;\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/HomePage/Features.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'Check' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":15,"suggestions":[{"messageId":"removeVar","data":{"varName":"Check"},"fix":{"range":[9,15],"text":""},"desc":"Remove unused variable 'Check'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Sparkles' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":17,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":25,"suggestions":[{"messageId":"removeVar","data":{"varName":"Sparkles"},"fix":{"range":[14,24],"text":""},"desc":"Remove unused variable 'Sparkles'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'ArrowRight' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":27,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":37,"suggestions":[{"messageId":"removeVar","data":{"varName":"ArrowRight"},"fix":{"range":[24,36],"text":""},"desc":"Remove unused variable 'ArrowRight'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":3,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { Check, Sparkles, ArrowRight } from \"lucide-react\";\n\nexport default function Features() {\n return (\n <section\n id=\"features-section\"\n className=\"relative min-h-screen overflow-hidden flex items-center\"\n >\n {/* Background com gradiente escuro */}\n <div\n className=\"absolute inset-0 h-full w-full -z-10\"\n style={{\n background: \"linear-gradient(135deg, #1f2937 0%, #111827 100%)\",\n }}\n ></div>\n\n {/* Container principal */}\n <div className=\"mx-auto max-w-7xl px-6 py-12 lg:py-16 lg:px-8 w-full\">\n <div className=\"container mx-auto\">\n {/* Header da seção */}\n <div className=\"flex max-w-3xl flex-col gap-4 mb-6\">\n <h2 className=\"mb-1 font-bold text-white tracking-tight text-3xl sm:text-4xl lg:text-5xl\">\n Tudo que você precisa para ensinar e aprender programação\n </h2>\n <p className=\"font-normal text-gray-300 text-base lg:text-lg\">\n Nossa plataforma oferece uma abordagem completa para educadores e\n alunos, combinando programação visual (usando blocos) e jogos\n educativos contextualizados.\n </p>\n </div>\n\n {/* Container das features */}\n <div className=\"flex flex-col items-center justify-center\">\n {/* SEÇÃO 1: Para Alunos */}\n <div className=\"w-full max-w-96 lg:max-w-none\">\n <div role=\"none\" className=\"my-6 border-t border-gray-700\"></div>\n <div className=\"mx-auto w-full gap-x-10 lg:grid lg:grid-cols-4\">\n <h3 className=\"mb-3 font-semibold text-white tracking-tight text-xl lg:text-2xl\">\n Para Alunos\n </h3>\n\n <ul className=\"col-span-3 grid gap-x-8 gap-y-4 lg:grid-cols-3\">\n <li className=\"flex gap-2 font-normal text-gray-300 text-base\">\n <Check\n className=\"text-green-400 mr-2 w-5 h-5 flex-shrink-0 mt-0.5\"\n aria-hidden=\"true\"\n />\n Programação visual com blocos intuitivos\n </li>\n <li className=\"flex gap-2 font-normal text-gray-300 text-base\">\n <Check\n className=\"text-green-400 mr-2 w-5 h-5 flex-shrink-0 mt-0.5\"\n aria-hidden=\"true\"\n />\n Jogos educativos contextualizados\n </li>\n <li className=\"flex gap-2 font-normal text-gray-300 text-base\">\n <Check\n className=\"text-green-400 mr-2 w-5 h-5 flex-shrink-0 mt-0.5\"\n aria-hidden=\"true\"\n />\n Feedback imediato e visual\n </li>\n <li className=\"flex gap-2 font-normal text-gray-300 text-base\">\n <Check\n className=\"text-green-400 mr-2 w-5 h-5 flex-shrink-0 mt-0.5\"\n aria-hidden=\"true\"\n />\n Sem erros de sintaxe ou barreiras técnicas\n </li>\n <li className=\"flex gap-2 font-normal text-gray-300 text-base\">\n <Check\n className=\"text-green-400 mr-2 w-5 h-5 flex-shrink-0 mt-0.5\"\n aria-hidden=\"true\"\n />\n Aprendizado por tentativa e experimentação\n </li>\n <li className=\"flex gap-2 font-normal text-gray-300 text-base\">\n <Check\n className=\"text-green-400 mr-2 w-5 h-5 flex-shrink-0 mt-0.5\"\n aria-hidden=\"true\"\n />\n Desenvolvimento da criatividade e autonomia\n </li>\n <li className=\"flex gap-2 font-normal text-gray-300 text-base\">\n <Check\n className=\"text-green-400 mr-2 w-5 h-5 flex-shrink-0 mt-0.5\"\n aria-hidden=\"true\"\n />\n Interface amigável e responsiva\n </li>\n <li className=\"flex gap-2 font-normal text-gray-300 text-base\">\n <Check\n className=\"text-green-400 mr-2 w-5 h-5 flex-shrink-0 mt-0.5\"\n aria-hidden=\"true\"\n />\n Progressão natural de dificuldade\n </li>\n <li className=\"flex gap-2 font-normal text-gray-300 text-base\">\n <Check\n className=\"text-green-400 mr-2 w-5 h-5 flex-shrink-0 mt-0.5\"\n aria-hidden=\"true\"\n />\n Facilidade de uso por meio de Blocos\n </li>\n </ul>\n </div>\n </div>\n\n {/* SEÇÃO 2: Para Educadores */}\n <div className=\"w-full max-w-96 lg:max-w-none\">\n <div role=\"none\" className=\"my-4 border-t border-gray-700\"></div>\n <div className=\"mx-auto w-full gap-x-10 lg:grid lg:grid-cols-4\">\n <h3 className=\"mb-3 font-semibold text-white tracking-tight text-xl lg:text-2xl\">\n Para Educadores\n </h3>\n\n <ul className=\"col-span-3 grid gap-x-8 gap-y-4 lg:grid-cols-3\">\n <li className=\"flex gap-2 font-normal text-gray-300 text-base\">\n <Check\n className=\"text-green-400 mr-2 w-5 h-5 flex-shrink-0 mt-0.5\"\n aria-hidden=\"true\"\n />\n Guias pedagógicos completos e práticos\n </li>\n <li className=\"flex gap-2 font-normal text-gray-300 text-base\">\n <Check\n className=\"text-green-400 mr-2 w-5 h-5 flex-shrink-0 mt-0.5\"\n aria-hidden=\"true\"\n />\n Estratégias de ensino para sala de aula\n </li>\n <li className=\"flex gap-2 font-normal text-gray-300 text-base\">\n <Check\n className=\"text-green-400 mr-2 w-5 h-5 flex-shrink-0 mt-0.5\"\n aria-hidden=\"true\"\n />\n Atividades de computação desplugada\n </li>\n <li className=\"flex gap-2 font-normal text-gray-300 text-base\">\n <Check\n className=\"text-green-400 mr-2 w-5 h-5 flex-shrink-0 mt-0.5\"\n aria-hidden=\"true\"\n />\n Planos de aula e preparação facilitada\n </li>\n <li className=\"flex gap-2 font-normal text-gray-300 text-base\">\n <Check\n className=\"text-green-400 mr-2 w-5 h-5 flex-shrink-0 mt-0.5\"\n aria-hidden=\"true\"\n />\n Ferramentas de avaliação de aprendizado\n </li>\n <li className=\"flex gap-2 font-normal text-gray-300 text-base\">\n <Check\n className=\"text-green-400 mr-2 w-5 h-5 flex-shrink-0 mt-0.5\"\n aria-hidden=\"true\"\n />\n Gestão e organização da sala de aula\n </li>\n <li className=\"flex gap-2 font-normal text-gray-300 text-base\">\n <Check\n className=\"text-green-400 mr-2 w-5 h-5 flex-shrink-0 mt-0.5\"\n aria-hidden=\"true\"\n />\n Resolução de problemas e desafios comuns\n </li>\n <li className=\"flex gap-2 font-normal text-gray-300 text-base\">\n <Check\n className=\"text-green-400 mr-2 w-5 h-5 flex-shrink-0 mt-0.5\"\n aria-hidden=\"true\"\n />\n Desenvolvimento do pensamento crítico\n </li>\n <li className=\"flex gap-2 font-normal text-gray-300 text-base\">\n <Check\n className=\"text-green-400 mr-2 w-5 h-5 flex-shrink-0 mt-0.5\"\n aria-hidden=\"true\"\n />\n Aprendizagem colaborativa e inclusiva\n </li>\n </ul>\n </div>\n </div>\n </div>\n\n {/* Divider */}\n <div role=\"none\" className=\"my-6 border-t border-gray-700\"></div>\n\n {/* FAQ Call to Action */}\n <div className=\"flex flex-col items-center justify-center text-center gap-4\">\n <p className=\"font-normal text-gray-300 text-lg lg:text-xl\">\n Ficou com alguma dúvida?\n </p>\n <a\n href=\"/faq\"\n className=\"inline-flex items-center justify-center gap-x-3 px-6 py-3 text-base md:text-lg font-bold text-white bg-gradient-to-r from-red-600 via-pink-600 to-purple-600 hover:from-red-700 hover:via-pink-700 hover:to-purple-700 rounded-full transition-all duration-300 shadow-lg hover:shadow-xl transform hover:scale-105\"\n >\n Consulte nossa FAQ\n <ArrowRight className=\"w-5 h-5\" />\n </a>\n </div>\n </div>\n </div>\n </section>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/HomePage/Footer.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'Link' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":14,"suggestions":[{"messageId":"removeVar","data":{"varName":"Link"},"fix":{"range":[0,40],"text":""},"desc":"Remove unused variable 'Link'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Github' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":16,"suggestions":[{"messageId":"removeVar","data":{"varName":"Github"},"fix":{"range":[50,57],"text":""},"desc":"Remove unused variable 'Github'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'BookOpen' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":18,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":26,"suggestions":[{"messageId":"removeVar","data":{"varName":"BookOpen"},"fix":{"range":[56,66],"text":""},"desc":"Remove unused variable 'BookOpen'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Mail' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":28,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":32,"suggestions":[{"messageId":"removeVar","data":{"varName":"Mail"},"fix":{"range":[66,72],"text":""},"desc":"Remove unused variable 'Mail'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Heart' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":34,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":39,"suggestions":[{"messageId":"removeVar","data":{"varName":"Heart"},"fix":{"range":[72,79],"text":""},"desc":"Remove unused variable 'Heart'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":5,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { Link } from \"react-router-dom\";\nimport { Github, BookOpen, Mail, Heart } from \"lucide-react\";\nimport logo from \"../../assets/logo_decoda.svg\";\n\nconst Footer = () => {\n const currentYear = new Date().getFullYear();\n\n return (\n <footer className=\"relative bg-white border-t border-gray-200\">\n {/* Conteúdo do Footer */}\n <div className=\"mx-auto max-w-7xl px-6 py-8 lg:py-12 lg:px-8\">\n {/* Header do Footer - Logo e Links */}\n <div className=\"sm:flex sm:items-center sm:justify-between\">\n {/* Logo e Nome */}\n <Link to=\"/\" className=\"flex items-center mb-4 sm:mb-0 space-x-3\">\n <div className=\"flex items-center gap-x-3\">\n {/* Logo Blockly-NT */}\n <img\n src={logo}\n alt=\"Decoda Logo\"\n className=\"h-[83px] transition-transform duration-200 group-hover:scale-110\"\n />\n </div>\n </Link>\n\n {/* Links de Navegação */}\n <ul className=\"flex flex-wrap items-center mb-6 sm:mb-0 gap-4 md:gap-6\">\n <li>\n <Link\n to=\"/sobre\"\n className=\"text-gray-600 hover:text-blue-600 dark:hover:text-brand-500 transition-colors\"\n >\n Sobre Nós\n </Link>\n </li>\n <li>\n <Link\n to=\"/docs\"\n className=\"text-gray-600 hover:text-blue-600 dark:hover:text-brand-500 transition-colors\"\n >\n Documentação\n </Link>\n </li>\n <li>\n <Link\n to=\"/faq\"\n className=\"text-gray-600 hover:text-blue-600 dark:hover:text-brand-500 transition-colors\"\n >\n FAQ\n </Link>\n </li>\n </ul>\n </div>\n\n {/* Divider */}\n <hr className=\"my-8 border-gray-300 dark:border-gray-700\" />\n\n {/* Copyright e Créditos */}\n <div className=\"flex flex-col sm:flex-row items-center justify-between gap-4\">\n <div className=\"flex items-center gap-2 text-sm text-gray-600\">\n <span>© {currentYear}</span>\n <Link\n to=\"/\"\n className=\"hover:text-blue-600 dark:hover:text-brand-500 transition-colors\"\n >\n Decoda\n </Link>\n <span>•</span>\n <span>Todos os direitos reservados</span>\n </div>\n\n <div className=\"flex items-center gap-2 text-sm text-gray-600\">\n <span>Feito com</span>\n <Heart className=\"w-4 h-4 text-red-500 fill-red-500\" />\n <span>para educação</span>\n </div>\n </div>\n\n {/* Licença */}\n <div className=\"mt-4 text-center\">\n <p className=\"text-xs text-gray-500 dark:text-gray-500\">\n Licenciado sob{\" \"}\n <a\n href=\"https://opensource.org/licenses/MIT\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"text-blue-600 dark:text-brand-500 hover:underline\"\n >\n MIT License\n </a>\n </p>\n </div>\n </div>\n </footer>\n );\n};\n\nexport default Footer;\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/HomePage/ForWhom.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'SectionTitle' is defined but never used. Allowed unused vars must match /^_/u.","line":4,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":4,"endColumn":20,"suggestions":[{"messageId":"removeVar","data":{"varName":"SectionTitle"},"fix":{"range":[183,201],"text":""},"desc":"Remove unused variable 'SectionTitle'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import FirstPersonImage from \"./assets/first-person.png\";\nimport SecondPersonImage from \"./assets/second-person.png\";\nimport ThirdPersonImage from \"./assets/third-person.png\";\nimport SectionTitle from \"./SectionTitle\";\n\nconst ForWhom = () => {\n return (\n <div className=\" bg-brand-300 py-10 lg:py-[72px] px-10 lg:px-[178px] flex flex-col items-center justify-center\">\n <SectionTitle title=\"Para quem?\" squaresColor=\"brand-400\" />\n <div className=\"flex flex-col lg:flex-row mt-8\">\n <div className=\"w-full lg:w-1/3 px-4\">\n <img\n src={FirstPersonImage}\n alt=\"Imagem de um jovem negro com mochila, segurando papéis e olhando para o lado\"\n />\n <h4 className=\"text-[24px] font-bold text-center\">\n Estudantes iniciantes\n </h4>\n <p className=\"text-[18px] text-center\">\n Aprender a lógica da programação da base com programação com códigos\n </p>\n </div>\n <div className=\"w-full lg:w-1/3 px-4\">\n <img\n src={SecondPersonImage}\n alt=\"Imagem de uma jovem branca com mochila, segurando um fichário, olhando para frente e sorrindo\"\n />\n <h4 className=\"text-[24px] font-bold text-center\">\n Estudantes avançados\n </h4>\n <p className=\"text-[18px] text-center\">\n Conseguem prototipar seus primeiros apps já com código.\n </p>\n </div>\n <div className=\"w-full lg:w-1/3 px-4\">\n <img\n src={ThirdPersonImage}\n alt=\"Imagem de um jovem negro conversando com uma jovem branca apontando para um celular em suas mãos\"\n />\n <h4 className=\"text-[24px] font-bold text-center\">Educadores</h4>\n <p className=\"text-[18px] text-center\">\n Materiais para desenvolver suas aulas de programação\n </p>\n </div>\n </div>\n </div>\n );\n};\n\nexport default ForWhom;\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/HomePage/Hero.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'Link' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":14,"suggestions":[{"messageId":"removeVar","data":{"varName":"Link"},"fix":{"range":[0,40],"text":""},"desc":"Remove unused variable 'Link'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'ArrowRight' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":20,"suggestions":[{"messageId":"removeVar","data":{"varName":"ArrowRight"},"fix":{"range":[50,61],"text":""},"desc":"Remove unused variable 'ArrowRight'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Gamepad2' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":22,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":30,"suggestions":[{"messageId":"removeVar","data":{"varName":"Gamepad2"},"fix":{"range":[60,70],"text":""},"desc":"Remove unused variable 'Gamepad2'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Trophy' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":32,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":38,"suggestions":[{"messageId":"removeVar","data":{"varName":"Trophy"},"fix":{"range":[70,78],"text":""},"desc":"Remove unused variable 'Trophy'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'scrollToFeatures' is assigned a value but never used. Allowed unused vars must match /^_/u.","line":6,"column":9,"nodeType":"Identifier","messageId":"unusedVar","endLine":6,"endColumn":25,"suggestions":[{"messageId":"removeVar","data":{"varName":"scrollToFeatures"},"fix":{"range":[178,408],"text":""},"desc":"Remove unused variable 'scrollToFeatures'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":5,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { Link } from \"react-router-dom\";\nimport { ArrowRight, Gamepad2, Trophy } from \"lucide-react\";\nimport heroImage from \"./assets/puzzled-image.png\";\n\nconst Hero = () => {\n const scrollToFeatures = (e) => {\n e.preventDefault();\n const featuresSection = document.getElementById(\"features-section\");\n if (featuresSection) {\n featuresSection.scrollIntoView({ behavior: \"smooth\" });\n }\n };\n\n return (\n <section className=\"relative overflow-hidden mt-32 lg:mt-[195px] py-20\">\n {/* Background Gradient */}\n <div\n className=\"isolate -z-10 absolute h-full w-full top-0 left-0\"\n style={{\n background: \"linear-gradient(135deg, #F90527 0%, #F50C52 100%)\",\n }}\n ></div>{\" \"}\n {/* Container Principal */}\n <div className=\"w-full px-6 lg:px-[120px] lg:py-[106px] h-full flex flex-col lg:items-center lg:flex-row justify-between gap-8\">\n {/* Coluna Esquerda - Texto */}\n <div className=\"w-full lg:w-[40%] flex flex-col justify-center \">\n {/* Título Principal */}\n <h1 className=\"font-bold text-white tracking-tight text-[60px] md:text-[76px] leading-tight font-title w-[90%]\">\n Programe com desafios{\" \"}\n </h1>\n <h1 className=\"font-bold tracking-tight md:text-[76px] text-[60px] mb-6 leading-tight font-title text-black\">\n em contexto\n </h1>\n\n {/* Subtítulo */}\n <p className=\"font-normal text-white text-lg\">\n Descubra o mundo da programação através de jogos educativos e\n interativos. <br />\n Desenvolva habilidades essenciais enquanto se diverte!\n </p>\n\n {/* Botões de Ação */}\n <div className=\"mt-8 hover:scale-110\">\n <Link\n to=\"/atividades\"\n className=\"bg-green-100 py-4 px-8 rounded-[32px] shadow-md w-full flex md:inline items-center \"\n >\n <span className=\"text-xl font-bold w-full text-center\">\n Comece as atividades\n </span>\n </Link>\n </div>\n </div>\n\n {/* Coluna Direita - Imagem Hero (oculta no mobile) */}\n <div className=\"hidden lg:flex flex-1 items-center justify-center\">\n <div className=\"w-full max-w-2xl\">\n <img\n src={heroImage}\n alt=\"Decoda - Programação Visual\"\n className=\"w-full h-auto object-contain\"\n />\n </div>\n </div>\n </div>\n </section>\n );\n};\n\nexport default Hero;\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/HomePage/HomePage.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'Navbar' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":14,"suggestions":[{"messageId":"removeVar","data":{"varName":"Navbar"},"fix":{"range":[7,19],"text":""},"desc":"Remove unused variable 'Navbar'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Hero' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":12,"suggestions":[{"messageId":"removeVar","data":{"varName":"Hero"},"fix":{"range":[53,63],"text":""},"desc":"Remove unused variable 'Hero'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Features' is defined but never used. Allowed unused vars must match /^_/u.","line":3,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":3,"endColumn":16,"suggestions":[{"messageId":"removeVar","data":{"varName":"Features"},"fix":{"range":[80,94],"text":""},"desc":"Remove unused variable 'Features'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'CtaPlayground' is defined but never used. Allowed unused vars must match /^_/u.","line":4,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":4,"endColumn":21,"suggestions":[{"messageId":"removeVar","data":{"varName":"CtaPlayground"},"fix":{"range":[115,134],"text":""},"desc":"Remove unused variable 'CtaPlayground'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Footer' is defined but never used. Allowed unused vars must match /^_/u.","line":5,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":5,"endColumn":14,"suggestions":[{"messageId":"removeVar","data":{"varName":"Footer"},"fix":{"range":[160,172],"text":""},"desc":"Remove unused variable 'Footer'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'About' is defined but never used. Allowed unused vars must match /^_/u.","line":6,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":6,"endColumn":13,"suggestions":[{"messageId":"removeVar","data":{"varName":"About"},"fix":{"range":[191,202],"text":""},"desc":"Remove unused variable 'About'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'ForWhom' is defined but never used. Allowed unused vars must match /^_/u.","line":7,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":7,"endColumn":15,"suggestions":[{"messageId":"removeVar","data":{"varName":"ForWhom"},"fix":{"range":[220,233],"text":""},"desc":"Remove unused variable 'ForWhom'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'StudentsMaterials' is defined but never used. Allowed unused vars must match /^_/u.","line":8,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":8,"endColumn":25,"suggestions":[{"messageId":"removeVar","data":{"varName":"StudentsMaterials"},"fix":{"range":[253,276],"text":""},"desc":"Remove unused variable 'StudentsMaterials'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'TeachersMaterials' is defined but never used. Allowed unused vars must match /^_/u.","line":9,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":9,"endColumn":25,"suggestions":[{"messageId":"removeVar","data":{"varName":"TeachersMaterials"},"fix":{"range":[306,329],"text":""},"desc":"Remove unused variable 'TeachersMaterials'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":9,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import Navbar from \"../../components/Navbar\";\nimport Hero from \"./Hero\";\nimport Features from \"./Features\";\nimport CtaPlayground from \"./CtaPlayground\";\nimport Footer from \"./Footer\";\nimport About from \"./About\";\nimport ForWhom from \"./ForWhom\";\nimport StudentsMaterials from \"./StudentsMaterials\";\nimport TeachersMaterials from \"./TeachersMaterials\";\n\nconst HomePage = () => {\n return (\n <div className=\"min-h-screen\">\n {/* Navegação */}\n <Navbar />\n\n {/* Hero Section */}\n <Hero />\n\n {/* Quem Somos */}\n <About />\n\n {/* Para quem */}\n <ForWhom />\n\n {/* Materiais para estudantes */}\n <StudentsMaterials />\n\n {/* Materiais para educadores */}\n <TeachersMaterials />\n\n <Footer />\n </div>\n );\n};\n\nexport default HomePage;\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/HomePage/SectionTitle.jsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/HomePage/StudentsMaterials.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'ArrowRight' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":20,"suggestions":[{"messageId":"removeVar","data":{"varName":"ArrowRight"},"fix":{"range":[0,42],"text":""},"desc":"Remove unused variable 'ArrowRight'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Link' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":14,"suggestions":[{"messageId":"removeVar","data":{"varName":"Link"},"fix":{"range":[43,83],"text":""},"desc":"Remove unused variable 'Link'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'CtaPlayground' is defined but never used. Allowed unused vars must match /^_/u.","line":3,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":3,"endColumn":21,"suggestions":[{"messageId":"removeVar","data":{"varName":"CtaPlayground"},"fix":{"range":[91,110],"text":""},"desc":"Remove unused variable 'CtaPlayground'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'CtaVideoPreview' is defined but never used. Allowed unused vars must match /^_/u.","line":4,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":4,"endColumn":23,"suggestions":[{"messageId":"removeVar","data":{"varName":"CtaVideoPreview"},"fix":{"range":[136,157],"text":""},"desc":"Remove unused variable 'CtaVideoPreview'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'SectionTitle' is defined but never used. Allowed unused vars must match /^_/u.","line":5,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":5,"endColumn":20,"suggestions":[{"messageId":"removeVar","data":{"varName":"SectionTitle"},"fix":{"range":[185,203],"text":""},"desc":"Remove unused variable 'SectionTitle'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":5,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ArrowRight } from \"lucide-react\";\nimport { Link } from \"react-router-dom\";\nimport CtaPlayground from \"./CtaPlayground\";\nimport CtaVideoPreview from \"./CtaVideoPreview\";\nimport SectionTitle from \"./SectionTitle\";\n\nconst StudentsMaterials = () => {\n return (\n <div className=\"bg-gray-100 px-10 lg:px-[200px] py-10 py-[72px]\">\n <SectionTitle title=\"Nossos materiais\" squaresColor=\"brand-300\" />\n <h3 className=\"text-[32px] mt-1 ml-6 font-title\">PARA ESTUDANTES</h3>\n\n <div className=\"flex flex-col lg:flex-row gap-8 mt-[86px]\">\n <div className=\"w-full lg:w-[40%] \">\n <h4 className=\"text-[28px] font-bold\">\n Está iniciando em programação e quer entender melhor?\n </h4>\n <p className=\"mt-3\">\n Acesse nossas atividades e dê os primeiros passos no mundo da\n programação! Começe a programar de forma divertida e interativa com\n nossos materiais feitos especialmente para iniciantes.\n </p>\n <div className=\"mt-10\">\n <Link to=\"/atividades\">\n <button className=\"bg-brand-500 py-4 px-8 flex flex-row justify-between lg:gap-1 items-center text-white text-5 lg:text-[24px] font-bold rounded-[32px] hover:scale-110\">\n Acesse nossas Atividades\n <ArrowRight />\n </button>\n </Link>\n </div>\n </div>\n <div className=\"hidden lg:block lg:w-[60%]\">\n <CtaVideoPreview />\n </div>\n </div>\n <div className=\"flex flex-col lg:flex-row gap-8 mt-10 lg:mt-[158px]\">\n <div className=\"hidden lg:block lg:w-[60%]\">\n <CtaPlayground />\n </div>\n <div className=\"w-full lg:w-[40%] \">\n <h4 className=\"text-[28px] font-bold\">\n Já tem a base de programação e busca algo mais avançado?\n </h4>\n <p className=\"mt-3\">\n Acesse nosso Laboratório e comece a criar seus próprios programas\n usando blocos visuais. Converta para JavaScript ou Python e aprenda\n programação de verdade enquanto se diverte.\n </p>\n <div className=\"mt-10\">\n <Link to=\"/atividades\">\n <button className=\"bg-brand-500 py-4 px-8 flex flex-row justify-between lg:gap-1 items-center text-white text-5 lg:text-[24px] font-bold rounded-[32px] hover:scale-110\">\n Acesse nosso Laboratório\n <ArrowRight />\n </button>\n </Link>\n </div>\n </div>\n </div>\n </div>\n );\n};\n\nexport default StudentsMaterials;\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/HomePage/TeachersMaterials.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'ArrowRight' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":20,"suggestions":[{"messageId":"removeVar","data":{"varName":"ArrowRight"},"fix":{"range":[0,42],"text":""},"desc":"Remove unused variable 'ArrowRight'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Link' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":14,"suggestions":[{"messageId":"removeVar","data":{"varName":"Link"},"fix":{"range":[43,83],"text":""},"desc":"Remove unused variable 'Link'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'CtaPlayground' is defined but never used. Allowed unused vars must match /^_/u.","line":3,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":3,"endColumn":21,"suggestions":[{"messageId":"removeVar","data":{"varName":"CtaPlayground"},"fix":{"range":[91,110],"text":""},"desc":"Remove unused variable 'CtaPlayground'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'SectionTitle' is defined but never used. Allowed unused vars must match /^_/u.","line":4,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":4,"endColumn":20,"suggestions":[{"messageId":"removeVar","data":{"varName":"SectionTitle"},"fix":{"range":[136,154],"text":""},"desc":"Remove unused variable 'SectionTitle'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":4,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { ArrowRight } from \"lucide-react\";\nimport { Link } from \"react-router-dom\";\nimport CtaPlayground from \"./CtaPlayground\";\nimport SectionTitle from \"./SectionTitle\";\n\nconst TeachersMaterials = () => {\n return (\n <div className=\"bg-gray-900 px-10 lg:px-[120px] py-10 lg:py-[72px]\">\n <SectionTitle\n title=\"Nossos materiais\"\n squaresColor=\"green-500\"\n titleColor=\"white\"\n textShadowColor=\"black\"\n />\n <h3 className=\"text-[32px] mt-1 ml-6 text-white font-title\">\n PARA EDUCADORES\n </h3>\n\n <div className=\"flex flex-col lg:flex-row mt-[86px]\">\n <div className=\"w-full lg:w-[40%]\">\n <h4 className=\"text-white font-bold text-[28px]\">\n Quem entender como você pode usar nossa plataforma para ensinar?\n </h4>\n <p className=\"text-white text-[24px]\">\n Acesse nosso material de documentação e comece a usar o Decoda para\n potencializar suas aulas e engajar seus alunos com atividades\n interativas.\n </p>\n <div className=\"mt-10\">\n <Link to=\"/educadores\">\n <button className=\"bg-brand-500 py-4 px-8 flex flex-row justify-between lg:gap-1 items-center text-white text-5 lg:text-[24px] font-bold rounded-[32px] hover:scale-110\">\n Acesse o material para Educadores\n <ArrowRight />\n </button>\n </Link>\n </div>\n </div>\n </div>\n </div>\n );\n};\n\nexport default TeachersMaterials;\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/LabPython/LabPython.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'GameNavBar' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":18,"suggestions":[{"messageId":"removeVar","data":{"varName":"GameNavBar"},"fix":{"range":[7,23],"text":""},"desc":"Remove unused variable 'GameNavBar'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import GameNavBar from \"../../components/game/GameNavBar\";\n\nexport default function LabPython() {\n return (\n <div\n className={`w-screen h-screen flex flex-col overflow-hidden transition-colors duration-300`}\n >\n <GameNavBar title=\"Laboratório Python\" type=\"code\" />\n\n <div className=\"flex-1 overflow-hidden\">\n <iframe\n src=\"/jupyter/\"\n title=\"JupyterLite Lab\"\n className=\"w-full h-full border-0\"\n allowFullScreen\n sandbox=\"allow-same-origin allow-scripts allow-popups allow-forms allow-modals\"\n />\n </div>\n </div>\n );\n}\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/Playground/Playground.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'React' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":13,"suggestions":[{"messageId":"removeVar","data":{"varName":"React"},"fix":{"range":[7,13],"text":""},"desc":"Remove unused variable 'React'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'PanelGroup' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":20,"suggestions":[{"messageId":"removeVar","data":{"varName":"PanelGroup"},"fix":{"range":[63,74],"text":""},"desc":"Remove unused variable 'PanelGroup'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Panel' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":22,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":27,"suggestions":[{"messageId":"removeVar","data":{"varName":"Panel"},"fix":{"range":[73,80],"text":""},"desc":"Remove unused variable 'Panel'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Moon' is defined but never used. Allowed unused vars must match /^_/u.","line":3,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":3,"endColumn":14,"suggestions":[{"messageId":"removeVar","data":{"varName":"Moon"},"fix":{"range":[123,128],"text":""},"desc":"Remove unused variable 'Moon'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Sun' is defined but never used. Allowed unused vars must match /^_/u.","line":3,"column":16,"nodeType":"Identifier","messageId":"unusedVar","endLine":3,"endColumn":19,"suggestions":[{"messageId":"removeVar","data":{"varName":"Sun"},"fix":{"range":[127,132],"text":""},"desc":"Remove unused variable 'Sun'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'HelpCircle' is defined but never used. Allowed unused vars must match /^_/u.","line":3,"column":21,"nodeType":"Identifier","messageId":"unusedVar","endLine":3,"endColumn":31,"suggestions":[{"messageId":"removeVar","data":{"varName":"HelpCircle"},"fix":{"range":[132,144],"text":""},"desc":"Remove unused variable 'HelpCircle'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'GameNavBar' is defined but never used. Allowed unused vars must match /^_/u.","line":6,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":6,"endColumn":18,"suggestions":[{"messageId":"removeVar","data":{"varName":"GameNavBar"},"fix":{"range":[241,257],"text":""},"desc":"Remove unused variable 'GameNavBar'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'ResizeHandle' is defined but never used. Allowed unused vars must match /^_/u.","line":7,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":7,"endColumn":20,"suggestions":[{"messageId":"removeVar","data":{"varName":"ResizeHandle"},"fix":{"range":[300,318],"text":""},"desc":"Remove unused variable 'ResizeHandle'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'PlaygroundEditor' is defined but never used. Allowed unused vars must match /^_/u.","line":8,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":8,"endColumn":24,"suggestions":[{"messageId":"removeVar","data":{"varName":"PlaygroundEditor"},"fix":{"range":[363,385],"text":""},"desc":"Remove unused variable 'PlaygroundEditor'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'PlaygroundViewer' is defined but never used. Allowed unused vars must match /^_/u.","line":9,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":9,"endColumn":24,"suggestions":[{"messageId":"removeVar","data":{"varName":"PlaygroundViewer"},"fix":{"range":[425,447],"text":""},"desc":"Remove unused variable 'PlaygroundViewer'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'PlaygroundModal' is defined but never used. Allowed unused vars must match /^_/u.","line":10,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":10,"endColumn":23,"suggestions":[{"messageId":"removeVar","data":{"varName":"PlaygroundModal"},"fix":{"range":[487,508],"text":""},"desc":"Remove unused variable 'PlaygroundModal'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":11,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import React, { useState, useCallback } from \"react\";\nimport { PanelGroup, Panel } from \"react-resizable-panels\";\nimport { Moon, Sun, HelpCircle } from \"lucide-react\";\nimport * as Blockly from \"blockly/core\";\nimport \"blockly/blocks\";\nimport GameNavBar from \"../../components/game/GameNavBar\";\nimport ResizeHandle from \"../../components/game/ResizeHandle\";\nimport PlaygroundEditor from \"./components/PlaygroundEditor\";\nimport PlaygroundViewer from \"./components/PlaygroundViewer\";\nimport PlaygroundModal from \"./components/PlaygroundModal\";\nimport { useCodeGeneration } from \"./hooks/useCodeGeneration\";\nimport { useCodeExecution } from \"./hooks/useCodeExecution\";\nimport { usePlaygroundTour } from \"./hooks/usePlaygroundTour\";\nimport { useIsMobile } from \"../../hooks/useIsMobile\";\nimport \"shepherd.js/dist/css/shepherd.css\";\nimport \"../../styles/shepherd-theme.css\";\nimport \"./Playground.css\";\n\nconst Playground = () => {\n const [workspace, setWorkspace] = useState(null);\n const [workspaceVersion, setWorkspaceVersion] = useState(0);\n const [theme, setTheme] = useState(\"light\");\n const [modal, setModal] = useState({\n isOpen: false,\n type: \"success\",\n title: \"\",\n message: \"\",\n details: \"\",\n });\n const isMobile = useIsMobile();\n const generatedCode = useCodeGeneration(workspace, workspaceVersion);\n const { output, isRunning, runCode, clearOutput } = useCodeExecution();\n const { startTour } = usePlaygroundTour();\n\n const blockCount = generatedCode.javascript\n ? generatedCode.javascript.split(\"\\n\").filter((line) => line.trim()).length\n : 0;\n\n const handleWorkspaceChange = useCallback((newWorkspace) => {\n setWorkspace(newWorkspace);\n setWorkspaceVersion((v) => v + 1);\n }, []);\n\n const handleRunCode = useCallback(() => {\n if (generatedCode.javascript) {\n runCode(generatedCode.javascript);\n }\n }, [generatedCode.javascript, runCode]);\n\n const toggleTheme = useCallback(() => {\n setTheme((prev) => (prev === \"light\" ? \"dark\" : \"light\"));\n }, []);\n\n const handleHelp = useCallback(() => {\n startTour();\n }, [startTour]);\n\n const closeModal = useCallback(() => {\n setModal({\n isOpen: false,\n type: \"success\",\n title: \"\",\n message: \"\",\n details: \"\",\n });\n }, []);\n\n const handleDownloadWorkspace = useCallback(() => {\n if (!workspace) {\n setModal({\n isOpen: true,\n type: \"error\",\n title: \"Erro ao Salvar\",\n message: \"Nenhum workspace disponível para salvar.\",\n details: \"\",\n });\n return;\n }\n\n try {\n const json = Blockly.serialization.workspaces.save(workspace);\n const dataStr = JSON.stringify(json, null, 2);\n const dataBlob = new Blob([dataStr], { type: \"application/json\" });\n const url = URL.createObjectURL(dataBlob);\n const link = document.createElement(\"a\");\n link.href = url;\n const fileName = `playground-${new Date().toISOString().slice(0, 10)}.json`;\n link.download = fileName;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n URL.revokeObjectURL(url);\n\n setModal({\n isOpen: true,\n type: \"success\",\n title: \"Blocos Salvos com Sucesso!\",\n message: \"Seu workspace foi exportado com sucesso.\",\n details: `Arquivo: ${fileName}\\nLocalização: Pasta de Downloads`,\n });\n } catch (error) {\n console.error(\"Erro ao baixar workspace:\", error);\n setModal({\n isOpen: true,\n type: \"error\",\n title: \"Erro ao Salvar\",\n message: \"Ocorreu um erro ao exportar o workspace.\",\n details: error.message,\n });\n }\n }, [workspace]);\n\n const handleUploadWorkspace = useCallback(() => {\n const input = document.createElement(\"input\");\n input.type = \"file\";\n input.accept = \".json\";\n\n input.onchange = (e) => {\n const file = e.target.files[0];\n if (!file) return;\n\n const reader = new FileReader();\n reader.onload = (event) => {\n try {\n const json = JSON.parse(event.target.result);\n\n if (!workspace) {\n setModal({\n isOpen: true,\n type: \"error\",\n title: \"Erro ao Carregar\",\n message: \"O workspace não está pronto para receber dados.\",\n details: \"\",\n });\n return;\n }\n\n // Limpar workspace atual\n workspace.clear();\n\n // Carregar novo workspace\n Blockly.serialization.workspaces.load(json, workspace);\n\n // Atualizar versão para forçar regeneração do código\n setWorkspaceVersion((v) => v + 1);\n\n setModal({\n isOpen: true,\n type: \"success\",\n title: \"Blocos Carregados com Sucesso!\",\n message: \"O workspace foi carregado e está pronto para uso.\",\n details: `Arquivo: ${file.name}`,\n });\n } catch (error) {\n console.error(\"Erro ao carregar workspace:\", error);\n setModal({\n isOpen: true,\n type: \"error\",\n title: \"Erro ao Carregar\",\n message:\n \"Não foi possível carregar o arquivo. Verifique se é um arquivo JSON válido.\",\n details: error.message,\n });\n }\n };\n\n reader.readAsText(file);\n };\n\n input.click();\n }, [workspace]);\n\n return (\n <div\n className={`w-screen h-screen flex flex-col overflow-hidden transition-colors duration-300 ${theme === \"light\" ? \"bg-gray-100\" : \"bg-[#1e1e1e]\"}`}\n >\n <GameNavBar title=\"Laboratório\" type=\"code\" />\n\n <div className=\"flex-1 overflow-hidden\">\n <PanelGroup direction={isMobile ? \"vertical\" : \"horizontal\"}>\n <Panel\n defaultSize={50}\n minSize={isMobile ? 10 : 50}\n maxSize={isMobile ? 90 : 50}\n >\n <div\n className={`h-full flex flex-col ${theme === \"light\" ? \"bg-white\" : \"bg-[#1e1e1e]\"}`}\n >\n <div\n className={`flex justify-between items-center px-5 py-4 min-h-[60px] border-b-2 ${theme === \"light\" ? \"bg-gray-100 border-gray-300 text-gray-800\" : \"bg-[#252526] border-gray-700 text-white\"}`}\n style={{\n borderImage:\n \"linear-gradient(to right, var(--brand-primary), var(--brand-secondary), var(--brand-accent)) 1\",\n }}\n >\n <h2 className=\"m-0 text-lg font-semibold leading-none\">\n Editor de Blocos\n </h2>\n {!isMobile && (\n <div className=\"flex items-center gap-2\">\n <button\n className={`rounded-lg p-2 flex items-center justify-center transition-all duration-200 flex-shrink-0 w-10 h-10 ${\n theme === \"light\"\n ? \"bg-black/10 hover:bg-black/20 text-gray-800\"\n : \"bg-white/10 hover:bg-white/20 text-white\"\n } hover:scale-110`}\n onClick={handleHelp}\n title=\"Ajuda\"\n data-tour=\"help-button\"\n >\n <HelpCircle className=\"w-5 h-5\" />\n </button>\n </div>\n )}\n </div>\n <div className=\"flex-1 overflow-hidden\">\n <PlaygroundEditor\n onWorkspaceChange={handleWorkspaceChange}\n onDownloadWorkspace={handleDownloadWorkspace}\n onUploadWorkspace={handleUploadWorkspace}\n onRunCode={handleRunCode}\n onHelp={handleHelp}\n isRunning={isRunning}\n blockCount={blockCount}\n theme={theme}\n />\n </div>\n </div>\n </Panel>\n\n <ResizeHandle\n direction={isMobile ? \"vertical\" : \"horizontal\"}\n theme={theme}\n disabled={!isMobile}\n />\n\n <Panel\n defaultSize={50}\n minSize={isMobile ? 15 : 50}\n maxSize={isMobile ? 85 : 50}\n >\n <div\n className={`h-full flex flex-col ${theme === \"light\" ? \"bg-white\" : \"bg-[#1e1e1e]\"}`}\n >\n <div\n className={`flex justify-between items-center px-5 py-4 min-h-[60px] border-b-2 ${theme === \"light\" ? \"bg-gray-100 border-gray-300 text-gray-800\" : \"bg-[#252526] border-gray-700 text-white\"}`}\n style={{\n borderImage:\n \"linear-gradient(to right, var(--brand-primary), var(--brand-secondary), var(--brand-accent)) 1\",\n }}\n >\n <h2 className=\"m-0 text-lg font-semibold leading-none\">\n Código\n </h2>\n <button\n className={`rounded-lg p-2 flex items-center justify-center transition-all duration-200 ml-auto flex-shrink-0 w-10 h-10 ${\n theme === \"light\"\n ? \"bg-black/10 hover:bg-black/20 text-gray-800\"\n : \"bg-white/10 hover:bg-white/20 text-white\"\n } hover:scale-110`}\n onClick={toggleTheme}\n title={\n theme === \"light\"\n ? \"Mudar para tema escuro\"\n : \"Mudar para tema claro\"\n }\n data-tour=\"theme-toggle\"\n >\n {theme === \"light\" ? (\n <Moon className=\"w-5 h-5\" />\n ) : (\n <Sun className=\"w-5 h-5\" />\n )}\n </button>\n </div>\n <div className=\"flex-1 overflow-hidden\">\n <PlaygroundViewer\n generatedCode={generatedCode}\n output={output}\n onClearConsole={clearOutput}\n theme={theme}\n />\n </div>\n </div>\n </Panel>\n </PanelGroup>\n </div>\n\n <PlaygroundModal\n isOpen={modal.isOpen}\n onClose={closeModal}\n type={modal.type}\n title={modal.title}\n message={modal.message}\n details={modal.details}\n />\n </div>\n );\n};\n\nexport default Playground;\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/Playground/components/CodeViewer.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'React' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":13,"suggestions":[{"messageId":"removeVar","data":{"varName":"React"},"fix":{"range":[7,13],"text":""},"desc":"Remove unused variable 'React'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Copy' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":14,"suggestions":[{"messageId":"removeVar","data":{"varName":"Copy"},"fix":{"range":[50,55],"text":""},"desc":"Remove unused variable 'Copy'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Check' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":16,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":21,"suggestions":[{"messageId":"removeVar","data":{"varName":"Check"},"fix":{"range":[54,61],"text":""},"desc":"Remove unused variable 'Check'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'CodeMirror' is defined but never used. Allowed unused vars must match /^_/u.","line":3,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":3,"endColumn":18,"suggestions":[{"messageId":"removeVar","data":{"varName":"CodeMirror"},"fix":{"range":[92,108],"text":""},"desc":"Remove unused variable 'CodeMirror'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'language' is assigned a value but never used. Allowed unused args must match /^_/u.","line":9,"column":3,"nodeType":"Identifier","messageId":"unusedVar","endLine":9,"endColumn":11,"suggestions":[{"messageId":"removeVar","data":{"varName":"language"},"fix":{"range":[247,274],"text":""},"desc":"Remove unused variable 'language'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'title' is assigned a value but never used. Allowed unused args must match /^_/u.","line":10,"column":3,"nodeType":"Identifier","messageId":"unusedVar","endLine":10,"endColumn":8,"suggestions":[{"messageId":"removeVar","data":{"varName":"title"},"fix":{"range":[274,294],"text":""},"desc":"Remove unused variable 'title'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":6,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import React, { useState } from \"react\";\nimport { Copy, Check } from \"lucide-react\";\nimport CodeMirror from \"@uiw/react-codemirror\";\nimport { javascript } from \"@codemirror/lang-javascript\";\nimport \"./CodeViewer.css\";\n\nconst CodeViewer = ({\n code,\n language = \"javascript\",\n title = \"Código\",\n theme = \"light\",\n}) => {\n const [copied, setCopied] = useState(false);\n\n const handleCopy = async () => {\n try {\n await navigator.clipboard.writeText(code);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n } catch (error) {\n console.error(\"Erro ao copiar:\", error);\n }\n };\n\n return (\n <div className={`code-viewer-container code-viewer-theme-${theme}`}>\n <button\n className={`code-viewer-copy-btn ${copied ? \"copied\" : \"\"}`}\n onClick={handleCopy}\n title=\"Copiar código\"\n >\n {copied ? (\n <>\n <Check className=\"w-4 h-4\" />\n <span>Copiado!</span>\n </>\n ) : (\n <>\n <Copy className=\"w-4 h-4\" />\n <span>Copiar</span>\n </>\n )}\n </button>\n\n <div className=\"code-viewer-editor\">\n <CodeMirror\n value={code || \"// Nenhum código gerado ainda\"}\n height=\"100%\"\n theme={theme}\n extensions={[javascript()]}\n basicSetup={{\n lineNumbers: true,\n highlightActiveLineGutter: false,\n highlightActiveLine: false,\n foldGutter: false,\n dropCursor: false,\n allowMultipleSelections: false,\n indentOnInput: false,\n bracketMatching: true,\n closeBrackets: false,\n autocompletion: false,\n highlightSelectionMatches: false,\n }}\n editable={false}\n readOnly={true}\n className=\"h-full\"\n />\n </div>\n </div>\n );\n};\n\nexport default CodeViewer;\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/Playground/components/ExamplesModal.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'React' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":13,"suggestions":[{"messageId":"removeVar","data":{"varName":"React"},"fix":{"range":[7,18],"text":""},"desc":"Remove unused variable 'React'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'X' is defined but never used. Allowed unused vars must match /^_/u.","line":3,"column":3,"nodeType":"Identifier","messageId":"unusedVar","endLine":3,"endColumn":4,"suggestions":[{"messageId":"removeVar","data":{"varName":"X"},"fix":{"range":[38,40],"text":""},"desc":"Remove unused variable 'X'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'BookOpen' is defined but never used. Allowed unused vars must match /^_/u.","line":4,"column":3,"nodeType":"Identifier","messageId":"unusedVar","endLine":4,"endColumn":11,"suggestions":[{"messageId":"removeVar","data":{"varName":"BookOpen"},"fix":{"range":[39,51],"text":""},"desc":"Remove unused variable 'BookOpen'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Play' is defined but never used. Allowed unused vars must match /^_/u.","line":5,"column":3,"nodeType":"Identifier","messageId":"unusedVar","endLine":5,"endColumn":7,"suggestions":[{"messageId":"removeVar","data":{"varName":"Play"},"fix":{"range":[51,59],"text":""},"desc":"Remove unused variable 'Play'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'IconComponent' is assigned a value but never used. Allowed unused vars must match /^_/u.","line":126,"column":21,"nodeType":"Identifier","messageId":"unusedVar","endLine":126,"endColumn":34,"suggestions":[{"messageId":"removeVar","data":{"varName":"IconComponent"},"fix":{"range":[3764,3799],"text":""},"desc":"Remove unused variable 'IconComponent'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":5,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import React from \"react\";\nimport {\n X,\n BookOpen,\n Play,\n Hash,\n User,\n RotateCw,\n Plus,\n Dices,\n BarChart3,\n Thermometer,\n Calculator,\n} from \"lucide-react\";\n\nconst ExamplesModal = ({ isOpen, onClose, onSelectExample }) => {\n if (!isOpen) return null;\n\n const examples = [\n {\n id: \"contador\",\n name: \"Contador de 1 a 10\",\n description: \"Um loop que conta de 1 a 10 e imprime cada valor\",\n file: \"/examples/contador.json\",\n icon: Hash,\n },\n {\n id: \"nome\",\n name: \"Imprimir Texto\",\n description: \"Um programa simples que imprime um texto\",\n file: \"/examples/nome.json\",\n icon: User,\n },\n {\n id: \"fibonacci\",\n name: \"Fibonacci\",\n description: \"Um exemplo de sequência de Fibonacci em blocos\",\n file: \"/examples/fibonacci.json\",\n icon: RotateCw,\n },\n {\n id: \"soma-lista\",\n name: \"Soma de Lista\",\n description: \"Percorre uma lista de números e calcula a soma total\",\n file: \"/examples/soma-lista.json\",\n icon: Plus,\n },\n {\n id: \"par-impar\",\n name: \"Par ou Ímpar\",\n description:\n \"Verifica se um número é par ou ímpar usando operador módulo\",\n file: \"/examples/par-impar.json\",\n icon: Dices,\n },\n {\n id: \"maior-numero\",\n name: \"Maior Número\",\n description: \"Encontra o maior número em uma lista usando comparação\",\n file: \"/examples/maior-numero.json\",\n icon: BarChart3,\n },\n {\n id: \"temperatura\",\n name: \"Conversor de Temperatura\",\n description: \"Converte temperatura de Celsius para Fahrenheit\",\n file: \"/examples/temperatura.json\",\n icon: Thermometer,\n },\n {\n id: \"fatorial\",\n name: \"Cálculo Fatorial\",\n description: \"Calcula o fatorial de um número usando loop\",\n file: \"/examples/fatorial.json\",\n icon: Calculator,\n },\n ];\n\n const handleExampleClick = async (example) => {\n try {\n const response = await fetch(example.file);\n if (!response.ok) {\n throw new Error(\"Erro ao carregar exemplo\");\n }\n const data = await response.json();\n\n // Extrair apenas os dados do workspace (blocks e variables)\n const workspaceData = {\n blocks: data.blocks,\n ...(data.variables && { variables: data.variables }),\n };\n\n onSelectExample(workspaceData);\n onClose();\n } catch (error) {\n console.error(\"Erro ao carregar exemplo:\", error);\n alert(\"Erro ao carregar exemplo. Tente novamente.\");\n }\n };\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm p-4\">\n <div className=\"bg-white/95 backdrop-blur-sm border border-white/30 rounded-xl shadow-xl w-full max-w-2xl max-h-[90vh] flex flex-col\">\n {/* Header */}\n <div className=\"flex items-center justify-between p-6 border-b border-white/20\">\n <div className=\"flex items-center space-x-3\">\n <div className=\"w-8 h-8 bg-gradient-to-br from-purple-400 to-pink-500 rounded-lg flex items-center justify-center text-white\">\n <BookOpen className=\"w-5 h-5\" />\n </div>\n <h3 className=\"text-xl font-bold text-gray-800\">Exemplos</h3>\n </div>\n <button\n onClick={onClose}\n className=\"w-8 h-8 rounded-lg bg-gray-200 hover:bg-gray-300 flex items-center justify-center text-gray-600 transition-colors\"\n aria-label=\"Fechar\"\n data-modal-close\n >\n <X className=\"w-4 h-4\" />\n </button>\n </div>\n\n {/* Content */}\n <div className=\"flex-1 overflow-y-auto p-6\">\n <div className=\"grid gap-4\">\n {examples.map((example) => {\n const IconComponent = example.icon;\n return (\n <button\n key={example.id}\n onClick={() => handleExampleClick(example)}\n className=\"group relative flex items-start gap-4 p-5 bg-white/80 backdrop-blur-sm border border-white/30 rounded-xl shadow-lg hover:border-blue-300 transition-all duration-200 text-left\"\n >\n {/* Icon */}\n <div className=\"flex-shrink-0 w-10 h-10 bg-gradient-to-br from-purple-400 to-pink-500 rounded-lg flex items-center justify-center text-white\">\n <IconComponent className=\"w-5 h-5\" />\n </div>\n\n {/* Content */}\n <div className=\"flex-1 min-w-0\">\n <h3 className=\"text-base font-semibold text-gray-800 mb-1 group-hover:text-blue-600 transition-colors\">\n {example.name}\n </h3>\n <p className=\"text-sm text-gray-600\">\n {example.description}\n </p>\n </div>\n\n {/* Arrow Icon */}\n <div className=\"flex-shrink-0 opacity-0 group-hover:opacity-100 transition-opacity\">\n <Play className=\"w-5 h-5 text-blue-500\" />\n </div>\n </button>\n );\n })}\n </div>\n </div>\n </div>\n </div>\n );\n};\n\nexport default ExamplesModal;\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/Playground/components/PlaygroundConsole.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'React' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":13,"suggestions":[{"messageId":"removeVar","data":{"varName":"React"},"fix":{"range":[7,13],"text":""},"desc":"Remove unused variable 'React'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Trash2' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":16,"suggestions":[{"messageId":"removeVar","data":{"varName":"Trash2"},"fix":{"range":[50,88],"text":""},"desc":"Remove unused variable 'Trash2'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import React, { useEffect, useRef } from \"react\";\nimport { Trash2 } from \"lucide-react\";\nimport \"./PlaygroundConsole.css\";\n\nconst PlaygroundConsole = ({ output, onClear, theme = \"light\" }) => {\n const consoleRef = useRef(null);\n\n useEffect(() => {\n if (consoleRef.current) {\n consoleRef.current.scrollTop = consoleRef.current.scrollHeight;\n }\n }, [output]);\n\n const formatTimestamp = (date) => {\n return date.toLocaleTimeString(\"pt-BR\", {\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n fractionalSecondDigits: 3,\n });\n };\n\n const getIcon = (type) => {\n switch (type) {\n case \"error\":\n return \"❌\";\n case \"success\":\n return \"✅\";\n case \"alert\":\n return \"⚠️\";\n default:\n return \"\";\n }\n };\n\n return (\n <div\n className={`playground-console-container playground-console-theme-${theme}`}\n >\n <div className=\"playground-console-header\">\n <span className=\"playground-console-title\">\n Console ({output.length})\n </span>\n <button\n className=\"playground-console-clear-btn\"\n onClick={onClear}\n disabled={output.length === 0}\n title=\"Limpar console\"\n >\n <Trash2 className=\"w-4 h-4\" />\n <span>Limpar</span>\n </button>\n </div>\n <div className=\"playground-console-content\" ref={consoleRef}>\n {output.length === 0 ? (\n <div className=\"playground-console-empty\">\n Nenhuma saída ainda. Execute o código para ver os resultados.\n </div>\n ) : (\n output.map((entry) => (\n <div\n key={entry.id}\n className={`playground-console-entry playground-console-${entry.type}`}\n >\n <span className=\"playground-console-timestamp\">\n {formatTimestamp(entry.timestamp)}\n </span>\n <span className=\"playground-console-icon\">\n {getIcon(entry.type)}\n </span>\n <span className=\"playground-console-message\">\n {entry.message}\n </span>\n </div>\n ))\n )}\n </div>\n </div>\n );\n};\n\nexport default PlaygroundConsole;\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/Playground/components/PlaygroundEditor.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'React' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":13,"suggestions":[{"messageId":"removeVar","data":{"varName":"React"},"fix":{"range":[7,13],"text":""},"desc":"Remove unused variable 'React'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Download' is defined but never used. Allowed unused vars must match /^_/u.","line":10,"column":3,"nodeType":"Identifier","messageId":"unusedVar","endLine":10,"endColumn":11,"suggestions":[{"messageId":"removeVar","data":{"varName":"Download"},"fix":{"range":[145,154],"text":""},"desc":"Remove unused variable 'Download'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Upload' is defined but never used. Allowed unused vars must match /^_/u.","line":11,"column":3,"nodeType":"Identifier","messageId":"unusedVar","endLine":11,"endColumn":9,"suggestions":[{"messageId":"removeVar","data":{"varName":"Upload"},"fix":{"range":[153,163],"text":""},"desc":"Remove unused variable 'Upload'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Play' is defined but never used. Allowed unused vars must match /^_/u.","line":12,"column":3,"nodeType":"Identifier","messageId":"unusedVar","endLine":12,"endColumn":7,"suggestions":[{"messageId":"removeVar","data":{"varName":"Play"},"fix":{"range":[163,171],"text":""},"desc":"Remove unused variable 'Play'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Loader' is defined but never used. Allowed unused vars must match /^_/u.","line":13,"column":3,"nodeType":"Identifier","messageId":"unusedVar","endLine":13,"endColumn":9,"suggestions":[{"messageId":"removeVar","data":{"varName":"Loader"},"fix":{"range":[171,181],"text":""},"desc":"Remove unused variable 'Loader'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'CircleAlert' is defined but never used. Allowed unused vars must match /^_/u.","line":14,"column":3,"nodeType":"Identifier","messageId":"unusedVar","endLine":14,"endColumn":14,"suggestions":[{"messageId":"removeVar","data":{"varName":"CircleAlert"},"fix":{"range":[181,196],"text":""},"desc":"Remove unused variable 'CircleAlert'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'BookOpen' is defined but never used. Allowed unused vars must match /^_/u.","line":15,"column":3,"nodeType":"Identifier","messageId":"unusedVar","endLine":15,"endColumn":11,"suggestions":[{"messageId":"removeVar","data":{"varName":"BookOpen"},"fix":{"range":[196,208],"text":""},"desc":"Remove unused variable 'BookOpen'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'HelpCircle' is defined but never used. Allowed unused vars must match /^_/u.","line":16,"column":3,"nodeType":"Identifier","messageId":"unusedVar","endLine":16,"endColumn":13,"suggestions":[{"messageId":"removeVar","data":{"varName":"HelpCircle"},"fix":{"range":[208,222],"text":""},"desc":"Remove unused variable 'HelpCircle'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'ExamplesModal' is defined but never used. Allowed unused vars must match /^_/u.","line":25,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":25,"endColumn":21,"suggestions":[{"messageId":"removeVar","data":{"varName":"ExamplesModal"},"fix":{"range":[529,548],"text":""},"desc":"Remove unused variable 'ExamplesModal'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":9,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import React, {\n useEffect,\n useRef,\n useCallback,\n useMemo,\n useState,\n} from \"react\";\nimport * as Blockly from \"blockly/core\";\nimport {\n Download,\n Upload,\n Play,\n Loader,\n CircleAlert,\n BookOpen,\n HelpCircle,\n} from \"lucide-react\";\nimport { playgroundToolbox } from \"../config/playgroundToolbox\";\nimport { defaultWorkspace } from \"../config/defaultWorkspace\";\nimport {\n loadWorkspace,\n createDebouncedSave,\n} from \"../services/playgroundStorage\";\nimport { useIsMobile } from \"../../../hooks/useIsMobile\";\nimport ExamplesModal from \"./ExamplesModal\";\nimport \"./PlaygroundEditor.css\";\n\n// Tema claro personalizado\nconst lightTheme = Blockly.Theme.defineTheme(\"light\", {\n base: Blockly.Themes.Classic,\n componentStyles: {\n workspaceBackgroundColour: \"#f9fafb\",\n toolboxBackgroundColour: \"#e5e7eb\",\n toolboxForegroundColour: \"#374151\",\n flyoutBackgroundColour: \"#f3f4f6\",\n flyoutForegroundColour: \"#111827\",\n flyoutOpacity: 0.95,\n scrollbarColour: \"#9ca3af\",\n scrollbarOpacity: 0.5,\n },\n});\n\n// Tema escuro personalizado\nconst darkTheme = Blockly.Theme.defineTheme(\"dark\", {\n base: Blockly.Themes.Classic,\n componentStyles: {\n workspaceBackgroundColour: \"#1e1e1e\",\n toolboxBackgroundColour: \"#252526\",\n toolboxForegroundColour: \"#cccccc\",\n flyoutBackgroundColour: \"#2d2d30\",\n flyoutForegroundColour: \"#ffffff\",\n flyoutOpacity: 0.95,\n scrollbarColour: \"#6b7280\",\n scrollbarOpacity: 0.5,\n },\n});\n\nconst PlaygroundEditor = ({\n onWorkspaceChange,\n onDownloadWorkspace,\n onUploadWorkspace,\n onRunCode,\n onHelp,\n isRunning,\n blockCount = 0,\n theme = \"light\",\n}) => {\n const blocklyDiv = useRef(null);\n const workspaceRef = useRef(null);\n const isInitializedRef = useRef(false);\n const isMobile = useIsMobile();\n const debouncedSave = useMemo(() => createDebouncedSave(1000), []);\n const [showExamples, setShowExamples] = useState(false);\n const [isVisible, setIsVisible] = useState(true);\n\n // Monitorar visibilidade da página\n useEffect(() => {\n const handleVisibilityChange = () => {\n const visible = !document.hidden;\n setIsVisible(visible);\n\n if (visible && workspaceRef.current) {\n // Forçar resize quando a página voltar a ficar visível\n requestAnimationFrame(() => {\n if (workspaceRef.current) {\n Blockly.svgResize(workspaceRef.current);\n }\n });\n }\n };\n\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\n return () =>\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange);\n }, []);\n\n // Forçar resize quando isVisible mudar\n useEffect(() => {\n if (isVisible && workspaceRef.current) {\n const timer = setTimeout(() => {\n if (workspaceRef.current) {\n Blockly.svgResize(workspaceRef.current);\n }\n }, 100);\n return () => clearTimeout(timer);\n }\n }, [isVisible]);\n\n const handleWorkspaceChange = useCallback(() => {\n if (!workspaceRef.current) return;\n\n debouncedSave(workspaceRef.current);\n\n if (onWorkspaceChange) {\n onWorkspaceChange(workspaceRef.current);\n }\n }, [onWorkspaceChange, debouncedSave]);\n\n useEffect(() => {\n if (isInitializedRef.current || !blocklyDiv.current) return;\n\n isInitializedRef.current = true;\n\n const toolboxWithIcons = {\n ...playgroundToolbox,\n contents: playgroundToolbox.contents.map((cat) => ({\n ...cat,\n \"css-icon\": cat.cssIcon || \"fa fa-cube\",\n })),\n };\n\n workspaceRef.current = Blockly.inject(blocklyDiv.current, {\n toolbox: toolboxWithIcons,\n theme: theme === \"light\" ? lightTheme : darkTheme,\n grid: {\n spacing: 25,\n length: 3,\n colour: theme === \"light\" ? \"#ccc\" : \"#3a3a3a\",\n snap: true,\n },\n zoom: {\n controls: false,\n wheel: true,\n startScale: 1.0,\n maxScale: 3,\n minScale: 0.3,\n scaleSpeed: 1.2,\n },\n trashcan: true,\n scrollbars: true,\n renderer: \"zelos\",\n });\n\n const savedData = loadWorkspace();\n const workspaceToLoad = savedData || defaultWorkspace;\n\n try {\n Blockly.serialization.workspaces.load(\n workspaceToLoad,\n workspaceRef.current,\n );\n } catch (error) {\n console.error(\"Erro ao carregar workspace:\", error);\n // Se falhar, tenta carregar o workspace padrão\n try {\n Blockly.serialization.workspaces.load(\n defaultWorkspace,\n workspaceRef.current,\n );\n } catch (fallbackError) {\n console.error(\"Erro ao carregar workspace padrão:\", fallbackError);\n }\n }\n\n handleWorkspaceChange();\n\n const listener = (event) => {\n if (event.isUiEvent) return;\n handleWorkspaceChange();\n };\n\n workspaceRef.current.addChangeListener(listener);\n\n // Fazer zoom para ajustar todos os blocos na tela (especialmente útil em mobile)\n setTimeout(() => {\n if (workspaceRef.current && isMobile) {\n const metrics = workspaceRef.current.getMetrics();\n if (metrics) {\n // Calcular escala para caber todos os blocos\n workspaceRef.current.zoomToFit();\n }\n }\n }, 200);\n\n // Adicionar ResizeObserver para garantir que o workspace se redimensione corretamente\n const resizeObserver = new ResizeObserver(() => {\n if (workspaceRef.current) {\n Blockly.svgResize(workspaceRef.current);\n }\n });\n\n if (blocklyDiv.current) {\n resizeObserver.observe(blocklyDiv.current);\n }\n\n // Listener para quando a página voltar a ficar visível (alt+tab, mudança de aba)\n const handleVisibilityChange = () => {\n if (!document.hidden && workspaceRef.current) {\n // Pequeno delay para garantir que o layout está estável\n setTimeout(() => {\n if (workspaceRef.current) {\n Blockly.svgResize(workspaceRef.current);\n }\n }, 50);\n }\n };\n\n // Listener para quando a janela receber foco novamente\n const handleWindowFocus = () => {\n if (workspaceRef.current) {\n setTimeout(() => {\n if (workspaceRef.current) {\n Blockly.svgResize(workspaceRef.current);\n }\n }, 50);\n }\n };\n\n // Listener para resize da janela\n const handleWindowResize = () => {\n if (workspaceRef.current) {\n Blockly.svgResize(workspaceRef.current);\n }\n };\n\n document.addEventListener(\"visibilitychange\", handleVisibilityChange);\n window.addEventListener(\"focus\", handleWindowFocus);\n window.addEventListener(\"resize\", handleWindowResize);\n\n // Forçar um resize inicial após um pequeno delay\n setTimeout(() => {\n if (workspaceRef.current) {\n Blockly.svgResize(workspaceRef.current);\n }\n }, 100);\n\n return () => {\n resizeObserver.disconnect();\n document.removeEventListener(\"visibilitychange\", handleVisibilityChange);\n window.removeEventListener(\"focus\", handleWindowFocus);\n window.removeEventListener(\"resize\", handleWindowResize);\n if (workspaceRef.current) {\n workspaceRef.current.dispose();\n workspaceRef.current = null;\n }\n };\n }, [handleWorkspaceChange]);\n\n // Efeito para trocar o tema do Blockly dinamicamente\n useEffect(() => {\n if (!workspaceRef.current) return;\n\n const newTheme = theme === \"light\" ? lightTheme : darkTheme;\n workspaceRef.current.setTheme(newTheme);\n\n // Atualizar a cor do grid\n const gridColour = theme === \"light\" ? \"#ccc\" : \"#3a3a3a\";\n workspaceRef.current.getTheme().componentStyles.gridColour = gridColour;\n\n // Forçar atualização visual\n workspaceRef.current.refreshTheme();\n }, [theme]);\n\n const naoTemBlocos = blockCount === 0;\n\n const handleLoadExample = useCallback(\n (exampleData) => {\n if (!workspaceRef.current) return;\n\n try {\n // Limpar workspace atual\n workspaceRef.current.clear();\n\n // Carregar o exemplo\n Blockly.serialization.workspaces.load(\n exampleData,\n workspaceRef.current,\n );\n\n // Fazer zoom para ajustar todos os blocos na tela (especialmente útil em mobile)\n setTimeout(() => {\n if (workspaceRef.current && isMobile) {\n workspaceRef.current.zoomToFit();\n }\n }, 150);\n\n // Notificar mudança\n handleWorkspaceChange();\n } catch (error) {\n console.error(\"Erro ao carregar exemplo:\", error);\n alert(\"Erro ao carregar exemplo. Tente novamente.\");\n }\n },\n [handleWorkspaceChange, isMobile],\n );\n\n const getRunButtonClasses = () => {\n const baseClasses = `flex items-center gap-2 ${isMobile ? \"px-3 py-3\" : \"px-6 py-3\"} rounded-full text-sm font-medium text-white transition-all duration-200 shadow-md hover:shadow-lg`;\n\n if (naoTemBlocos) {\n return `${baseClasses} bg-gradient-to-r from-amber-700 to-amber-600 cursor-not-allowed opacity-100`;\n }\n\n if (isRunning) {\n return `${baseClasses} bg-gradient-to-r from-blue-500 to-emerald-500 hover:from-blue-600 hover:to-emerald-600 hover:-translate-y-0.5`;\n }\n\n return `${baseClasses} bg-gradient-to-r from-red-500 via-pink-500 to-purple-500 hover:from-red-600 hover:via-pink-600 hover:to-purple-600 hover:-translate-y-0.5`;\n };\n\n return (\n <div className=\"playground-editor-container\" data-tour=\"blockly-editor\">\n <div\n className={`flex gap-3 p-3 border-b-2 border-transparent ${\n theme === \"light\"\n ? \"bg-gradient-to-br from-gray-50 to-gray-200\"\n : \"bg-gradient-to-br from-[#252526] to-[#1e1e1e]\"\n }`}\n style={{\n borderImage:\n \"linear-gradient(to right, var(--brand-primary), var(--brand-secondary), var(--brand-accent)) 1\",\n }}\n data-tour=\"toolbar\"\n >\n <button\n onClick={onRunCode}\n className={getRunButtonClasses()}\n disabled={naoTemBlocos && !isRunning}\n data-tour=\"run-button\"\n title={\n isRunning\n ? \"Executando...\"\n : naoTemBlocos\n ? \"Adicione blocos para executar\"\n : \"Executar\"\n }\n >\n {isRunning ? (\n <>\n <Loader className=\"w-4 h-4 animate-spin\" />\n {!isMobile && <span>Executando...</span>}\n </>\n ) : naoTemBlocos ? (\n <>\n <CircleAlert className=\"w-4 h-4 opacity-50\" />\n {!isMobile && <span>Adicione blocos para executar</span>}\n </>\n ) : (\n <>\n <Play className=\"w-4 h-4\" />\n {!isMobile && <span>Executar</span>}\n </>\n )}\n </button>\n\n <button\n onClick={onDownloadWorkspace}\n className={`flex items-center gap-2 ${isMobile ? \"px-3 py-3\" : \"px-6 py-3\"} bg-gradient-to-r from-blue-500 to-purple-600 text-white rounded-full text-sm font-medium transition-all duration-200 shadow-md hover:shadow-lg hover:from-blue-600 hover:to-purple-700 hover:-translate-y-0.5 active:translate-y-0`}\n title=\"Salvar blocos do workspace\"\n data-tour=\"download-button\"\n >\n <Download className=\"w-4 h-4\" />\n {!isMobile && <span>Salvar Blocos</span>}\n </button>\n\n <button\n onClick={onUploadWorkspace}\n className={`flex items-center gap-2 ${isMobile ? \"px-3 py-3\" : \"px-6 py-3\"} bg-gradient-to-r from-blue-500 to-purple-600 text-white rounded-full text-sm font-medium transition-all duration-200 shadow-md hover:shadow-lg hover:from-blue-600 hover:to-purple-700 hover:-translate-y-0.5 active:translate-y-0`}\n title=\"Carregar blocos salvos\"\n data-tour=\"upload-button\"\n >\n <Upload className=\"w-4 h-4\" />\n {!isMobile && <span>Carregar Blocos</span>}\n </button>\n\n <button\n onClick={() => setShowExamples(true)}\n className={`flex items-center gap-2 ${isMobile ? \"px-3 py-3\" : \"px-6 py-3\"} bg-gradient-to-r from-green-500 to-teal-600 text-white rounded-full text-sm font-medium transition-all duration-200 shadow-md hover:shadow-lg hover:from-green-600 hover:to-teal-700 hover:-translate-y-0.5 active:translate-y-0`}\n data-tour=\"examples-button\"\n title=\"Ver exemplos de código\"\n >\n <BookOpen className=\"w-4 h-4\" />\n {!isMobile && <span>Exemplos</span>}\n </button>\n\n {isMobile && onHelp && (\n <button\n onClick={onHelp}\n className={`flex items-center justify-center px-3 py-3 rounded-full text-sm font-medium transition-all duration-200 shadow-md hover:shadow-lg hover:-translate-y-0.5 active:translate-y-0 ${\n theme === \"light\"\n ? \"bg-gray-200 hover:bg-gray-300 text-gray-800\"\n : \"bg-gray-700 hover:bg-gray-600 text-white\"\n }`}\n data-tour=\"help-button\"\n title=\"Ajuda - Tour guiado\"\n >\n <HelpCircle className=\"w-4 h-4\" />\n </button>\n )}\n </div>\n <div ref={blocklyDiv} className=\"playground-editor\" />\n\n <ExamplesModal\n isOpen={showExamples}\n onClose={() => setShowExamples(false)}\n onSelectExample={handleLoadExample}\n />\n </div>\n );\n};\n\nexport default PlaygroundEditor;\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/Playground/components/PlaygroundModal.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'React' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":13,"suggestions":[{"messageId":"removeVar","data":{"varName":"React"},"fix":{"range":[7,18],"text":""},"desc":"Remove unused variable 'React'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'X' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":11,"suggestions":[{"messageId":"removeVar","data":{"varName":"X"},"fix":{"range":[36,38],"text":""},"desc":"Remove unused variable 'X'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'CheckCircle' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":13,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":24,"suggestions":[{"messageId":"removeVar","data":{"varName":"CheckCircle"},"fix":{"range":[37,50],"text":""},"desc":"Remove unused variable 'CheckCircle'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'AlertCircle' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":26,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":37,"suggestions":[{"messageId":"removeVar","data":{"varName":"AlertCircle"},"fix":{"range":[50,63],"text":""},"desc":"Remove unused variable 'AlertCircle'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":4,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import React from \"react\";\nimport { X, CheckCircle, AlertCircle } from \"lucide-react\";\n\nconst PlaygroundModal = ({\n isOpen,\n onClose,\n type = \"success\",\n title,\n message,\n details,\n}) => {\n if (!isOpen) return null;\n\n const getIcon = () => {\n switch (type) {\n case \"success\":\n return <CheckCircle className=\"w-12 h-12 text-green-500\" />;\n case \"error\":\n return <AlertCircle className=\"w-12 h-12 text-red-500\" />;\n default:\n return null;\n }\n };\n\n const getStyles = () => {\n switch (type) {\n case \"success\":\n return {\n border: \"border-green-500\",\n bg: \"bg-green-50\",\n titleColor: \"text-green-900\",\n messageColor: \"text-green-700\",\n };\n case \"error\":\n return {\n border: \"border-red-500\",\n bg: \"bg-red-50\",\n titleColor: \"text-red-900\",\n messageColor: \"text-red-700\",\n };\n default:\n return {\n border: \"border-gray-500\",\n bg: \"bg-gray-50\",\n titleColor: \"text-gray-900\",\n messageColor: \"text-gray-700\",\n };\n }\n };\n\n const styles = getStyles();\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50 backdrop-blur-sm\">\n <div className=\"relative bg-white rounded-2xl shadow-2xl max-w-md w-full mx-4 overflow-hidden\">\n {/* Header com gradiente */}\n <div\n className={`${styles.bg} border-b-2 ${styles.border} p-6 flex items-center justify-between`}\n >\n <div className=\"flex items-center gap-4\">\n {getIcon()}\n <h2 className={`text-xl font-bold ${styles.titleColor}`}>\n {title}\n </h2>\n </div>\n <button\n onClick={onClose}\n className=\"p-1 rounded-full hover:bg-white hover:bg-opacity-50 transition-all\"\n title=\"Fechar\"\n >\n <X className=\"w-5 h-5 text-gray-600\" />\n </button>\n </div>\n\n {/* Conteúdo */}\n <div className=\"p-6\">\n <p className={`text-base ${styles.messageColor} mb-4`}>{message}</p>\n\n {details && (\n <div className=\"bg-gray-100 rounded-lg p-4 border border-gray-200\">\n <p className=\"text-sm text-gray-600 font-mono break-all\">\n {details}\n </p>\n </div>\n )}\n </div>\n\n {/* Footer */}\n <div className=\"bg-gray-50 px-6 py-4 flex justify-end border-t border-gray-200\">\n <button\n onClick={onClose}\n className=\"px-6 py-2 bg-gradient-to-r from-blue-500 to-purple-500 text-white rounded-full font-medium hover:from-blue-600 hover:to-purple-600 transition-all shadow-md hover:shadow-lg transform hover:-translate-y-0.5\"\n >\n Fechar\n </button>\n </div>\n </div>\n </div>\n );\n};\n\nexport default PlaygroundModal;\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/Playground/components/PlaygroundViewer.jsx","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'React' is defined but never used. Allowed unused vars must match /^_/u.","line":1,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":1,"endColumn":13,"suggestions":[{"messageId":"removeVar","data":{"varName":"React"},"fix":{"range":[7,13],"text":""},"desc":"Remove unused variable 'React'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'PanelGroup' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":20,"suggestions":[{"messageId":"removeVar","data":{"varName":"PanelGroup"},"fix":{"range":[50,61],"text":""},"desc":"Remove unused variable 'PanelGroup'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'Panel' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":22,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":27,"suggestions":[{"messageId":"removeVar","data":{"varName":"Panel"},"fix":{"range":[60,67],"text":""},"desc":"Remove unused variable 'Panel'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'PanelResizeHandle' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":29,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":46,"suggestions":[{"messageId":"removeVar","data":{"varName":"PanelResizeHandle"},"fix":{"range":[67,86],"text":""},"desc":"Remove unused variable 'PanelResizeHandle'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'CodeViewer' is defined but never used. Allowed unused vars must match /^_/u.","line":3,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":3,"endColumn":18,"suggestions":[{"messageId":"removeVar","data":{"varName":"CodeViewer"},"fix":{"range":[127,143],"text":""},"desc":"Remove unused variable 'CodeViewer'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'PlaygroundConsole' is defined but never used. Allowed unused vars must match /^_/u.","line":4,"column":8,"nodeType":"Identifier","messageId":"unusedVar","endLine":4,"endColumn":25,"suggestions":[{"messageId":"removeVar","data":{"varName":"PlaygroundConsole"},"fix":{"range":[166,189],"text":""},"desc":"Remove unused variable 'PlaygroundConsole'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'ResizeHandle' is assigned a value but never used. Allowed unused vars must match /^_/u.","line":6,"column":7,"nodeType":"Identifier","messageId":"unusedVar","endLine":6,"endColumn":19,"suggestions":[{"messageId":"removeVar","data":{"varName":"ResizeHandle"},"fix":{"range":[213,1425],"text":""},"desc":"Remove unused variable 'ResizeHandle'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":7,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import React, { useState } from \"react\";\nimport { PanelGroup, Panel, PanelResizeHandle } from \"react-resizable-panels\";\nimport CodeViewer from \"./CodeViewer\";\nimport PlaygroundConsole from \"./PlaygroundConsole\";\n\nconst ResizeHandle = ({ theme }) => {\n return (\n <PanelResizeHandle\n className={`h-3 flex items-center justify-center cursor-row-resize transition-colors duration-200 group ${\n theme === \"light\"\n ? \"bg-gray-200 hover:bg-gray-300\"\n : \"bg-gray-700 hover:bg-gray-600\"\n }`}\n >\n <div className=\"flex flex-row gap-1 items-center justify-center\">\n <span\n className={`block w-1 h-1 rounded-full transition-colors duration-200 ${\n theme === \"light\"\n ? \"bg-gray-500 group-hover:bg-gray-700\"\n : \"bg-gray-400 group-hover:bg-gray-200\"\n }`}\n />\n <span\n className={`block w-1 h-1 rounded-full transition-colors duration-200 ${\n theme === \"light\"\n ? \"bg-gray-500 group-hover:bg-gray-700\"\n : \"bg-gray-400 group-hover:bg-gray-200\"\n }`}\n />\n <span\n className={`block w-1 h-1 rounded-full transition-colors duration-200 ${\n theme === \"light\"\n ? \"bg-gray-500 group-hover:bg-gray-700\"\n : \"bg-gray-400 group-hover:bg-gray-200\"\n }`}\n />\n </div>\n </PanelResizeHandle>\n );\n};\n\nconst PlaygroundViewer = ({\n generatedCode,\n output,\n onClearConsole,\n theme = \"light\",\n}) => {\n const [activeTab, setActiveTab] = useState(\"javascript\");\n\n const tabs = [\n { id: \"javascript\", label: \"JavaScript\", icon: \"JS\" },\n { id: \"python\", label: \"Python\", icon: \"PY\" },\n ];\n\n const renderContent = () => {\n switch (activeTab) {\n case \"javascript\":\n return (\n <CodeViewer\n code={generatedCode.javascript}\n language=\"javascript\"\n title=\"JavaScript\"\n theme={theme}\n />\n );\n\n case \"python\":\n return (\n <CodeViewer\n code={generatedCode.python}\n language=\"python\"\n title=\"Python\"\n theme={theme}\n />\n );\n\n default:\n return null;\n }\n };\n\n return (\n <div\n className={`flex flex-col h-full transition-colors duration-300 ${theme === \"light\" ? \"bg-white\" : \"bg-[#1e1e1e]\"}`}\n >\n <div\n className={`flex p-0 gap-0.5 transition-all duration-300 ${theme === \"light\" ? \"bg-gray-100 border-b border-gray-300\" : \"bg-[#252526] border-b border-gray-700\"}`}\n >\n {tabs.map((tab) => (\n <button\n key={tab.id}\n className={`flex items-center gap-1.5 px-4 py-2.5 bg-transparent border-none cursor-pointer text-[13px] font-medium transition-all duration-200 ${\n activeTab === tab.id\n ? `border-b-2 border-transparent ${theme === \"light\" ? \"text-black bg-white\" : \"text-white bg-[#1e1e1e]\"}`\n : `border-b-2 border-transparent ${theme === \"light\" ? \"text-gray-600 hover:bg-gray-200 hover:text-gray-800\" : \"text-gray-500 hover:bg-[#2a2a2a] hover:text-gray-300\"}`\n }`}\n style={\n activeTab === tab.id\n ? {\n borderBottom: \"2px solid\",\n borderImage:\n \"linear-gradient(to right, var(--brand-primary), var(--brand-secondary), var(--brand-accent)) 1\",\n }\n : {}\n }\n onClick={() => setActiveTab(tab.id)}\n title={tab.label}\n >\n <span className=\"text-sm\">{tab.icon}</span>\n <span className=\"font-sans\">{tab.label}</span>\n </button>\n ))}\n </div>\n\n <div className=\"flex-1 overflow-hidden flex flex-col\">\n <PanelGroup direction=\"vertical\">\n <Panel defaultSize={50} minSize={25}>\n <div className=\"h-full overflow-hidden\" data-tour=\"code-viewer\">\n {renderContent()}\n </div>\n </Panel>\n\n <ResizeHandle theme={theme} />\n\n <Panel defaultSize={50} minSize={25}>\n <div className=\"h-full overflow-hidden\" data-tour=\"console\">\n <PlaygroundConsole\n output={output}\n onClear={onClearConsole}\n theme={theme}\n />\n </div>\n </Panel>\n </PanelGroup>\n </div>\n </div>\n );\n};\n\nexport default PlaygroundViewer;\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/Playground/config/defaultWorkspace.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/Playground/config/playgroundToolbox.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/Playground/config/tourSteps.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/Playground/hooks/useCodeExecution.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/Playground/hooks/useCodeGeneration.js","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'Blockly' is defined but never used. Allowed unused vars must match /^_/u.","line":2,"column":13,"nodeType":"Identifier","messageId":"unusedVar","endLine":2,"endColumn":20,"suggestions":[{"messageId":"removeVar","data":{"varName":"Blockly"},"fix":{"range":[40,58],"text":""},"desc":"Remove unused variable 'Blockly'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { useMemo } from \"react\";\nimport * as Blockly from \"blockly/core\";\nimport { javascriptGenerator } from \"blockly/javascript\";\nimport { pythonGenerator } from \"blockly/python\";\n\nexport const useCodeGeneration = (workspace, version = 0) => {\n return useMemo(() => {\n if (!workspace) {\n return {\n javascript: \"\",\n python: \"\",\n };\n }\n\n try {\n javascriptGenerator.STATEMENT_PREFIX = null;\n javascriptGenerator.STATEMENT_SUFFIX = null;\n javascriptGenerator.INFINITE_LOOP_TRAP = null;\n\n pythonGenerator.STATEMENT_PREFIX = null;\n pythonGenerator.STATEMENT_SUFFIX = null;\n pythonGenerator.INFINITE_LOOP_TRAP = null;\n\n const javascript = javascriptGenerator.workspaceToCode(workspace);\n const python = pythonGenerator.workspaceToCode(workspace);\n\n return {\n javascript,\n python,\n };\n } catch (error) {\n console.error(\"Erro ao gerar código:\", error);\n return {\n javascript: `// Erro ao gerar código: ${error.message}`,\n python: `# Erro ao gerar código: ${error.message}`,\n };\n }\n }, [workspace, version]);\n};\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/Playground/hooks/usePlaygroundTour.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/pages/Playground/services/playgroundStorage.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/services/blockstorage.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/services/codestorage.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/shared/BaseGameScene.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/shared/BaseGameValidator.js","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'history' is defined but never used. Allowed unused args must match /^_/u.","line":35,"column":17,"nodeType":"Identifier","messageId":"unusedVar","endLine":35,"endColumn":24,"suggestions":[{"messageId":"removeVar","data":{"varName":"history"},"fix":{"range":[1234,1242],"text":""},"desc":"Remove unused variable 'history'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'configFase' is defined but never used. Allowed unused args must match /^_/u.","line":35,"column":26,"nodeType":"Identifier","messageId":"unusedVar","endLine":35,"endColumn":36,"suggestions":[{"messageId":"removeVar","data":{"varName":"configFase"},"fix":{"range":[1241,1253],"text":""},"desc":"Remove unused variable 'configFase'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'gameConfig' is defined but never used. Allowed unused args must match /^_/u.","line":35,"column":38,"nodeType":"Identifier","messageId":"unusedVar","endLine":35,"endColumn":48,"suggestions":[{"messageId":"removeVar","data":{"varName":"gameConfig"},"fix":{"range":[1253,1265],"text":""},"desc":"Remove unused variable 'gameConfig'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":3,"fixableErrorCount":0,"fixableWarningCount":0,"source":"export class BaseGameValidator {\n /**\n * Valida a solução do aluno.\n */\n validate(history, configFase, gameConfig) {\n // 1. Fail-Safe Técnico: A config da fase chegou?\n if (!configFase || Object.keys(configFase).length === 0) {\n console.error(\"Validação falhou: Configuração da fase não encontrada.\");\n return this.failure(\"Erro Técnico: Fase não configurada.\");\n }\n\n // 2. Sanity Check: O histórico está vazio? (Genérico para qualquer jogo)\n const sanityCheck = this.checkSanity(history, gameConfig);\n if (!sanityCheck.success) return sanityCheck;\n\n // 3. Validação de Sequência (Opcional, ativado via config)\n // Muitos jogos usam \"siga estes passos exatos\", então vale manter na base.\n if (configFase.expectedSequence) {\n const sequenceCheck = this.checkSequence(\n history,\n configFase.expectedSequence,\n gameConfig,\n );\n if (!sequenceCheck.success) return sequenceCheck;\n }\n\n // 4. Validação Específica (Polimorfismo)\n // Aqui entra a lógica de Negócio do Jogo (Limites, Regras, etc)\n return this.validatePhase(history, configFase, gameConfig);\n }\n\n /**\n * Método abstrato - DEVE ser implementado pelo jogo filho\n */\n validatePhase(history, configFase, gameConfig) {\n console.warn(\"BaseGameValidator: validatePhase não implementado.\");\n return this.success();\n }\n\n // --- Verificações Genéricas ---\n\n checkSanity(history, gameConfig) {\n if (!history || history.length === 0) {\n return this.failure(\n gameConfig?.mensagens?.semMovimento || \"Nenhum movimento detectado.\",\n );\n }\n return this.success();\n }\n\n checkSequence(history, expected, gameConfig) {\n // Compara se o histórico bate com a sequência esperada, propriedade por propriedade\n const match = expected.every((step, i) => {\n const action = history[i];\n if (!action) return false;\n // Verifica se todas as chaves do passo esperado estão presentes e iguais na ação\n return Object.keys(step).every((key) => step[key] === action[key]);\n });\n\n return match\n ? this.success()\n : this.failure(\n gameConfig?.mensagens?.caminhoErrado || \"Sequência incorreta.\",\n );\n }\n\n // --- Helpers de Retorno (Syntax Sugar) ---\n\n success() {\n return { success: true };\n }\n\n failure(reason) {\n return { success: false, reason };\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/shared/__tests__/BaseGameScene.test.js","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'GameInterpreter' is defined but never used. Allowed unused vars must match /^_/u.","line":4,"column":10,"nodeType":"Identifier","messageId":"unusedVar","endLine":4,"endColumn":25,"suggestions":[{"messageId":"removeVar","data":{"varName":"GameInterpreter"},"fix":{"range":[168,237],"text":""},"desc":"Remove unused variable 'GameInterpreter'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'data' is defined but never used. Allowed unused args must match /^_/u.","line":154,"column":32,"nodeType":"Identifier","messageId":"unusedVar","endLine":154,"endColumn":36,"suggestions":[{"messageId":"removeVar","data":{"varName":"data"},"fix":{"range":[3970,3974],"text":""},"desc":"Remove unused variable 'data'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'reason' is defined but never used. Allowed unused args must match /^_/u.","line":445,"column":39,"nodeType":"Identifier","messageId":"unusedVar","endLine":445,"endColumn":45,"suggestions":[{"messageId":"removeVar","data":{"varName":"reason"},"fix":{"range":[11935,11941],"text":""},"desc":"Remove unused variable 'reason'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":3,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { describe, it, expect, beforeEach, vi } from \"vitest\";\nimport { BaseGameScene } from \"../BaseGameScene\";\nimport { gameEventBus } from \"../../utils/gameEvents\";\nimport { GameInterpreter } from \"../../interpreters/GameInterpreter\";\n\n// Mock dos módulos externos\nvi.mock(\"../../utils/gameEvents\", () => ({\n gameEventBus: {\n gameSuccess: vi.fn(),\n gameFailure: vi.fn(),\n },\n}));\n\nvi.mock(\"../../interpreters/GameInterpreter\", () => ({\n GameInterpreter: class {\n constructor(config) {\n this.config = config;\n this.executeCode = vi.fn();\n this.stop = vi.fn();\n }\n },\n}));\n\nvi.mock(\"../gameController\", () => ({\n setupGameController: vi.fn(),\n}));\n\n// Mock do Phaser\nvi.mock(\"phaser\", () => ({\n default: {\n Scene: class {\n constructor(key) {\n this.key = key;\n this.time = {\n delayedCall: vi.fn((delay, callback) => callback()),\n };\n this.sound = {\n get: vi.fn(),\n play: vi.fn(),\n stop: vi.fn(),\n stopByKey: vi.fn(),\n context: {\n state: \"running\",\n resume: vi.fn().mockResolvedValue(),\n },\n };\n this.registry = {\n values: {},\n };\n }\n },\n },\n}));\n\ndescribe(\"BaseGameScene\", () => {\n let scene;\n\n beforeEach(() => {\n vi.clearAllMocks();\n scene = new BaseGameScene(\"test-scene\");\n });\n\n describe(\"Constructor\", () => {\n it(\"deve inicializar com valores padrão\", () => {\n expect(scene.historico).toEqual([]);\n expect(scene.isRunning).toBe(false);\n expect(scene.workspace).toBeNull();\n });\n\n it(\"deve armazenar a chave da cena\", () => {\n expect(scene.key).toBe(\"test-scene\");\n });\n });\n\n describe(\"init()\", () => {\n it(\"deve inicializar com configurações vazias\", () => {\n scene.init({});\n\n expect(scene.configFase).toEqual({});\n expect(scene.gameConfig).toEqual({});\n expect(scene.historico).toEqual([]);\n expect(scene.isRunning).toBe(false);\n });\n\n it(\"deve armazenar configurações fornecidas\", () => {\n const data = {\n configFase: { nivel: 1 },\n gameConfig: { pontos: 100 },\n customFailureHandler: function () {},\n stepDelay: 100,\n };\n\n scene.init(data);\n\n expect(scene.configFase).toEqual({ nivel: 1 });\n expect(scene.gameConfig).toEqual({ pontos: 100 });\n expect(scene.customFailureHandler).toBe(data.customFailureHandler);\n });\n\n it(\"deve usar dados do registry quando não passados diretamente\", () => {\n scene.registry.values = {\n configFase: { nivel: 2 },\n gameConfig: { pontos: 200 },\n stepDelay: 75,\n };\n\n scene.init({});\n\n expect(scene.configFase).toEqual({ nivel: 2 });\n expect(scene.gameConfig).toEqual({ pontos: 200 });\n expect(scene.gameInterpreter.config.stepDelay).toBe(75);\n });\n\n it(\"deve priorizar dados passados direto sobre registry\", () => {\n scene.registry.values = {\n configFase: { nivel: 2 },\n gameConfig: { pontos: 200 },\n stepDelay: 75,\n };\n\n const data = {\n configFase: { nivel: 1 },\n gameConfig: { pontos: 100 },\n stepDelay: 100,\n };\n\n scene.init(data);\n\n expect(scene.configFase).toEqual({ nivel: 1 });\n expect(scene.gameConfig).toEqual({ pontos: 100 });\n expect(scene.gameInterpreter.config.stepDelay).toBe(100);\n });\n\n it(\"deve criar uma instância de GameInterpreter\", () => {\n scene.init({ stepDelay: 200 });\n\n expect(scene.gameInterpreter).toBeDefined();\n expect(scene.gameInterpreter.config).toEqual({\n stepDelay: 200,\n pauseExec: true,\n });\n });\n\n it(\"deve usar stepDelay padrão de 50\", () => {\n scene.init({});\n\n expect(scene.gameInterpreter.config).toEqual({\n stepDelay: 50,\n pauseExec: true,\n });\n });\n\n it(\"deve chamar onInit se definido\", () => {\n scene.onInit = function (data) {};\n scene.onInit = vi.fn(scene.onInit);\n const data = { configFase: {} };\n\n scene.init(data);\n\n expect(scene.onInit).toHaveBeenCalledWith(data);\n });\n });\n\n describe(\"aguardar()\", () => {\n it(\"deve retornar uma Promise\", () => {\n const result = scene.aguardar(1);\n expect(result).toBeInstanceOf(Promise);\n });\n\n it(\"deve chamar time.delayedCall com tempo correto\", async () => {\n scene.aguardar(2);\n\n expect(scene.time.delayedCall).toHaveBeenCalledWith(\n 2000,\n expect.any(Function),\n );\n });\n });\n\n describe(\"highlightBlock()\", () => {\n it(\"deve chamar workspace.highlightBlock quando workspace existe\", () => {\n scene.workspace = {\n highlightBlock: vi.fn(),\n };\n\n scene.highlightBlock(\"block-123\");\n\n expect(scene.workspace.highlightBlock).toHaveBeenCalledWith(\"block-123\");\n });\n\n it(\"não deve lançar erro quando workspace é null\", () => {\n scene.workspace = null;\n\n expect(() => scene.highlightBlock(\"block-123\")).not.toThrow();\n });\n });\n\n describe(\"playAudio()\", () => {\n it(\"deve reproduzir áudio\", () => {\n scene.playAudio(\"som-teste\", { volume: 0.5 });\n\n expect(scene.sound.play).toHaveBeenCalledWith(\"som-teste\", {\n volume: 0.5,\n });\n });\n\n it(\"deve aceitar config vazia\", () => {\n scene.playAudio(\"som-teste\");\n\n expect(scene.sound.play).toHaveBeenCalledWith(\"som-teste\", {});\n });\n\n it(\"não deve lançar erro se falhar\", () => {\n scene.sound.play.mockImplementation(() => {\n throw new Error(\"Audio error\");\n });\n\n expect(() => scene.playAudio(\"som-teste\")).not.toThrow();\n });\n });\n\n describe(\"stopAudio()\", () => {\n it(\"deve chamar stopByKey\", () => {\n scene.stopAudio(\"loop\");\n expect(scene.sound.stopByKey).toHaveBeenCalledWith(\"loop\");\n });\n\n it(\"não deve lançar erro se falhar\", () => {\n scene.sound.stopByKey.mockImplementation(() => {\n throw new Error(\"Stop audio error\");\n });\n\n expect(() => scene.stopAudio(\"ghost\")).not.toThrow();\n });\n });\n\n describe(\"checkRegex()\", () => {\n it(\"deve retornar true quando não há regex configurada\", () => {\n scene.configFase = {};\n\n const result = scene.checkRegex(\"qualquer codigo\");\n\n expect(result).toBe(true);\n });\n\n it(\"deve validar código com regex string\", () => {\n scene.configFase = {\n validationRegex: \"function\\\\s+\\\\w+\",\n };\n\n expect(scene.checkRegex(\"function teste() {}\")).toBe(true);\n expect(scene.checkRegex(\"const x = 5\")).toBe(false);\n });\n\n it(\"deve validar código com objeto RegExp\", () => {\n scene.configFase = {\n validationRegex: /function\\s+\\w+/,\n };\n\n expect(scene.checkRegex(\"function teste() {}\")).toBe(true);\n expect(scene.checkRegex(\"const x = 5\")).toBe(false);\n });\n\n it(\"deve retornar true em caso de regex inválida\", () => {\n scene.configFase = {\n validationRegex: \"[invalid regex(\",\n };\n\n const result = scene.checkRegex(\"codigo qualquer\");\n\n expect(result).toBe(true);\n });\n });\n\n describe(\"handleExecutionStart()\", () => {\n it(\"deve resetar estado de execução\", () => {\n const workspace = { highlightBlock: vi.fn() };\n scene.historico = [\"item1\", \"item2\"];\n scene.isRunning = false;\n\n scene.handleExecutionStart(workspace);\n\n expect(scene.workspace).toBe(workspace);\n expect(scene.historico).toEqual([]);\n expect(scene.isRunning).toBe(true);\n });\n\n it(\"deve limpar highlight\", () => {\n const workspace = { highlightBlock: vi.fn() };\n\n scene.handleExecutionStart(workspace);\n\n expect(workspace.highlightBlock).toHaveBeenCalledWith(null);\n });\n });\n\n describe(\"BaseGameScene (Assets Globais)\", () => {\n let scene;\n\n beforeEach(() => {\n vi.useFakeTimers();\n scene = new BaseGameScene(\"TestScene\");\n\n // Mock do Loader do Phaser\n scene.load = {\n audio: vi.fn(),\n };\n\n // Mock do Sound Manager\n scene.sound = {\n get: vi.fn().mockReturnValue(true),\n play: vi.fn(),\n stop: vi.fn(),\n };\n\n scene.init({});\n });\n\n it(\"preloadGlobalAssets: deve carregar os audios padrão de win/fail\", () => {\n scene.preloadGlobalAssets();\n\n // Verifica se carregou com as chaves reservadas\n expect(scene.load.audio).toHaveBeenCalledWith(\n \"global_win\",\n expect.stringContaining(\"win.mp3\"),\n );\n expect(scene.load.audio).toHaveBeenCalledWith(\n \"global_fail\",\n expect.stringContaining(\"fail.mp3\"),\n );\n });\n });\n\n describe(\"handleValidation()\", () => {\n beforeEach(() => {\n scene.init({});\n scene.workspace = { highlightBlock: vi.fn() };\n scene.isRunning = true;\n });\n\n it(\"não deve validar se não está executando\", () => {\n scene.isRunning = false;\n const validator = function () {};\n const validatorSpy = vi.fn(validator);\n\n scene.handleValidation(validatorSpy);\n\n expect(validatorSpy).not.toHaveBeenCalled();\n });\n\n it(\"deve chamar gameSuccess quando validação passa\", () => {\n scene.onSuccess = function () {};\n scene.onSuccess = vi.fn(scene.onSuccess);\n const validator = function () {\n return { success: true };\n };\n const validatorSpy = vi.fn(validator);\n\n scene.handleValidation(validatorSpy);\n\n expect(validatorSpy).toHaveBeenCalledWith(\n scene.historico,\n scene.configFase,\n scene.gameConfig,\n );\n expect(scene.onSuccess).toHaveBeenCalled();\n expect(gameEventBus.gameSuccess).toHaveBeenCalled();\n expect(scene.isRunning).toBe(false);\n });\n\n it(\"deve chamar handleFailure quando validação falha\", () => {\n scene.handleFailure = function () {};\n scene.handleFailure = vi.fn(scene.handleFailure);\n const validator = function () {\n return {\n success: false,\n reason: \"Teste falhou\",\n };\n };\n const validatorSpy = vi.fn(validator);\n\n scene.handleValidation(validatorSpy);\n\n expect(scene.handleFailure).toHaveBeenCalledWith(\"Teste falhou\");\n });\n\n it(\"deve limpar highlight após validação\", () => {\n const validator = function () {\n return { success: true };\n };\n const validatorSpy = vi.fn(validator);\n\n scene.handleValidation(validatorSpy);\n\n expect(scene.workspace.highlightBlock).toHaveBeenCalledWith(null);\n });\n\n it(\"deve tocar som global_win automaticamente no sucesso\", () => {\n // Setup\n scene.init({});\n scene.isRunning = true;\n scene.playAudio = vi.fn(); // Espião no método playAudio\n\n const validator = () => ({ success: true });\n\n scene.handleValidation(validator);\n\n expect(scene.playAudio).toHaveBeenCalledWith(\"global_win\");\n expect(gameEventBus.gameSuccess).toHaveBeenCalled();\n });\n\n it(\"deve tocar som global_fail automaticamente na falha\", () => {\n // Setup\n scene.init({});\n scene.isRunning = true;\n scene.playAudio = vi.fn(); // Espião no método playAudio\n\n const validator = () => ({ success: false, reason: \"Erro\" });\n\n scene.handleValidation(validator);\n\n expect(scene.playAudio).toHaveBeenCalledWith(\"global_fail\");\n // Pequeno avanço de tempo pois o failure tem delay\n vi.advanceTimersByTime(100);\n expect(gameEventBus.gameFailure).toHaveBeenCalled();\n });\n });\n\n describe(\"handleFailure()\", () => {\n beforeEach(() => {\n scene.init({});\n scene.isRunning = true;\n });\n\n it(\"deve chamar onFailure se definido\", () => {\n scene.onFailure = function () {};\n scene.onFailure = vi.fn(scene.onFailure);\n\n scene.handleFailure(\"Erro teste\");\n\n expect(scene.onFailure).toHaveBeenCalled();\n });\n\n it(\"deve chamar customFailureHandler se definido\", () => {\n const customHandler = function (reason) {};\n scene.init({ customFailureHandler: vi.fn(customHandler) });\n\n scene.handleFailure(\"Erro teste\");\n\n expect(scene.customFailureHandler).toHaveBeenCalledWith(\"Erro teste\");\n });\n\n it(\"deve chamar gameEventBus.gameFailure\", () => {\n scene.handleFailure(\"Erro teste\");\n\n expect(gameEventBus.gameFailure).toHaveBeenCalled();\n });\n\n it(\"deve definir isRunning como false\", () => {\n scene.handleFailure(\"Erro teste\");\n\n expect(scene.isRunning).toBe(false);\n });\n });\n\n describe(\"cleanupExecution()\", () => {\n beforeEach(() => {\n scene.init({});\n scene.workspace = { highlightBlock: vi.fn() };\n scene.historico = [\"item1\", \"item2\"];\n scene.isRunning = true;\n });\n\n it(\"deve parar o interpretador\", () => {\n scene.cleanupExecution();\n\n expect(scene.gameInterpreter.stop).toHaveBeenCalled();\n });\n\n it(\"deve limpar highlight\", () => {\n scene.cleanupExecution();\n\n expect(scene.workspace.highlightBlock).toHaveBeenCalledWith(null);\n });\n\n it(\"deve resetar estado\", () => {\n scene.cleanupExecution();\n\n expect(scene.isRunning).toBe(false);\n expect(scene.historico).toEqual([]);\n });\n });\n\n describe(\"setupStandardController()\", () => {\n let apiFactory;\n let validatorFunc;\n let setupGameController;\n\n beforeEach(async () => {\n const { setupGameController: mockSetup } =\n await import(\"../gameController\");\n setupGameController = mockSetup;\n\n scene.init({\n configFase: { timeout: 5 },\n });\n\n apiFactory = function () {\n return {\n funcao1: vi.fn(),\n funcao2: vi.fn(),\n };\n };\n apiFactory = vi.fn(apiFactory);\n\n validatorFunc = function () {\n return { success: true };\n };\n validatorFunc = vi.fn(validatorFunc);\n\n scene.setupStandardController(apiFactory, validatorFunc);\n });\n\n it(\"deve chamar setupGameController\", () => {\n expect(setupGameController).toHaveBeenCalledWith(\n scene,\n expect.objectContaining({\n onExecuteCode: expect.any(Function),\n onReset: expect.any(Function),\n onCheckSuccess: expect.any(Function),\n }),\n );\n });\n\n it(\"handler de reset deve chamar cleanupExecution\", () => {\n scene.cleanupExecution = function () {};\n scene.cleanupExecution = vi.fn(scene.cleanupExecution);\n\n const callArgs = setupGameController.mock.calls[0][1];\n callArgs.onReset();\n\n expect(scene.cleanupExecution).toHaveBeenCalled();\n });\n\n it(\"handler de reset deve chamar onReset se definido\", () => {\n scene.onReset = function () {};\n scene.onReset = vi.fn(scene.onReset);\n\n const callArgs = setupGameController.mock.calls[0][1];\n callArgs.onReset();\n\n expect(scene.onReset).toHaveBeenCalled();\n });\n });\n\n describe(\"setupStandardController - executeHandler\", () => {\n let apiFactory;\n let validatorFunc;\n let executeHandler;\n\n beforeEach(async () => {\n const { setupGameController } = await import(\"../gameController\");\n\n scene.init({\n configFase: {\n timeout: 5,\n delayValidacao: 500,\n },\n });\n\n apiFactory = function () {\n return {};\n };\n validatorFunc = function () {\n return { success: true };\n };\n\n scene.setupStandardController(apiFactory, validatorFunc);\n\n executeHandler = setupGameController.mock.calls[0][1].onExecuteCode;\n });\n\n it(\"deve executar código sem validação regex\", async () => {\n const codigo = 'console.log(\"teste\")';\n const workspace = { highlightBlock: vi.fn() };\n\n scene.gameInterpreter.executeCode.mockResolvedValue();\n\n await executeHandler(codigo, workspace);\n\n expect(scene.gameInterpreter.executeCode).toHaveBeenCalledWith(\n codigo,\n expect.any(Object),\n );\n });\n\n it(\"deve validar regex antes de executar\", async () => {\n scene.configFase.validationRegex = /function/;\n scene.handleFailure = function () {};\n scene.handleFailure = vi.fn(scene.handleFailure);\n const workspace = { highlightBlock: vi.fn() };\n\n await executeHandler(\"const x = 5\", workspace);\n\n expect(scene.handleFailure).toHaveBeenCalledWith(\n expect.stringContaining(\"estrutural\"),\n );\n expect(scene.gameInterpreter.executeCode).not.toHaveBeenCalled();\n });\n\n it(\"deve chamar onBeforeRun se definido\", async () => {\n scene.onBeforeRun = function () {\n return Promise.resolve();\n };\n scene.onBeforeRun = vi.fn(scene.onBeforeRun);\n scene.gameInterpreter.executeCode.mockResolvedValue();\n const workspace = { highlightBlock: vi.fn() };\n\n await executeHandler(\"codigo\", workspace);\n\n expect(scene.onBeforeRun).toHaveBeenCalled();\n });\n\n it(\"deve tratar timeout de execução\", async () => {\n scene.configFase.timeout = 0.001; // 1ms\n scene.handleFailure = function () {};\n scene.handleFailure = vi.fn(scene.handleFailure);\n scene.gameInterpreter.executeCode.mockImplementation(\n () => new Promise((resolve) => setTimeout(resolve, 1000)),\n );\n const workspace = { highlightBlock: vi.fn() };\n\n await executeHandler(\"codigo\", workspace);\n\n expect(scene.handleFailure).toHaveBeenCalledWith(\n expect.stringContaining(\"Tempo limite\"),\n );\n });\n\n it(\"deve usar timeout padrão de 15 segundos\", async () => {\n delete scene.configFase.timeout;\n scene.gameInterpreter.executeCode.mockResolvedValue();\n const workspace = { highlightBlock: vi.fn() };\n\n await executeHandler(\"codigo\", workspace);\n\n // Verifica que não deu timeout imediato\n expect(scene.gameInterpreter.executeCode).toHaveBeenCalled();\n });\n\n it(\"deve tratar erros inesperados\", async () => {\n scene.handleFailure = function () {};\n scene.handleFailure = vi.fn(scene.handleFailure);\n scene.gameInterpreter.executeCode.mockRejectedValue(\n new Error(\"Erro customizado\"),\n );\n const workspace = { highlightBlock: vi.fn() };\n\n await executeHandler(\"codigo\", workspace);\n\n expect(scene.handleFailure).toHaveBeenCalledWith(\n expect.stringContaining(\"inesperado\"),\n );\n });\n\n it(\"deve aguardar delay antes de validar\", async () => {\n scene.configFase.delayValidacao = 1000;\n scene.gameInterpreter.executeCode.mockResolvedValue();\n const workspace = { highlightBlock: vi.fn() };\n\n await executeHandler(\"codigo\", workspace);\n\n expect(scene.time.delayedCall).toHaveBeenCalledWith(\n 1000,\n expect.any(Function),\n );\n });\n\n it(\"deve usar delay padrão de 1000ms\", async () => {\n delete scene.configFase.delayValidacao;\n scene.gameInterpreter.executeCode.mockResolvedValue();\n const workspace = { highlightBlock: vi.fn() };\n\n await executeHandler(\"codigo\", workspace);\n\n expect(scene.time.delayedCall).toHaveBeenCalledWith(\n 1000,\n expect.any(Function),\n );\n });\n });\n});\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/shared/__tests__/BaseGameValidator.test.js","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'history' is defined but never used. Allowed unused args must match /^_/u.","line":6,"column":17,"nodeType":"Identifier","messageId":"unusedVar","endLine":6,"endColumn":24,"suggestions":[{"messageId":"removeVar","data":{"varName":"history"},"fix":{"range":[255,263],"text":""},"desc":"Remove unused variable 'history'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'config' is defined but never used. Allowed unused args must match /^_/u.","line":6,"column":26,"nodeType":"Identifier","messageId":"unusedVar","endLine":6,"endColumn":32,"suggestions":[{"messageId":"removeVar","data":{"varName":"config"},"fix":{"range":[262,270],"text":""},"desc":"Remove unused variable 'config'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'gameConfig' is defined but never used. Allowed unused args must match /^_/u.","line":6,"column":34,"nodeType":"Identifier","messageId":"unusedVar","endLine":6,"endColumn":44,"suggestions":[{"messageId":"removeVar","data":{"varName":"gameConfig"},"fix":{"range":[270,282],"text":""},"desc":"Remove unused variable 'gameConfig'."}]}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":3,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { describe, it, expect, vi, beforeEach } from \"vitest\";\nimport { BaseGameValidator } from \"../BaseGameValidator\";\n\n// Subclasse Fake apenas para testar a Base sem depender do MoleMash\nclass TestValidator extends BaseGameValidator {\n validatePhase(history, config, gameConfig) {\n // Simula uma validação específica que sempre passa se chegar aqui\n return this.success();\n }\n}\n\ndescribe(\"BaseGameValidator\", () => {\n let validator;\n const mockGameConfig = {\n mensagens: {\n semMovimento: \"Nada feito\",\n caminhoErrado: \"Caminho ruim\",\n foraTabuleiro: \"Saiu da borda\",\n },\n };\n\n beforeEach(() => {\n validator = new TestValidator();\n });\n\n describe(\"1. Fail-Safe (Configuração)\", () => {\n it(\"deve falhar se a config da fase for undefined\", () => {\n const result = validator.validate([], undefined, mockGameConfig);\n expect(result.success).toBe(false);\n expect(result.reason).toContain(\"Erro Técnico\");\n });\n\n it(\"deve falhar se a config da fase for vazia\", () => {\n const result = validator.validate([], {}, mockGameConfig);\n expect(result.success).toBe(false);\n expect(result.reason).toContain(\"Erro Técnico\");\n });\n });\n\n describe(\"2. Sanity Check (Histórico Vazio)\", () => {\n it(\"deve falhar se o histórico for vazio\", () => {\n const result = validator.validate([], { id: 1 }, mockGameConfig);\n expect(result.success).toBe(false);\n expect(result.reason).toBe(\"Nada feito\");\n });\n\n it(\"deve falhar se o histórico for null\", () => {\n const result = validator.validate(null, { id: 1 }, mockGameConfig);\n expect(result.success).toBe(false);\n });\n });\n\n describe(\"3. Validação de Sequência (ExpectedSequence)\", () => {\n const configSequencia = {\n id: 1,\n expectedSequence: [\n { l: 1, c: 1 },\n { l: 1, c: 2 },\n ],\n };\n\n it(\"deve passar se o histórico bater com a sequência\", () => {\n const history = [\n { l: 1, c: 1, t: 100 }, // t (tempo) deve ser ignorado na comparação\n { l: 1, c: 2, t: 200 },\n ];\n const result = validator.validate(\n history,\n configSequencia,\n mockGameConfig,\n );\n expect(result.success).toBe(true);\n });\n\n it(\"deve falhar se um passo for diferente\", () => {\n const history = [\n { l: 1, c: 1 },\n { l: 1, c: 3 }, // Errado\n ];\n const result = validator.validate(\n history,\n configSequencia,\n mockGameConfig,\n );\n expect(result.success).toBe(false);\n expect(result.reason).toBe(\"Caminho ruim\");\n });\n\n it(\"deve falhar se o tamanho for menor que a sequência\", () => {\n const history = [{ l: 1, c: 1 }];\n const result = validator.validate(\n history,\n configSequencia,\n mockGameConfig,\n );\n expect(result.success).toBe(false);\n });\n });\n\n describe(\"4. Delegação (Template Method)\", () => {\n it(\"deve chamar validatePhase se passar nas validações básicas\", () => {\n // Espiona o método da subclasse\n const spy = vi.spyOn(validator, \"validatePhase\");\n\n const history = [{ x: 1 }];\n const config = { id: 99 }; // Sem sequence, deve ir pro validatePhase\n\n const result = validator.validate(history, config, mockGameConfig);\n\n expect(result.success).toBe(true);\n expect(spy).toHaveBeenCalledWith(history, config, mockGameConfig);\n });\n });\n});\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/shared/gameController.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/utils/blocklyConfigureMobileToolbox.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/utils/blocklySetup.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/utils/blocklyValidation.js","messages":[{"ruleId":"no-unused-vars","severity":1,"message":"'index' is defined but never used. Allowed unused args must match /^_/u.","line":84,"column":31,"nodeType":"Identifier","messageId":"unusedVar","endLine":84,"endColumn":36,"suggestions":[{"messageId":"removeVar","data":{"varName":"index"},"fix":{"range":[2256,2263],"text":""},"desc":"Remove unused variable 'index'."}]},{"ruleId":"no-unused-vars","severity":1,"message":"'error' is defined but never used.","line":104,"column":20,"nodeType":"Identifier","messageId":"unusedVar","endLine":104,"endColumn":25},{"ruleId":"no-unused-vars","severity":1,"message":"'error' is defined but never used.","line":108,"column":16,"nodeType":"Identifier","messageId":"unusedVar","endLine":108,"endColumn":21}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":3,"fixableErrorCount":0,"fixableWarningCount":0,"source":"export class BlocklyWorkspaceValidator {\n constructor(options = {}) {\n this.options = {\n allowMultipleTopBlocks: false,\n preferredStartBlocks: [\"start\", \"when_run\", \"main\"],\n ...options,\n };\n }\n\n /**\n * Valida e limpa a workspace antes da execução\n * @param {Blockly.Workspace} workspace - A workspace do Blockly\n * @returns {Object} Resultado da validação\n */\n validateAndClean(workspace) {\n if (!workspace) {\n return { isValid: false, warnings: [\"Workspace não disponível\"] };\n }\n\n const topBlocks = workspace.getTopBlocks(false);\n const warnings = [];\n\n if (topBlocks.length === 0) {\n return {\n isValid: false,\n warnings: [\"Nenhum bloco encontrado na workspace\"],\n };\n }\n\n if (topBlocks.length === 1) {\n return {\n isValid: true,\n warnings: [],\n mainBlock: topBlocks[0],\n reEnableBlocks: null, // Não há blocos para reabilitar\n };\n }\n\n warnings.push(\n `Encontrados ${topBlocks.length} programas separados. Apenas um será executado.`,\n );\n\n if (!this.options.allowMultipleTopBlocks) {\n const result = this.handleMultipleTopBlocks(topBlocks, warnings);\n return result;\n }\n\n return {\n isValid: true,\n warnings,\n mainBlock: topBlocks[0],\n reEnableBlocks: null,\n };\n }\n\n /**\n * Verifica se um bloco tem o método setDisabledReason\n * @param {Object} block - Bloco do Blockly\n * @returns {boolean} Se o bloco é válido para controle\n */\n isBlockControllable(block) {\n return (\n block && typeof block.setDisabledReason === \"function\" && !block.disposed\n );\n }\n\n /**\n * Lida com múltiplos blocos top-level\n * @param {Array} topBlocks - Array de blocos top-level\n * @param {Array} warnings - Array de warnings acumulados\n * @returns {Object} Resultado do processamento\n */\n handleMultipleTopBlocks(topBlocks, warnings) {\n const mainBlock = this.selectMainBlock(topBlocks);\n\n if (mainBlock) {\n warnings.push(`Programa principal selecionado: \"${mainBlock.type}\"`);\n }\n\n // Desabilitar todos os outros blocos top-level (diferentes do mainBlock)\n const disabledBlocks = [];\n const reEnableCallbacks = [];\n\n topBlocks.forEach((block, index) => {\n // Pular o bloco principal\n if (block === mainBlock) {\n return;\n }\n\n if (!this.isBlockControllable(block)) {\n return;\n }\n\n try {\n block.setDisabledReason(true, \"MANUALLY_DISABLED\");\n disabledBlocks.push(block);\n warnings.push(`Programa \"${block.type}\" desabilitado temporariamente`);\n\n reEnableCallbacks.push(() => {\n try {\n if (this.isBlockControllable(block)) {\n block.setDisabledReason(false);\n }\n } catch (error) {\n // Falhou ao reabilitar, mas não precisamos fazer nada\n }\n });\n } catch (error) {\n // Falhou ao desabilitar, mas não precisamos fazer nada\n }\n });\n\n const reEnableAllBlocks = () => {\n if (reEnableCallbacks.length > 0) {\n reEnableCallbacks.forEach((callback) => callback());\n }\n };\n\n return {\n isValid: true,\n warnings,\n mainBlock,\n disabledBlocks,\n disabledCount: disabledBlocks.length,\n reEnableBlocks: disabledBlocks.length > 0 ? reEnableAllBlocks : null,\n };\n }\n\n /**\n * Seleciona o bloco principal entre múltiplos blocos top-level\n * @param {Array} topBlocks - Array de blocos top-level\n * @returns {Object} Bloco principal selecionado\n */\n selectMainBlock(topBlocks) {\n const validBlocks = topBlocks.filter((block) => block && !block.disposed);\n\n if (validBlocks.length === 0) {\n return null;\n }\n\n for (const preferredType of this.options.preferredStartBlocks) {\n const startBlock = validBlocks.find(\n (block) =>\n block.type === preferredType || block.type.includes(preferredType),\n );\n if (startBlock) {\n return startBlock;\n }\n }\n\n return validBlocks[0];\n }\n}\n\nexport const defaultValidator = new BlocklyWorkspaceValidator({\n allowMultipleTopBlocks: false,\n preferredStartBlocks: [\"start\", \"when_run\", \"main\", \"begin\"],\n});\n\n/**\n * Função de conveniência para validação rápida\n * @param {Blockly.Workspace} workspace - A workspace do Blockly\n * @param {Object} options - Opções de validação\n * @returns {Object} Resultado da validação\n */\nexport function validateBlocklyWorkspace(workspace, options = {}) {\n const validator = new BlocklyWorkspaceValidator(options);\n return validator.validateAndClean(workspace);\n}\n","usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/utils/drawAxes.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/utils/fieldAngleLoader.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/utils/gameEvents.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/utils/phaserResize.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/src/utils/tourHelpers.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/tailwind.config.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/rui/src/new/plataforma-edu/app/vite.config.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]}]
|