hoi4-agent-tools 0.0.0-bootstrap.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/LICENSE +201 -0
- package/README.md +140 -2
- package/SECURITY.md +17 -0
- package/dist/bin/http.d.ts +3 -0
- package/dist/bin/http.d.ts.map +1 -0
- package/dist/bin/http.js +27 -0
- package/dist/bin/http.js.map +1 -0
- package/dist/bin/setup.d.ts +3 -0
- package/dist/bin/setup.d.ts.map +1 -0
- package/dist/bin/setup.js +188 -0
- package/dist/bin/setup.js.map +1 -0
- package/dist/bin/stdio.d.ts +3 -0
- package/dist/bin/stdio.d.ts.map +1 -0
- package/dist/bin/stdio.js +36 -0
- package/dist/bin/stdio.js.map +1 -0
- package/dist/hoi4_agent_tools/core/artifacts.d.ts +117 -0
- package/dist/hoi4_agent_tools/core/artifacts.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/artifacts.js +1047 -0
- package/dist/hoi4_agent_tools/core/artifacts.js.map +1 -0
- package/dist/hoi4_agent_tools/core/canonical.d.ts +13 -0
- package/dist/hoi4_agent_tools/core/canonical.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/canonical.js +41 -0
- package/dist/hoi4_agent_tools/core/canonical.js.map +1 -0
- package/dist/hoi4_agent_tools/core/configuration.d.ts +137 -0
- package/dist/hoi4_agent_tools/core/configuration.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/configuration.js +383 -0
- package/dist/hoi4_agent_tools/core/configuration.js.map +1 -0
- package/dist/hoi4_agent_tools/core/diagnostic-collector.d.ts +18 -0
- package/dist/hoi4_agent_tools/core/diagnostic-collector.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/diagnostic-collector.js +45 -0
- package/dist/hoi4_agent_tools/core/diagnostic-collector.js.map +1 -0
- package/dist/hoi4_agent_tools/core/diagnostics.d.ts +26 -0
- package/dist/hoi4_agent_tools/core/diagnostics.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/diagnostics.js +25 -0
- package/dist/hoi4_agent_tools/core/diagnostics.js.map +1 -0
- package/dist/hoi4_agent_tools/core/diff.d.ts +22 -0
- package/dist/hoi4_agent_tools/core/diff.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/diff.js +93 -0
- package/dist/hoi4_agent_tools/core/diff.js.map +1 -0
- package/dist/hoi4_agent_tools/core/engine.d.ts +56 -0
- package/dist/hoi4_agent_tools/core/engine.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/engine.js +319 -0
- package/dist/hoi4_agent_tools/core/engine.js.map +1 -0
- package/dist/hoi4_agent_tools/core/image-diff.d.ts +14 -0
- package/dist/hoi4_agent_tools/core/image-diff.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/image-diff.js +121 -0
- package/dist/hoi4_agent_tools/core/image-diff.js.map +1 -0
- package/dist/hoi4_agent_tools/core/index.d.ts +64 -0
- package/dist/hoi4_agent_tools/core/index.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/index.js +963 -0
- package/dist/hoi4_agent_tools/core/index.js.map +1 -0
- package/dist/hoi4_agent_tools/core/index.public.d.ts +16 -0
- package/dist/hoi4_agent_tools/core/index.public.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/index.public.js +16 -0
- package/dist/hoi4_agent_tools/core/index.public.js.map +1 -0
- package/dist/hoi4_agent_tools/core/render-budget.d.ts +51 -0
- package/dist/hoi4_agent_tools/core/render-budget.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/render-budget.js +109 -0
- package/dist/hoi4_agent_tools/core/render-budget.js.map +1 -0
- package/dist/hoi4_agent_tools/core/result.d.ts +45 -0
- package/dist/hoi4_agent_tools/core/result.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/result.js +26 -0
- package/dist/hoi4_agent_tools/core/result.js.map +1 -0
- package/dist/hoi4_agent_tools/core/scanner.d.ts +28 -0
- package/dist/hoi4_agent_tools/core/scanner.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/scanner.js +136 -0
- package/dist/hoi4_agent_tools/core/scanner.js.map +1 -0
- package/dist/hoi4_agent_tools/core/server-state.d.ts +31 -0
- package/dist/hoi4_agent_tools/core/server-state.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/server-state.js +413 -0
- package/dist/hoi4_agent_tools/core/server-state.js.map +1 -0
- package/dist/hoi4_agent_tools/core/source/encoding.d.ts +10 -0
- package/dist/hoi4_agent_tools/core/source/encoding.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/source/encoding.js +54 -0
- package/dist/hoi4_agent_tools/core/source/encoding.js.map +1 -0
- package/dist/hoi4_agent_tools/core/source/index.d.ts +7 -0
- package/dist/hoi4_agent_tools/core/source/index.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/source/index.js +7 -0
- package/dist/hoi4_agent_tools/core/source/index.js.map +1 -0
- package/dist/hoi4_agent_tools/core/source/lexer.d.ts +30 -0
- package/dist/hoi4_agent_tools/core/source/lexer.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/source/lexer.js +266 -0
- package/dist/hoi4_agent_tools/core/source/lexer.js.map +1 -0
- package/dist/hoi4_agent_tools/core/source/limits.d.ts +21 -0
- package/dist/hoi4_agent_tools/core/source/limits.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/source/limits.js +49 -0
- package/dist/hoi4_agent_tools/core/source/limits.js.map +1 -0
- package/dist/hoi4_agent_tools/core/source/localisation.d.ts +20 -0
- package/dist/hoi4_agent_tools/core/source/localisation.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/source/localisation.js +163 -0
- package/dist/hoi4_agent_tools/core/source/localisation.js.map +1 -0
- package/dist/hoi4_agent_tools/core/source/parser.d.ts +44 -0
- package/dist/hoi4_agent_tools/core/source/parser.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/source/parser.js +334 -0
- package/dist/hoi4_agent_tools/core/source/parser.js.map +1 -0
- package/dist/hoi4_agent_tools/core/source/rewrite.d.ts +10 -0
- package/dist/hoi4_agent_tools/core/source/rewrite.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/source/rewrite.js +28 -0
- package/dist/hoi4_agent_tools/core/source/rewrite.js.map +1 -0
- package/dist/hoi4_agent_tools/core/svg-text.d.ts +34 -0
- package/dist/hoi4_agent_tools/core/svg-text.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/svg-text.js +127 -0
- package/dist/hoi4_agent_tools/core/svg-text.js.map +1 -0
- package/dist/hoi4_agent_tools/core/transaction-limits.d.ts +9 -0
- package/dist/hoi4_agent_tools/core/transaction-limits.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/transaction-limits.js +9 -0
- package/dist/hoi4_agent_tools/core/transaction-limits.js.map +1 -0
- package/dist/hoi4_agent_tools/core/transactions.d.ts +162 -0
- package/dist/hoi4_agent_tools/core/transactions.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/transactions.js +1181 -0
- package/dist/hoi4_agent_tools/core/transactions.js.map +1 -0
- package/dist/hoi4_agent_tools/core/workspace.d.ts +68 -0
- package/dist/hoi4_agent_tools/core/workspace.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/workspace.js +794 -0
- package/dist/hoi4_agent_tools/core/workspace.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/compiler.d.ts +9 -0
- package/dist/hoi4_agent_tools/focus/compiler.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/compiler.js +273 -0
- package/dist/hoi4_agent_tools/focus/compiler.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/continuous-render.d.ts +32 -0
- package/dist/hoi4_agent_tools/focus/continuous-render.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/continuous-render.js +179 -0
- package/dist/hoi4_agent_tools/focus/continuous-render.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/drift.d.ts +22 -0
- package/dist/hoi4_agent_tools/focus/drift.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/drift.js +124 -0
- package/dist/hoi4_agent_tools/focus/drift.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/geometry.d.ts +17 -0
- package/dist/hoi4_agent_tools/focus/geometry.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/geometry.js +35 -0
- package/dist/hoi4_agent_tools/focus/geometry.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/importer.d.ts +9 -0
- package/dist/hoi4_agent_tools/focus/importer.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/importer.js +732 -0
- package/dist/hoi4_agent_tools/focus/importer.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/index.d.ts +14 -0
- package/dist/hoi4_agent_tools/focus/index.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/index.js +14 -0
- package/dist/hoi4_agent_tools/focus/index.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/layout.d.ts +5 -0
- package/dist/hoi4_agent_tools/focus/layout.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/layout.js +653 -0
- package/dist/hoi4_agent_tools/focus/layout.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/limits.d.ts +9 -0
- package/dist/hoi4_agent_tools/focus/limits.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/limits.js +9 -0
- package/dist/hoi4_agent_tools/focus/limits.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/lint.d.ts +14 -0
- package/dist/hoi4_agent_tools/focus/lint.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/lint.js +839 -0
- package/dist/hoi4_agent_tools/focus/lint.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/model.d.ts +303 -0
- package/dist/hoi4_agent_tools/focus/model.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/model.js +20 -0
- package/dist/hoi4_agent_tools/focus/model.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/native-effects.d.ts +9 -0
- package/dist/hoi4_agent_tools/focus/native-effects.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/native-effects.js +563 -0
- package/dist/hoi4_agent_tools/focus/native-effects.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/planning.d.ts +15 -0
- package/dist/hoi4_agent_tools/focus/planning.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/planning.js +241 -0
- package/dist/hoi4_agent_tools/focus/planning.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/presentation.d.ts +24 -0
- package/dist/hoi4_agent_tools/focus/presentation.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/presentation.js +204 -0
- package/dist/hoi4_agent_tools/focus/presentation.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/render.d.ts +35 -0
- package/dist/hoi4_agent_tools/focus/render.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/render.js +491 -0
- package/dist/hoi4_agent_tools/focus/render.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/service.d.ts +82 -0
- package/dist/hoi4_agent_tools/focus/service.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/service.js +492 -0
- package/dist/hoi4_agent_tools/focus/service.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/source-update.d.ts +6 -0
- package/dist/hoi4_agent_tools/focus/source-update.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/source-update.js +328 -0
- package/dist/hoi4_agent_tools/focus/source-update.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/animation-manifest.d.ts +37 -0
- package/dist/hoi4_agent_tools/gui/animation-manifest.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/animation-manifest.js +66 -0
- package/dist/hoi4_agent_tools/gui/animation-manifest.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/assets.d.ts +71 -0
- package/dist/hoi4_agent_tools/gui/assets.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/assets.js +629 -0
- package/dist/hoi4_agent_tools/gui/assets.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/dds.d.ts +17 -0
- package/dist/hoi4_agent_tools/gui/dds.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/dds.js +245 -0
- package/dist/hoi4_agent_tools/gui/dds.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/helpers.d.ts +101 -0
- package/dist/hoi4_agent_tools/gui/helpers.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/helpers.js +1072 -0
- package/dist/hoi4_agent_tools/gui/helpers.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/index.d.ts +15 -0
- package/dist/hoi4_agent_tools/gui/index.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/index.js +15 -0
- package/dist/hoi4_agent_tools/gui/index.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/layout.d.ts +6 -0
- package/dist/hoi4_agent_tools/gui/layout.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/layout.js +753 -0
- package/dist/hoi4_agent_tools/gui/layout.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/limits.d.ts +25 -0
- package/dist/hoi4_agent_tools/gui/limits.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/limits.js +27 -0
- package/dist/hoi4_agent_tools/gui/limits.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/renderer.d.ts +18 -0
- package/dist/hoi4_agent_tools/gui/renderer.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/renderer.js +388 -0
- package/dist/hoi4_agent_tools/gui/renderer.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/scenario.d.ts +66 -0
- package/dist/hoi4_agent_tools/gui/scenario.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/scenario.js +191 -0
- package/dist/hoi4_agent_tools/gui/scenario.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/source-graph.d.ts +7 -0
- package/dist/hoi4_agent_tools/gui/source-graph.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/source-graph.js +1074 -0
- package/dist/hoi4_agent_tools/gui/source-graph.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/source-patch.d.ts +13 -0
- package/dist/hoi4_agent_tools/gui/source-patch.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/source-patch.js +90 -0
- package/dist/hoi4_agent_tools/gui/source-patch.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/studio.d.ts +86 -0
- package/dist/hoi4_agent_tools/gui/studio.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/studio.js +830 -0
- package/dist/hoi4_agent_tools/gui/studio.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/tga.d.ts +16 -0
- package/dist/hoi4_agent_tools/gui/tga.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/tga.js +157 -0
- package/dist/hoi4_agent_tools/gui/tga.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/types.d.ts +322 -0
- package/dist/hoi4_agent_tools/gui/types.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/types.js +19 -0
- package/dist/hoi4_agent_tools/gui/types.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/validators.d.ts +8 -0
- package/dist/hoi4_agent_tools/gui/validators.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/validators.js +951 -0
- package/dist/hoi4_agent_tools/gui/validators.js.map +1 -0
- package/dist/hoi4_agent_tools/map/bmp.d.ts +62 -0
- package/dist/hoi4_agent_tools/map/bmp.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/map/bmp.js +363 -0
- package/dist/hoi4_agent_tools/map/bmp.js.map +1 -0
- package/dist/hoi4_agent_tools/map/diagnostic-limit.d.ts +5 -0
- package/dist/hoi4_agent_tools/map/diagnostic-limit.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/map/diagnostic-limit.js +21 -0
- package/dist/hoi4_agent_tools/map/diagnostic-limit.js.map +1 -0
- package/dist/hoi4_agent_tools/map/index.d.ts +8 -0
- package/dist/hoi4_agent_tools/map/index.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/map/index.js +8 -0
- package/dist/hoi4_agent_tools/map/index.js.map +1 -0
- package/dist/hoi4_agent_tools/map/limits.d.ts +13 -0
- package/dist/hoi4_agent_tools/map/limits.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/map/limits.js +13 -0
- package/dist/hoi4_agent_tools/map/limits.js.map +1 -0
- package/dist/hoi4_agent_tools/map/model.d.ts +269 -0
- package/dist/hoi4_agent_tools/map/model.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/map/model.js +1114 -0
- package/dist/hoi4_agent_tools/map/model.js.map +1 -0
- package/dist/hoi4_agent_tools/map/operations.d.ts +401 -0
- package/dist/hoi4_agent_tools/map/operations.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/map/operations.js +2717 -0
- package/dist/hoi4_agent_tools/map/operations.js.map +1 -0
- package/dist/hoi4_agent_tools/map/render.d.ts +90 -0
- package/dist/hoi4_agent_tools/map/render.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/map/render.js +759 -0
- package/dist/hoi4_agent_tools/map/render.js.map +1 -0
- package/dist/hoi4_agent_tools/map/service.d.ts +81 -0
- package/dist/hoi4_agent_tools/map/service.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/map/service.js +695 -0
- package/dist/hoi4_agent_tools/map/service.js.map +1 -0
- package/dist/hoi4_agent_tools/map/validation.d.ts +23 -0
- package/dist/hoi4_agent_tools/map/validation.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/map/validation.js +1177 -0
- package/dist/hoi4_agent_tools/map/validation.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/prompts/register.d.ts +3 -0
- package/dist/hoi4_agent_tools/mcp/prompts/register.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/prompts/register.js +50 -0
- package/dist/hoi4_agent_tools/mcp/prompts/register.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/registry/metadata.d.ts +9 -0
- package/dist/hoi4_agent_tools/mcp/registry/metadata.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/registry/metadata.js +10 -0
- package/dist/hoi4_agent_tools/mcp/registry/metadata.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/resources/register.d.ts +5 -0
- package/dist/hoi4_agent_tools/mcp/resources/register.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/resources/register.js +278 -0
- package/dist/hoi4_agent_tools/mcp/resources/register.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/security/auth.d.ts +24 -0
- package/dist/hoi4_agent_tools/mcp/security/auth.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/security/auth.js +234 -0
- package/dist/hoi4_agent_tools/mcp/security/auth.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/server/base-tools.d.ts +35 -0
- package/dist/hoi4_agent_tools/mcp/server/base-tools.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/server/base-tools.js +835 -0
- package/dist/hoi4_agent_tools/mcp/server/base-tools.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/server/create.d.ts +5 -0
- package/dist/hoi4_agent_tools/mcp/server/create.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/server/create.js +26 -0
- package/dist/hoi4_agent_tools/mcp/server/create.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/server/output-schemas.d.ts +133 -0
- package/dist/hoi4_agent_tools/mcp/server/output-schemas.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/server/output-schemas.js +127 -0
- package/dist/hoi4_agent_tools/mcp/server/output-schemas.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/server/progress.d.ts +8 -0
- package/dist/hoi4_agent_tools/mcp/server/progress.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/server/progress.js +21 -0
- package/dist/hoi4_agent_tools/mcp/server/progress.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/server/result.d.ts +232 -0
- package/dist/hoi4_agent_tools/mcp/server/result.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/server/result.js +353 -0
- package/dist/hoi4_agent_tools/mcp/server/result.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/tools/focus.d.ts +5 -0
- package/dist/hoi4_agent_tools/mcp/tools/focus.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/tools/focus.js +1059 -0
- package/dist/hoi4_agent_tools/mcp/tools/focus.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/tools/gui.d.ts +111 -0
- package/dist/hoi4_agent_tools/mcp/tools/gui.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/tools/gui.js +598 -0
- package/dist/hoi4_agent_tools/mcp/tools/gui.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/tools/map.d.ts +5 -0
- package/dist/hoi4_agent_tools/mcp/tools/map.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/tools/map.js +685 -0
- package/dist/hoi4_agent_tools/mcp/tools/map.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/transports/bounded-stdio.d.ts +58 -0
- package/dist/hoi4_agent_tools/mcp/transports/bounded-stdio.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/transports/bounded-stdio.js +181 -0
- package/dist/hoi4_agent_tools/mcp/transports/bounded-stdio.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/transports/event-store.d.ts +26 -0
- package/dist/hoi4_agent_tools/mcp/transports/event-store.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/transports/event-store.js +86 -0
- package/dist/hoi4_agent_tools/mcp/transports/event-store.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/transports/http.d.ts +28 -0
- package/dist/hoi4_agent_tools/mcp/transports/http.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/transports/http.js +744 -0
- package/dist/hoi4_agent_tools/mcp/transports/http.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/transports/protocol-gate.d.ts +15 -0
- package/dist/hoi4_agent_tools/mcp/transports/protocol-gate.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/transports/protocol-gate.js +51 -0
- package/dist/hoi4_agent_tools/mcp/transports/protocol-gate.js.map +1 -0
- package/dist/hoi4_agent_tools/runtime.d.ts +4 -0
- package/dist/hoi4_agent_tools/runtime.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/runtime.js +27 -0
- package/dist/hoi4_agent_tools/runtime.js.map +1 -0
- package/dist/hoi4_agent_tools/schemas/common.d.ts +74 -0
- package/dist/hoi4_agent_tools/schemas/common.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/schemas/common.js +51 -0
- package/dist/hoi4_agent_tools/schemas/common.js.map +1 -0
- package/dist/hoi4_agent_tools/schemas/focus.d.ts +784 -0
- package/dist/hoi4_agent_tools/schemas/focus.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/schemas/focus.js +330 -0
- package/dist/hoi4_agent_tools/schemas/focus.js.map +1 -0
- package/dist/hoi4_agent_tools/schemas/index.d.ts +5 -0
- package/dist/hoi4_agent_tools/schemas/index.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/schemas/index.js +5 -0
- package/dist/hoi4_agent_tools/schemas/index.js.map +1 -0
- package/dist/hoi4_agent_tools/schemas/map.d.ts +654 -0
- package/dist/hoi4_agent_tools/schemas/map.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/schemas/map.js +570 -0
- package/dist/hoi4_agent_tools/schemas/map.js.map +1 -0
- package/dist/hoi4_agent_tools/schemas/transaction.d.ts +259 -0
- package/dist/hoi4_agent_tools/schemas/transaction.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/schemas/transaction.js +104 -0
- package/dist/hoi4_agent_tools/schemas/transaction.js.map +1 -0
- package/dist/hoi4_agent_tools/version.d.ts +6 -0
- package/dist/hoi4_agent_tools/version.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/version.js +7 -0
- package/dist/hoi4_agent_tools/version.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/docs/README.md +22 -0
- package/docs/adr/0001-runtime-and-language.md +16 -0
- package/docs/adr/0002-lossless-source-model.md +18 -0
- package/docs/adr/0003-image-and-font-codecs.md +18 -0
- package/docs/adr/0004-focus-layout.md +16 -0
- package/docs/adr/0005-offline-rendering.md +18 -0
- package/docs/adr/0006-mcp-sdk-and-protocol.md +16 -0
- package/docs/adr/0007-storage-and-transactions.md +24 -0
- package/docs/adr/0008-test-strategy.md +18 -0
- package/docs/adr/0009-packaging-and-registry.md +16 -0
- package/docs/adr/0010-unsupported-and-refusal-policy.md +18 -0
- package/docs/adr/0011-http-security.md +16 -0
- package/docs/adr/README.md +13 -0
- package/docs/architecture.md +60 -0
- package/docs/artifacts.md +133 -0
- package/docs/compatibility.md +33 -0
- package/docs/completion-report.md +60 -0
- package/docs/configuration.md +285 -0
- package/docs/focus-workflow.md +68 -0
- package/docs/gui-workflow.md +121 -0
- package/docs/limitations.md +30 -0
- package/docs/map-workflow.md +89 -0
- package/docs/performance.md +117 -0
- package/docs/release.md +181 -0
- package/docs/research/mcp-2026-07-10.md +46 -0
- package/docs/research/source-ledger.md +68 -0
- package/docs/security.md +139 -0
- package/docs/self-hosting.md +168 -0
- package/docs/testing.md +43 -0
- package/docs/third-party-notices.md +11 -0
- package/docs/transactions.md +162 -0
- package/docs/troubleshooting.md +46 -0
- package/package.json +98 -5
- package/schemas/configuration.schema.json +794 -0
- package/schemas/continuous-focus-palette.schema.json +325 -0
- package/schemas/focus-plan.schema.json +902 -0
- package/schemas/focus-planning-sidecar.schema.json +364 -0
- package/schemas/gui-animation-source.schema.json +176 -0
- package/schemas/gui-helper.schema.json +301 -0
- package/schemas/gui-scenario.schema.json +298 -0
- package/schemas/map-operation.schema.json +2284 -0
- package/schemas/operation-result.schema.json +344 -0
- package/schemas/transaction-manifest.schema.json +685 -0
- package/server.json +32 -0
|
@@ -0,0 +1,951 @@
|
|
|
1
|
+
import { DiagnosticCollector } from '../core/diagnostic-collector.js';
|
|
2
|
+
import { sortDiagnostics } from '../core/diagnostics.js';
|
|
3
|
+
import { sha256Bytes } from '../core/canonical.js';
|
|
4
|
+
import { assertRenderDimensions } from '../core/render-budget.js';
|
|
5
|
+
import { GuiAssetCatalog } from './assets.js';
|
|
6
|
+
import { GUI_GRAPH_MAX_EDGES, GUI_GRAPH_MAX_NODES, GUI_SCENE_MAX_ELEMENTS, GUI_VALIDATION_MAX_ANCESTOR_HOPS, GUI_VALIDATION_MAX_DIAGNOSTICS, GUI_VALIDATION_MAX_PAIR_COMPARISONS, } from './limits.js';
|
|
7
|
+
const allStates = [
|
|
8
|
+
'normal',
|
|
9
|
+
'hover',
|
|
10
|
+
'selected',
|
|
11
|
+
'locked',
|
|
12
|
+
'disabled',
|
|
13
|
+
'warning',
|
|
14
|
+
'active',
|
|
15
|
+
'completed',
|
|
16
|
+
'empty-list',
|
|
17
|
+
'full-list',
|
|
18
|
+
'minimum-value',
|
|
19
|
+
'maximum-value',
|
|
20
|
+
'long-text',
|
|
21
|
+
'missing-localisation',
|
|
22
|
+
];
|
|
23
|
+
function validationCollector() {
|
|
24
|
+
return new DiagnosticCollector(GUI_VALIDATION_MAX_DIAGNOSTICS, {
|
|
25
|
+
code: 'GUI_VALIDATION_DIAGNOSTICS_TRUNCATED',
|
|
26
|
+
category: 'layout',
|
|
27
|
+
message: 'GUI validation diagnostics exceeded the fixed global result ceiling',
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
class GuiValidationWorkBudget {
|
|
31
|
+
pairComparisons = 0;
|
|
32
|
+
ancestorHops = 0;
|
|
33
|
+
pairBlocked = false;
|
|
34
|
+
ancestorBlocked = false;
|
|
35
|
+
admitPairs(comparisons, phase, diagnostics) {
|
|
36
|
+
if (!Number.isSafeInteger(comparisons) ||
|
|
37
|
+
comparisons < 0 ||
|
|
38
|
+
comparisons > GUI_VALIDATION_MAX_PAIR_COMPARISONS - this.pairComparisons) {
|
|
39
|
+
if (!this.pairBlocked) {
|
|
40
|
+
this.pairBlocked = true;
|
|
41
|
+
diagnostics.push({
|
|
42
|
+
code: 'GUI_VALIDATION_COMPARISON_BUDGET_BLOCKED',
|
|
43
|
+
severity: 'blocker',
|
|
44
|
+
category: 'layout',
|
|
45
|
+
message: `${phase} exceeds the fixed shared pair-comparison ceiling`,
|
|
46
|
+
details: {
|
|
47
|
+
phase,
|
|
48
|
+
usedComparisons: this.pairComparisons,
|
|
49
|
+
requestedComparisons: comparisons,
|
|
50
|
+
maximumComparisons: GUI_VALIDATION_MAX_PAIR_COMPARISONS,
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
this.pairComparisons += comparisons;
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
spendAncestorHop(phase, diagnostics) {
|
|
60
|
+
if (this.ancestorHops >= GUI_VALIDATION_MAX_ANCESTOR_HOPS) {
|
|
61
|
+
if (!this.ancestorBlocked) {
|
|
62
|
+
this.ancestorBlocked = true;
|
|
63
|
+
diagnostics.push({
|
|
64
|
+
code: 'GUI_VALIDATION_ANCESTOR_BUDGET_BLOCKED',
|
|
65
|
+
severity: 'blocker',
|
|
66
|
+
category: 'layout',
|
|
67
|
+
message: `${phase} exceeds the fixed ancestor-hop ceiling`,
|
|
68
|
+
details: {
|
|
69
|
+
phase,
|
|
70
|
+
ancestorHops: this.ancestorHops,
|
|
71
|
+
maximumAncestorHops: GUI_VALIDATION_MAX_ANCESTOR_HOPS,
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
this.ancestorHops += 1;
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function intersection(left, right) {
|
|
82
|
+
const x = Math.max(left.x, right.x);
|
|
83
|
+
const y = Math.max(left.y, right.y);
|
|
84
|
+
const rightEdge = Math.min(left.x + left.width, right.x + right.width);
|
|
85
|
+
const bottomEdge = Math.min(left.y + left.height, right.y + right.height);
|
|
86
|
+
return rightEdge <= x || bottomEdge <= y
|
|
87
|
+
? undefined
|
|
88
|
+
: { x, y, width: rightEdge - x, height: bottomEdge - y };
|
|
89
|
+
}
|
|
90
|
+
function area(rect) {
|
|
91
|
+
return Math.max(0, rect.width) * Math.max(0, rect.height);
|
|
92
|
+
}
|
|
93
|
+
function contains(outer, inner, tolerance = 0.01) {
|
|
94
|
+
return (inner.x >= outer.x - tolerance &&
|
|
95
|
+
inner.y >= outer.y - tolerance &&
|
|
96
|
+
inner.x + inner.width <= outer.x + outer.width + tolerance &&
|
|
97
|
+
inner.y + inner.height <= outer.y + outer.height + tolerance);
|
|
98
|
+
}
|
|
99
|
+
function sameRect(left, right) {
|
|
100
|
+
return (Math.abs(left.x - right.x) < 0.01 &&
|
|
101
|
+
Math.abs(left.y - right.y) < 0.01 &&
|
|
102
|
+
Math.abs(left.width - right.width) < 0.01 &&
|
|
103
|
+
Math.abs(left.height - right.height) < 0.01);
|
|
104
|
+
}
|
|
105
|
+
function issue(code, severity, category, message, element, details) {
|
|
106
|
+
return {
|
|
107
|
+
code,
|
|
108
|
+
severity,
|
|
109
|
+
category,
|
|
110
|
+
message,
|
|
111
|
+
...(element?.location === undefined ? {} : { location: element.location }),
|
|
112
|
+
...(details === undefined ? {} : { details }),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function hasAncestor(left, right, byId, work, diagnostics) {
|
|
116
|
+
let current = left.parentId;
|
|
117
|
+
while (current !== undefined) {
|
|
118
|
+
if (!work.spendAncestorHop('GUI overlap ancestry validation', diagnostics))
|
|
119
|
+
return undefined;
|
|
120
|
+
if (current === right.id || byId.get(current)?.sourceId === right.sourceId)
|
|
121
|
+
return true;
|
|
122
|
+
current = byId.get(current)?.parentId;
|
|
123
|
+
}
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
function actionElementName(action) {
|
|
127
|
+
return action
|
|
128
|
+
.replace(/_(?:alt_|control_|shift_)?(?:left_|right_)?click_enabled$/u, '')
|
|
129
|
+
.replace(/_(?:alt_|control_|shift_)?(?:left_|right_)?click$/u, '')
|
|
130
|
+
.replace(/_(?:visible|enabled)$/u, '');
|
|
131
|
+
}
|
|
132
|
+
function attachedScriptedGuis(graph, scene) {
|
|
133
|
+
return graph.scriptedGuis.filter((definition) => definition.windowName === scene.windowName ||
|
|
134
|
+
definition.parentWindowName === scene.windowName);
|
|
135
|
+
}
|
|
136
|
+
function normalizedCostResource(value) {
|
|
137
|
+
const normalized = value.toLowerCase().replace(/_icon$/u, '');
|
|
138
|
+
return normalized === 'political_power' ? 'pol_power' : normalized;
|
|
139
|
+
}
|
|
140
|
+
function localisationCosts(value) {
|
|
141
|
+
const result = new Map();
|
|
142
|
+
const plain = value.replace(/\u00a7./gu, '');
|
|
143
|
+
for (const match of plain.matchAll(/\u00a3([A-Za-z0-9_.-]+)\s*([+-]?(?:\d+(?:\.\d*)?|\.\d+))/gu)) {
|
|
144
|
+
const resource = match[1];
|
|
145
|
+
const amount = match[2];
|
|
146
|
+
if (resource === undefined || amount === undefined)
|
|
147
|
+
continue;
|
|
148
|
+
result.set(normalizedCostResource(resource), Math.abs(Number(amount)));
|
|
149
|
+
}
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
function validateSourceCosts(graph, scene, diagnostics) {
|
|
153
|
+
const elementsByName = new Map(graph.elements.map((element) => [element.name, element]));
|
|
154
|
+
const localisationByKey = new Map(graph.localisation
|
|
155
|
+
.filter(({ language }) => language === scene.scenario.language)
|
|
156
|
+
.map((entry) => [entry.key, entry]));
|
|
157
|
+
for (const scripted of attachedScriptedGuis(graph, scene)) {
|
|
158
|
+
for (const effect of scripted.effectDefinitions) {
|
|
159
|
+
const element = elementsByName.get(effect.elementName);
|
|
160
|
+
if (element === undefined)
|
|
161
|
+
continue;
|
|
162
|
+
const advertised = new Map();
|
|
163
|
+
for (const field of ['buttonText', 'pdx_tooltip', 'pdx_tooltip_delayed']) {
|
|
164
|
+
const key = element.attributes[field];
|
|
165
|
+
if (typeof key !== 'string')
|
|
166
|
+
continue;
|
|
167
|
+
const entry = localisationByKey.get(key);
|
|
168
|
+
if (entry === undefined)
|
|
169
|
+
continue;
|
|
170
|
+
for (const [resource, amount] of localisationCosts(entry.value)) {
|
|
171
|
+
const previous = advertised.get(resource);
|
|
172
|
+
if (previous !== undefined && previous.amount !== amount) {
|
|
173
|
+
diagnostics.push({
|
|
174
|
+
code: 'GUI_COST_DISPLAY_CONFLICT',
|
|
175
|
+
severity: 'error',
|
|
176
|
+
category: 'design',
|
|
177
|
+
message: `GUI element ${element.name} advertises conflicting ${resource} costs ${previous.amount} and ${amount}.`,
|
|
178
|
+
...(entry.location === undefined ? {} : { location: entry.location }),
|
|
179
|
+
...(previous.location === undefined ? {} : { related: [previous.location] }),
|
|
180
|
+
details: { element: element.name, resource, firstKey: previous.key, secondKey: key },
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
advertised.set(resource, {
|
|
185
|
+
amount,
|
|
186
|
+
...(entry.location === undefined ? {} : { location: entry.location }),
|
|
187
|
+
key,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
for (const [resource, display] of advertised) {
|
|
193
|
+
const scriptCost = effect.costs[resource];
|
|
194
|
+
if (scriptCost === display.amount)
|
|
195
|
+
continue;
|
|
196
|
+
diagnostics.push({
|
|
197
|
+
code: 'GUI_COST_MISMATCH',
|
|
198
|
+
severity: 'error',
|
|
199
|
+
category: 'design',
|
|
200
|
+
message: scriptCost === undefined
|
|
201
|
+
? `GUI element ${element.name} advertises a ${display.amount} ${resource} cost, but ${effect.name} has no matching direct cost effect.`
|
|
202
|
+
: `GUI element ${element.name} advertises a ${display.amount} ${resource} cost, but ${effect.name} deducts ${scriptCost}.`,
|
|
203
|
+
...(display.location === undefined
|
|
204
|
+
? element.location === undefined
|
|
205
|
+
? {}
|
|
206
|
+
: { location: element.location }
|
|
207
|
+
: { location: display.location }),
|
|
208
|
+
...(effect.location === undefined ? {} : { related: [effect.location] }),
|
|
209
|
+
details: {
|
|
210
|
+
element: element.name,
|
|
211
|
+
localisationKey: display.key,
|
|
212
|
+
effect: effect.name,
|
|
213
|
+
resource,
|
|
214
|
+
guiCost: display.amount,
|
|
215
|
+
scriptCost: scriptCost ?? null,
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
function validateOverlapAndGeometry(scene, diagnostics, work) {
|
|
223
|
+
const visible = scene.elements.filter((element) => element.visible && area(element.rect) > 0);
|
|
224
|
+
const byId = new Map(scene.elements.map((element) => [element.id, element]));
|
|
225
|
+
for (const element of scene.elements) {
|
|
226
|
+
if (element.unclippedRect.width <= 0 || element.unclippedRect.height <= 0) {
|
|
227
|
+
diagnostics.push(issue('GUI_INVALID_SIZE', 'warning', 'layout', `${element.name} has a non-positive ${element.unclippedRect.width}x${element.unclippedRect.height} size.`, element));
|
|
228
|
+
}
|
|
229
|
+
if (element.clipped)
|
|
230
|
+
diagnostics.push(issue('GUI_ACCIDENTAL_CLIPPING', 'warning', 'layout', `${element.name} is clipped from ${element.unclippedRect.width}x${element.unclippedRect.height} to ${element.rect.width}x${element.rect.height}.`, element));
|
|
231
|
+
if (element.text?.overflowX === true || element.text?.overflowY === true) {
|
|
232
|
+
diagnostics.push(issue('GUI_TEXT_OVERFLOW', 'warning', 'layout', `${element.name} text measures ${element.text.measuredWidth.toFixed(2)}x${element.text.measuredHeight.toFixed(2)} inside ${element.unclippedRect.width.toFixed(2)}x${element.unclippedRect.height.toFixed(2)}.`, element));
|
|
233
|
+
}
|
|
234
|
+
if (element.parentId !== undefined &&
|
|
235
|
+
element.clipRect !== undefined &&
|
|
236
|
+
!contains(element.clipRect, element.unclippedRect)) {
|
|
237
|
+
diagnostics.push(issue('GUI_CHILD_OUTSIDE_CLIPPED_PARENT', 'warning', 'layout', `${element.name} extends outside its clipped parent.`, element));
|
|
238
|
+
}
|
|
239
|
+
if (element.clickable && element.visible && !sameRect(element.rect, element.unclippedRect)) {
|
|
240
|
+
diagnostics.push(issue('GUI_CLICK_BOUNDS_MISMATCH', 'warning', 'design', `${element.name} has visible/clipped bounds that differ from its source click rectangle.`, element));
|
|
241
|
+
}
|
|
242
|
+
if (element.clickable && !element.visible)
|
|
243
|
+
diagnostics.push(issue('GUI_INVISIBLE_CLICK_BLOCKER', 'error', 'design', `${element.name} remains clickable while invisible in this scenario.`, element));
|
|
244
|
+
}
|
|
245
|
+
const visiblePairCount = (visible.length * (visible.length - 1)) / 2;
|
|
246
|
+
if (work.admitPairs(visiblePairCount, 'GUI overlap validation', diagnostics)) {
|
|
247
|
+
pairLoop: for (let leftIndex = 0; leftIndex < visible.length; leftIndex += 1) {
|
|
248
|
+
const left = visible[leftIndex];
|
|
249
|
+
if (left === undefined)
|
|
250
|
+
continue;
|
|
251
|
+
for (let rightIndex = leftIndex + 1; rightIndex < visible.length; rightIndex += 1) {
|
|
252
|
+
const right = visible[rightIndex];
|
|
253
|
+
if (right === undefined)
|
|
254
|
+
continue;
|
|
255
|
+
const leftAncestor = hasAncestor(left, right, byId, work, diagnostics);
|
|
256
|
+
if (leftAncestor === undefined)
|
|
257
|
+
break pairLoop;
|
|
258
|
+
const rightAncestor = hasAncestor(right, left, byId, work, diagnostics);
|
|
259
|
+
if (rightAncestor === undefined)
|
|
260
|
+
break pairLoop;
|
|
261
|
+
if (leftAncestor || rightAncestor)
|
|
262
|
+
continue;
|
|
263
|
+
const overlap = intersection(left.rect, right.rect);
|
|
264
|
+
if (overlap === undefined)
|
|
265
|
+
continue;
|
|
266
|
+
const ratio = area(overlap) / Math.max(1, Math.min(area(left.rect), area(right.rect)));
|
|
267
|
+
if (left.clickable && right.clickable && ratio > 0.02) {
|
|
268
|
+
diagnostics.push(issue('GUI_CONFLICTING_CLICK_REGIONS', 'error', 'design', `${left.name} and ${right.name} have overlapping click regions (${(ratio * 100).toFixed(1)}%).`, right, { left: left.id, right: right.id, ratio }));
|
|
269
|
+
}
|
|
270
|
+
if (left.depth === right.depth && ratio > 0.2) {
|
|
271
|
+
diagnostics.push(issue('GUI_VISIBLE_OVERLAP', 'warning', 'layout', `${left.name} and ${right.name} overlap by ${(ratio * 100).toFixed(1)}% of the smaller element.`, right, { left: left.id, right: right.id, ratio }));
|
|
272
|
+
}
|
|
273
|
+
const later = left.zIndex > right.zIndex ? left : right;
|
|
274
|
+
const earlier = later === left ? right : left;
|
|
275
|
+
if (earlier.clickable && !later.clickThrough && ratio > 0.9) {
|
|
276
|
+
diagnostics.push(issue('GUI_Z_ORDER_RISK', 'warning', 'design', `${later.name} nearly covers clickable ${earlier.name} at a higher z-order.`, later));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
function validateAlignmentAndSpacing(scene, diagnostics, work) {
|
|
283
|
+
const groups = new Map();
|
|
284
|
+
for (const element of scene.elements.filter(({ visible }) => visible)) {
|
|
285
|
+
const group = groups.get(element.parentId ?? '<root>') ?? [];
|
|
286
|
+
group.push(element);
|
|
287
|
+
groups.set(element.parentId ?? '<root>', group);
|
|
288
|
+
}
|
|
289
|
+
const siblingPairCount = [...groups.values()].reduce((total, siblings) => total + (siblings.length * (siblings.length - 1)) / 2, 0);
|
|
290
|
+
const compareSiblingPairs = work.admitPairs(siblingPairCount, 'GUI sibling validation', diagnostics);
|
|
291
|
+
for (const siblings of groups.values()) {
|
|
292
|
+
if (siblings.length < 2)
|
|
293
|
+
continue;
|
|
294
|
+
if (compareSiblingPairs)
|
|
295
|
+
for (let leftIndex = 0; leftIndex < siblings.length; leftIndex += 1) {
|
|
296
|
+
const left = siblings[leftIndex];
|
|
297
|
+
if (left === undefined)
|
|
298
|
+
continue;
|
|
299
|
+
for (let rightIndex = leftIndex + 1; rightIndex < siblings.length; rightIndex += 1) {
|
|
300
|
+
const right = siblings[rightIndex];
|
|
301
|
+
if (right === undefined)
|
|
302
|
+
continue;
|
|
303
|
+
const xDelta = Math.abs(left.unclippedRect.x - right.unclippedRect.x);
|
|
304
|
+
const yDelta = Math.abs(left.unclippedRect.y - right.unclippedRect.y);
|
|
305
|
+
if ((xDelta > 0.25 && xDelta < 3) || (yDelta > 0.25 && yDelta < 3)) {
|
|
306
|
+
diagnostics.push(issue('GUI_INCONSISTENT_ALIGNMENT', 'info', 'layout', `${left.name} and ${right.name} are nearly, but not exactly, aligned (${xDelta.toFixed(2)}px x / ${yDelta.toFixed(2)}px y).`, right));
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
const vertical = siblings.toSorted((left, right) => left.unclippedRect.y - right.unclippedRect.y);
|
|
311
|
+
const gaps = vertical.slice(1).map((element, index) => {
|
|
312
|
+
const previous = vertical[index];
|
|
313
|
+
return previous === undefined
|
|
314
|
+
? 0
|
|
315
|
+
: element.unclippedRect.y - (previous.unclippedRect.y + previous.unclippedRect.height);
|
|
316
|
+
});
|
|
317
|
+
const positive = gaps.filter((gap) => gap >= 0);
|
|
318
|
+
if (positive.length >= 3 && Math.max(...positive) - Math.min(...positive) > 3) {
|
|
319
|
+
diagnostics.push(issue('GUI_INCONSISTENT_SPACING', 'info', 'layout', `Sibling vertical gaps vary from ${Math.min(...positive).toFixed(2)}px to ${Math.max(...positive).toFixed(2)}px.`, vertical.at(-1)));
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
function validateReferencesAndScript(graph, scene, diagnostics) {
|
|
324
|
+
const sceneSourceIds = new Set(scene.elements.map(({ sourceId }) => sourceId));
|
|
325
|
+
const selectedSpriteNames = new Set(scene.elements.flatMap(({ sprite }) => (sprite === undefined ? [] : [sprite.spriteName])));
|
|
326
|
+
const selectedSpriteIds = new Set(graph.sprites.filter(({ name }) => selectedSpriteNames.has(name)).map(({ id }) => id));
|
|
327
|
+
const selectedFontNames = new Set(scene.elements.flatMap(({ text }) => (text?.fontName === undefined ? [] : [text.fontName])));
|
|
328
|
+
const selectedFontIds = new Set(graph.fonts.filter(({ name }) => selectedFontNames.has(name)).map(({ id }) => id));
|
|
329
|
+
const selectedManifestIds = new Set(graph.animationSources
|
|
330
|
+
.filter(({ sprite }) => selectedSpriteNames.has(sprite))
|
|
331
|
+
.map(({ id }) => id));
|
|
332
|
+
const unresolved = graph.edges
|
|
333
|
+
.filter(({ resolved }) => !resolved)
|
|
334
|
+
.filter((edge) => {
|
|
335
|
+
if (edge.kind === 'uses_sprite' || edge.kind === 'uses_localisation')
|
|
336
|
+
return sceneSourceIds.has(edge.from);
|
|
337
|
+
if (edge.kind === 'uses_texture')
|
|
338
|
+
return selectedSpriteIds.has(edge.from);
|
|
339
|
+
if (edge.kind === 'uses_font')
|
|
340
|
+
return edge.metadata.assetPath === undefined
|
|
341
|
+
? sceneSourceIds.has(edge.from)
|
|
342
|
+
: selectedFontIds.has(edge.from);
|
|
343
|
+
if (edge.kind === 'animation_sheet' || edge.kind === 'animation_source_frame')
|
|
344
|
+
return selectedManifestIds.has(edge.from);
|
|
345
|
+
return true;
|
|
346
|
+
});
|
|
347
|
+
for (const edge of unresolved) {
|
|
348
|
+
const partialInventory = edge.partialInventory === true;
|
|
349
|
+
diagnostics.push({
|
|
350
|
+
code: partialInventory
|
|
351
|
+
? 'GUI_REFERENCE_UNRESOLVED_PARTIAL'
|
|
352
|
+
: edge.kind === 'uses_sprite'
|
|
353
|
+
? 'GUI_MISSING_SPRITE'
|
|
354
|
+
: edge.kind === 'uses_texture'
|
|
355
|
+
? 'GUI_MISSING_TEXTURE'
|
|
356
|
+
: edge.kind === 'uses_font'
|
|
357
|
+
? 'GUI_MISSING_FONT'
|
|
358
|
+
: edge.kind === 'uses_localisation'
|
|
359
|
+
? 'GUI_MISSING_LOCALISATION'
|
|
360
|
+
: 'GUI_UNRESOLVED_REFERENCE',
|
|
361
|
+
severity: partialInventory ? 'warning' : 'error',
|
|
362
|
+
category: 'reference',
|
|
363
|
+
message: partialInventory
|
|
364
|
+
? `The partial GUI inventory cannot resolve ${edge.kind.replaceAll('_', ' ')} reference ${referenceLabel(edge.metadata, edge.to)}; a skipped source could define it.`
|
|
365
|
+
: `Unresolved ${edge.kind.replaceAll('_', ' ')} reference ${referenceLabel(edge.metadata, edge.to)}.`,
|
|
366
|
+
...(edge.location === undefined ? {} : { location: edge.location }),
|
|
367
|
+
...(partialInventory ? { details: { partialInventory: true, edgeId: edge.id } } : {}),
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
for (const element of scene.elements) {
|
|
371
|
+
if (element.text?.metricSource === 'approximation' && element.text.fontName !== undefined)
|
|
372
|
+
diagnostics.push(issue('GUI_MISSING_FONT', 'warning', 'reference', `${element.name} uses unavailable font metrics for ${element.text.fontName}.`, element));
|
|
373
|
+
if (element.text !== undefined && element.text.unresolvedTokens.length > 0)
|
|
374
|
+
diagnostics.push(issue('GUI_UNRESOLVED_DYNAMIC_VALUE', 'warning', 'rendering', `${element.name} contains unresolved values: ${element.text.unresolvedTokens.join(', ')}.`, element));
|
|
375
|
+
for (const field of element.unsupportedAttributes)
|
|
376
|
+
diagnostics.push(issue('GUI_RENDER_FIELD_UNSUPPORTED', 'info', 'rendering', `${element.name}.${field} is preserved in source but not reliably modelled.`, element));
|
|
377
|
+
}
|
|
378
|
+
const validContexts = new Set([
|
|
379
|
+
'country',
|
|
380
|
+
'state',
|
|
381
|
+
'unit_leader',
|
|
382
|
+
'operative',
|
|
383
|
+
'character',
|
|
384
|
+
'division',
|
|
385
|
+
'decision',
|
|
386
|
+
'decision_category',
|
|
387
|
+
'national_focus',
|
|
388
|
+
'shared_focus',
|
|
389
|
+
'technology',
|
|
390
|
+
'equipment',
|
|
391
|
+
'military_industrial_organization',
|
|
392
|
+
'special_project',
|
|
393
|
+
]);
|
|
394
|
+
const names = new Set(graph.scriptedGuis.map(({ name }) => name));
|
|
395
|
+
const windowNames = new Set(graph.elements.map(({ name }) => name));
|
|
396
|
+
for (const scripted of graph.scriptedGuis) {
|
|
397
|
+
if (scripted.contextType === undefined) {
|
|
398
|
+
diagnostics.push({
|
|
399
|
+
code: 'GUI_SCRIPTED_CONTEXT_MISSING',
|
|
400
|
+
severity: 'warning',
|
|
401
|
+
category: 'reference',
|
|
402
|
+
message: `Scripted GUI ${scripted.name} does not declare context_type.`,
|
|
403
|
+
...(scripted.location === undefined ? {} : { location: scripted.location }),
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
else if (!validContexts.has(scripted.contextType)) {
|
|
407
|
+
diagnostics.push({
|
|
408
|
+
code: 'GUI_SCRIPTED_CONTEXT_INVALID',
|
|
409
|
+
severity: 'error',
|
|
410
|
+
category: 'reference',
|
|
411
|
+
message: `Scripted GUI ${scripted.name} uses unknown context_type ${scripted.contextType}.`,
|
|
412
|
+
...(scripted.location === undefined ? {} : { location: scripted.location }),
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
if (scripted.windowName !== undefined && !windowNames.has(scripted.windowName))
|
|
416
|
+
diagnostics.push({
|
|
417
|
+
code: 'GUI_PARENT_WINDOW_INVALID',
|
|
418
|
+
severity: 'error',
|
|
419
|
+
category: 'reference',
|
|
420
|
+
message: `Scripted GUI ${scripted.name} references missing window ${scripted.windowName}.`,
|
|
421
|
+
...(scripted.location === undefined ? {} : { location: scripted.location }),
|
|
422
|
+
});
|
|
423
|
+
if (scripted.parentWindowName !== undefined && !windowNames.has(scripted.parentWindowName))
|
|
424
|
+
diagnostics.push({
|
|
425
|
+
code: 'GUI_PARENT_WINDOW_INVALID',
|
|
426
|
+
severity: 'error',
|
|
427
|
+
category: 'reference',
|
|
428
|
+
message: `Scripted GUI ${scripted.name} references missing parent window ${scripted.parentWindowName}.`,
|
|
429
|
+
...(scripted.location === undefined ? {} : { location: scripted.location }),
|
|
430
|
+
});
|
|
431
|
+
if (scripted.parentScriptedGui !== undefined && !names.has(scripted.parentScriptedGui))
|
|
432
|
+
diagnostics.push({
|
|
433
|
+
code: 'GUI_PARENT_SCRIPTED_GUI_INVALID',
|
|
434
|
+
severity: 'error',
|
|
435
|
+
category: 'reference',
|
|
436
|
+
message: `Scripted GUI ${scripted.name} references missing parent scripted GUI ${scripted.parentScriptedGui}.`,
|
|
437
|
+
...(scripted.location === undefined ? {} : { location: scripted.location }),
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
const attached = attachedScriptedGuis(graph, scene);
|
|
441
|
+
if (attached.length > 0) {
|
|
442
|
+
const effects = new Set(attached.flatMap(({ effects }) => effects.map(actionElementName)));
|
|
443
|
+
const triggers = new Set(attached.flatMap(({ triggers }) => triggers.map(actionElementName)));
|
|
444
|
+
for (const button of scene.elements.filter(({ elementType }) => /button/iu.test(elementType))) {
|
|
445
|
+
if (!effects.has(button.name))
|
|
446
|
+
diagnostics.push(issue('GUI_BUTTON_EFFECT_MISSING', 'error', 'reference', `Button ${button.name} has no matching scripted GUI click effect.`, button));
|
|
447
|
+
if (!triggers.has(button.name))
|
|
448
|
+
diagnostics.push(issue('GUI_BUTTON_TRIGGER_MISSING', 'warning', 'reference', `Button ${button.name} has no matching scripted GUI visibility/enabled trigger.`, button));
|
|
449
|
+
}
|
|
450
|
+
for (const scripted of attached.filter(({ effects, aiEnabled }) => effects.length > 0 && !aiEnabled)) {
|
|
451
|
+
diagnostics.push({
|
|
452
|
+
code: 'GUI_AI_EQUIVALENT_MISSING',
|
|
453
|
+
severity: 'warning',
|
|
454
|
+
category: 'design',
|
|
455
|
+
message: `Scripted GUI ${scripted.name} exposes player effects without ai_enabled or ai_check logic.`,
|
|
456
|
+
...(scripted.location === undefined ? {} : { location: scripted.location }),
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
validateSourceCosts(graph, scene, diagnostics);
|
|
461
|
+
for (const [key, guiCost] of Object.entries(scene.scenario.guiCosts)) {
|
|
462
|
+
const scriptCost = scene.scenario.scriptCosts[key];
|
|
463
|
+
if (scriptCost !== undefined && guiCost !== scriptCost)
|
|
464
|
+
diagnostics.push({
|
|
465
|
+
code: 'GUI_COST_MISMATCH',
|
|
466
|
+
severity: 'error',
|
|
467
|
+
category: 'design',
|
|
468
|
+
message: `GUI cost ${key} is ${guiCost}, but the scenario script cost is ${scriptCost}.`,
|
|
469
|
+
details: { key, guiCost, scriptCost },
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
function referenceLabel(metadata, fallback) {
|
|
474
|
+
const value = metadata.spriteName ?? metadata.texturePath ?? metadata.fontName ?? metadata.key;
|
|
475
|
+
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean')
|
|
476
|
+
return String(value);
|
|
477
|
+
return value === undefined ? fallback : JSON.stringify(value);
|
|
478
|
+
}
|
|
479
|
+
function validateScrollAndTabs(scene, diagnostics) {
|
|
480
|
+
for (const element of scene.elements) {
|
|
481
|
+
if (element.rowIndex !== undefined && element.clipped)
|
|
482
|
+
diagnostics.push(issue('GUI_SCROLL_ROW_CUTOFF', 'warning', 'layout', `Row ${element.rowIndex + 1} element ${element.name} is cut off by its container.`, element));
|
|
483
|
+
}
|
|
484
|
+
const groups = new Map();
|
|
485
|
+
for (const element of scene.elements.filter(({ visible, name, state }) => visible && /tab/iu.test(name) && (state === 'selected' || state === 'active'))) {
|
|
486
|
+
const group = groups.get(element.parentId ?? '<root>') ?? [];
|
|
487
|
+
group.push(element);
|
|
488
|
+
groups.set(element.parentId ?? '<root>', group);
|
|
489
|
+
}
|
|
490
|
+
for (const tabs of groups.values()) {
|
|
491
|
+
if (tabs.length > 1)
|
|
492
|
+
diagnostics.push(issue('GUI_TAB_STATE_CONFLICT', 'error', 'design', `Conflicting tab state: ${tabs.map(({ name }) => name).join(', ')} are visible and active together.`, tabs[1]));
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
function alphaBounds(data, width, height, left, frameWidth) {
|
|
496
|
+
let minX = frameWidth;
|
|
497
|
+
let minY = height;
|
|
498
|
+
let maxX = -1;
|
|
499
|
+
let maxY = -1;
|
|
500
|
+
for (let y = 0; y < height; y += 1) {
|
|
501
|
+
for (let x = 0; x < frameWidth; x += 1) {
|
|
502
|
+
const alpha = data.readUInt8((y * width + left + x) * 4 + 3);
|
|
503
|
+
if (alpha === 0)
|
|
504
|
+
continue;
|
|
505
|
+
minX = Math.min(minX, x);
|
|
506
|
+
minY = Math.min(minY, y);
|
|
507
|
+
maxX = Math.max(maxX, x);
|
|
508
|
+
maxY = Math.max(maxY, y);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
return maxX < minX
|
|
512
|
+
? undefined
|
|
513
|
+
: { x: minX, y: minY, width: maxX - minX + 1, height: maxY - minY + 1 };
|
|
514
|
+
}
|
|
515
|
+
function normalizedAssetPath(value) {
|
|
516
|
+
return value
|
|
517
|
+
.replaceAll('\\', '/')
|
|
518
|
+
.replace(/^\/+|^\.\//u, '')
|
|
519
|
+
.toLowerCase();
|
|
520
|
+
}
|
|
521
|
+
function rasterCell(raster, left, width, height) {
|
|
522
|
+
assertRenderDimensions(width, height, 'GUI animation validation cell');
|
|
523
|
+
const output = Buffer.alloc(width * height * 4);
|
|
524
|
+
for (let y = 0; y < height; y += 1) {
|
|
525
|
+
const start = (y * raster.width + left) * 4;
|
|
526
|
+
raster.data.copy(output, y * width * 4, start, start + width * 4);
|
|
527
|
+
}
|
|
528
|
+
return output;
|
|
529
|
+
}
|
|
530
|
+
function sameNumber(left, right) {
|
|
531
|
+
return left === undefined || right === undefined
|
|
532
|
+
? left === right
|
|
533
|
+
: Math.abs(left - right) < 0.000_001;
|
|
534
|
+
}
|
|
535
|
+
async function validateAnimationSourceManifest(sprite, manifest, graph, catalog, diagnostics) {
|
|
536
|
+
const add = (code, severity, message, details) => {
|
|
537
|
+
diagnostics.push({
|
|
538
|
+
code,
|
|
539
|
+
severity,
|
|
540
|
+
category: code.includes('DIMENSION') || code.includes('ANCHOR') ? 'layout' : 'rendering',
|
|
541
|
+
message,
|
|
542
|
+
...(manifest.location === undefined ? {} : { location: manifest.location }),
|
|
543
|
+
...(sprite.location === undefined ? {} : { related: [sprite.location] }),
|
|
544
|
+
...(details === undefined ? {} : { details }),
|
|
545
|
+
});
|
|
546
|
+
};
|
|
547
|
+
if (normalizedAssetPath(sprite.texturePath ?? '') !== normalizedAssetPath(manifest.sheet.path)) {
|
|
548
|
+
add('GUI_ANIMATION_SOURCE_SHEET_REFERENCE_MISMATCH', 'error', `${manifest.sprite} manifest sheet does not match the frameAnimatedSpriteType texturefile.`, { manifestSheet: manifest.sheet.path, spriteSheet: sprite.texturePath ?? null });
|
|
549
|
+
}
|
|
550
|
+
if (sprite.frameCount !== manifest.animation.frameCount ||
|
|
551
|
+
manifest.sourceFrames.length !== manifest.animation.frameCount) {
|
|
552
|
+
add('GUI_ANIMATION_SOURCE_FRAME_COUNT_MISMATCH', 'error', `${manifest.sprite} manifest, source-frame list, and noOfFrames must have the same count.`, {
|
|
553
|
+
spriteFrameCount: sprite.frameCount,
|
|
554
|
+
manifestFrameCount: manifest.animation.frameCount,
|
|
555
|
+
sourceFrameCount: manifest.sourceFrames.length,
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
const settingsMatch = sameNumber(sprite.animationRateFps, manifest.animation.rateFps) &&
|
|
559
|
+
sprite.looping === manifest.animation.looping &&
|
|
560
|
+
sprite.playOnShow === manifest.animation.playOnShow &&
|
|
561
|
+
sameNumber(sprite.pauseOnLoop, manifest.animation.pauseOnLoop);
|
|
562
|
+
if (!settingsMatch) {
|
|
563
|
+
add('GUI_ANIMATION_SOURCE_SETTINGS_MISMATCH', 'error', `${manifest.sprite} manifest animation settings disagree with the sprite definition.`, {
|
|
564
|
+
manifest: manifest.animation,
|
|
565
|
+
sprite: {
|
|
566
|
+
rateFps: sprite.animationRateFps ?? null,
|
|
567
|
+
looping: sprite.looping ?? null,
|
|
568
|
+
playOnShow: sprite.playOnShow ?? null,
|
|
569
|
+
pauseOnLoop: sprite.pauseOnLoop ?? null,
|
|
570
|
+
},
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
const sheetFile = catalog.resolveFile(manifest.sheet.path, manifest.sourcePath);
|
|
574
|
+
const sheetRaster = await catalog.loadRaster(manifest.sheet.path, manifest.sourcePath);
|
|
575
|
+
if (sheetFile === undefined) {
|
|
576
|
+
add('GUI_ANIMATION_SOURCE_SHEET_MISSING', 'error', `${manifest.sprite} manifest sheet is missing.`);
|
|
577
|
+
}
|
|
578
|
+
else if (sheetFile.sha256 !== manifest.sheet.sha256) {
|
|
579
|
+
add('GUI_ANIMATION_SOURCE_HASH_MISMATCH', 'error', `${manifest.sprite} sheet hash does not match its manifest.`, { path: manifest.sheet.path, expected: manifest.sheet.sha256, actual: sheetFile.sha256 });
|
|
580
|
+
}
|
|
581
|
+
if (!sheetRaster.supported ||
|
|
582
|
+
sheetRaster.width !== manifest.sheet.frameWidth * manifest.animation.frameCount ||
|
|
583
|
+
sheetRaster.height !== manifest.sheet.frameHeight) {
|
|
584
|
+
add('GUI_ANIMATION_SOURCE_SHEET_DIMENSION_MISMATCH', 'error', `${manifest.sprite} sheet dimensions do not equal frame width times count by frame height.`, {
|
|
585
|
+
expectedWidth: manifest.sheet.frameWidth * manifest.animation.frameCount,
|
|
586
|
+
expectedHeight: manifest.sheet.frameHeight,
|
|
587
|
+
actualWidth: sheetRaster.width,
|
|
588
|
+
actualHeight: sheetRaster.height,
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
const firstAnchor = manifest.sourceFrames[0]?.anchor;
|
|
592
|
+
const fileHashes = new Set();
|
|
593
|
+
const pixelHashes = new Set();
|
|
594
|
+
const sourceRasters = [];
|
|
595
|
+
for (const [index, frame] of manifest.sourceFrames.entries()) {
|
|
596
|
+
const file = catalog.resolveFile(frame.path, manifest.sourcePath);
|
|
597
|
+
const raster = await catalog.loadRaster(frame.path, manifest.sourcePath);
|
|
598
|
+
sourceRasters.push(raster);
|
|
599
|
+
if (file === undefined) {
|
|
600
|
+
add('GUI_ANIMATION_SOURCE_FRAME_MISSING', 'error', `${manifest.sprite} source frame ${index} is missing.`, { index, path: frame.path });
|
|
601
|
+
continue;
|
|
602
|
+
}
|
|
603
|
+
fileHashes.add(file.sha256);
|
|
604
|
+
if (file.sha256 !== frame.sha256) {
|
|
605
|
+
add('GUI_ANIMATION_SOURCE_HASH_MISMATCH', 'error', `${manifest.sprite} source frame ${index} hash does not match its manifest.`, { index, path: frame.path, expected: frame.sha256, actual: file.sha256 });
|
|
606
|
+
}
|
|
607
|
+
if (!raster.supported ||
|
|
608
|
+
raster.width !== manifest.sheet.frameWidth ||
|
|
609
|
+
raster.height !== manifest.sheet.frameHeight) {
|
|
610
|
+
add('GUI_ANIMATION_SOURCE_FRAME_DIMENSION_MISMATCH', 'error', `${manifest.sprite} source frame ${index} does not match the declared cell dimensions.`, {
|
|
611
|
+
index,
|
|
612
|
+
path: frame.path,
|
|
613
|
+
expectedWidth: manifest.sheet.frameWidth,
|
|
614
|
+
expectedHeight: manifest.sheet.frameHeight,
|
|
615
|
+
actualWidth: raster.width,
|
|
616
|
+
actualHeight: raster.height,
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
else {
|
|
620
|
+
const pixelHash = sha256Bytes(raster.data);
|
|
621
|
+
pixelHashes.add(pixelHash);
|
|
622
|
+
if (sheetRaster.supported &&
|
|
623
|
+
sheetRaster.width === manifest.sheet.frameWidth * manifest.animation.frameCount &&
|
|
624
|
+
sheetRaster.height === manifest.sheet.frameHeight) {
|
|
625
|
+
const sheetCell = rasterCell(sheetRaster, index * manifest.sheet.frameWidth, manifest.sheet.frameWidth, manifest.sheet.frameHeight);
|
|
626
|
+
if (sha256Bytes(sheetCell) !== pixelHash) {
|
|
627
|
+
add('GUI_ANIMATION_SOURCE_SHEET_CELL_MISMATCH', 'error', `${manifest.sprite} sheet cell ${index} does not match its declared independent source frame.`, { index, path: frame.path });
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
if (frame.anchor.x >= manifest.sheet.frameWidth ||
|
|
632
|
+
frame.anchor.y >= manifest.sheet.frameHeight) {
|
|
633
|
+
add('GUI_ANIMATION_SOURCE_ANCHOR_INVALID', 'error', `${manifest.sprite} source frame ${index} anchor is outside its frame.`, { index, anchor: frame.anchor });
|
|
634
|
+
}
|
|
635
|
+
if (firstAnchor !== undefined &&
|
|
636
|
+
(frame.anchor.x !== firstAnchor.x || frame.anchor.y !== firstAnchor.y)) {
|
|
637
|
+
add('GUI_ANIMATION_SOURCE_ANCHOR_DRIFT', 'error', `${manifest.sprite} source-frame anchors are not stable.`, { index, expected: firstAnchor, actual: frame.anchor });
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
if (fileHashes.size !== manifest.sourceFrames.length ||
|
|
641
|
+
pixelHashes.size !== manifest.sourceFrames.length) {
|
|
642
|
+
add('GUI_ANIMATION_SOURCE_FRAMES_NOT_DISTINCT', 'error', `${manifest.sprite} must use independently authored source frames with distinct source and pixel hashes.`, {
|
|
643
|
+
frameCount: manifest.sourceFrames.length,
|
|
644
|
+
distinctSourceHashes: fileHashes.size,
|
|
645
|
+
distinctPixelHashes: pixelHashes.size,
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
const fallbackSprite = graph.sprites.find(({ name }) => name === manifest.staticFallback.sprite);
|
|
649
|
+
if (sprite.staticFallback !== manifest.staticFallback.sprite ||
|
|
650
|
+
fallbackSprite === undefined ||
|
|
651
|
+
normalizedAssetPath(fallbackSprite.texturePath ?? '') !==
|
|
652
|
+
normalizedAssetPath(manifest.staticFallback.path)) {
|
|
653
|
+
add('GUI_ANIMATION_SOURCE_STATIC_FALLBACK_MISMATCH', 'error', `${manifest.sprite} manifest static fallback does not match a resolvable sprite and texture.`, {
|
|
654
|
+
manifestSprite: manifest.staticFallback.sprite,
|
|
655
|
+
declaredSprite: sprite.staticFallback ?? null,
|
|
656
|
+
manifestPath: manifest.staticFallback.path,
|
|
657
|
+
resolvedPath: fallbackSprite?.texturePath ?? null,
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
const fallbackFile = catalog.resolveFile(manifest.staticFallback.path, manifest.sourcePath);
|
|
661
|
+
const fallbackRaster = await catalog.loadRaster(manifest.staticFallback.path, manifest.sourcePath);
|
|
662
|
+
if (fallbackFile?.sha256 !== manifest.staticFallback.sha256) {
|
|
663
|
+
add('GUI_ANIMATION_SOURCE_HASH_MISMATCH', 'error', `${manifest.sprite} static fallback hash does not match its manifest.`, {
|
|
664
|
+
path: manifest.staticFallback.path,
|
|
665
|
+
expected: manifest.staticFallback.sha256,
|
|
666
|
+
actual: fallbackFile?.sha256 ?? null,
|
|
667
|
+
});
|
|
668
|
+
}
|
|
669
|
+
const fallbackSource = sourceRasters[manifest.staticFallback.frameIndex];
|
|
670
|
+
if (manifest.staticFallback.frameIndex >= manifest.sourceFrames.length ||
|
|
671
|
+
!fallbackRaster.supported ||
|
|
672
|
+
fallbackRaster.width !== manifest.sheet.frameWidth ||
|
|
673
|
+
fallbackRaster.height !== manifest.sheet.frameHeight ||
|
|
674
|
+
fallbackSource?.supported !== true ||
|
|
675
|
+
sha256Bytes(fallbackRaster.data) !== sha256Bytes(fallbackSource.data)) {
|
|
676
|
+
add('GUI_ANIMATION_SOURCE_STATIC_FALLBACK_CONTENT_MISMATCH', 'error', `${manifest.sprite} static fallback must exactly match its declared source frame.`, { frameIndex: manifest.staticFallback.frameIndex, path: manifest.staticFallback.path });
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
async function validateGuiAnimationsInto(graph, files, selectedSpriteNames, catalog, diagnostics) {
|
|
680
|
+
const spriteNames = new Set(graph.sprites.map(({ name }) => name));
|
|
681
|
+
const fallbackEdges = new Set(graph.edges
|
|
682
|
+
.filter((edge) => edge.kind === 'static_fallback' && edge.resolved)
|
|
683
|
+
.map(({ from }) => from));
|
|
684
|
+
const manifestsBySprite = new Map();
|
|
685
|
+
for (const manifest of graph.animationSources) {
|
|
686
|
+
const manifests = manifestsBySprite.get(manifest.sprite) ?? [];
|
|
687
|
+
manifests.push(manifest);
|
|
688
|
+
manifestsBySprite.set(manifest.sprite, manifests);
|
|
689
|
+
}
|
|
690
|
+
for (const sprite of graph.sprites) {
|
|
691
|
+
if (selectedSpriteNames !== undefined && !selectedSpriteNames.has(sprite.name))
|
|
692
|
+
continue;
|
|
693
|
+
const location = sprite.location;
|
|
694
|
+
const add = (code, severity, message) => {
|
|
695
|
+
diagnostics.push({
|
|
696
|
+
code,
|
|
697
|
+
severity,
|
|
698
|
+
category: code.includes('DIMENSION') || code.includes('ANCHOR') ? 'layout' : 'rendering',
|
|
699
|
+
message,
|
|
700
|
+
...(location === undefined ? {} : { location }),
|
|
701
|
+
});
|
|
702
|
+
};
|
|
703
|
+
if (sprite.frameCount < 1)
|
|
704
|
+
add('GUI_SPRITE_FRAME_COUNT_INVALID', 'error', `${sprite.name} has invalid noOfFrames ${sprite.frameCount}.`);
|
|
705
|
+
if (sprite.frameAnimated && sprite.frameCount < 2)
|
|
706
|
+
add('GUI_ANIMATION_FRAME_COUNT_INVALID', 'error', `${sprite.name} is frame-animated but noOfFrames is ${sprite.frameCount}.`);
|
|
707
|
+
if (sprite.frameAnimated &&
|
|
708
|
+
(sprite.animationRateFps === undefined ||
|
|
709
|
+
!Number.isFinite(sprite.animationRateFps) ||
|
|
710
|
+
sprite.animationRateFps <= 0))
|
|
711
|
+
add('GUI_ANIMATION_RATE_INVALID', 'error', `${sprite.name} must declare a positive animation_rate_fps; fractional rates are supported.`);
|
|
712
|
+
if (sprite.texturePath === undefined) {
|
|
713
|
+
add(sprite.frameAnimated ? 'GUI_ANIMATION_TEXTURE_MISSING' : 'GUI_TEXTURE_MISSING', 'error', `${sprite.name} has no texturefile.`);
|
|
714
|
+
continue;
|
|
715
|
+
}
|
|
716
|
+
const raster = await catalog.loadRaster(sprite.texturePath, sprite.sourcePath);
|
|
717
|
+
if (!raster.supported) {
|
|
718
|
+
add(sprite.frameAnimated ? 'GUI_ANIMATION_TEXTURE_UNSUPPORTED' : 'GUI_TEXTURE_UNSUPPORTED', 'error', `${sprite.name}: ${raster.reason ?? 'texture unsupported'}`);
|
|
719
|
+
continue;
|
|
720
|
+
}
|
|
721
|
+
const safeFrameCount = Math.max(1, sprite.frameCount);
|
|
722
|
+
if (raster.width % safeFrameCount !== 0)
|
|
723
|
+
add(sprite.frameAnimated
|
|
724
|
+
? 'GUI_ANIMATION_SHEET_DIMENSION_INVALID'
|
|
725
|
+
: 'GUI_SPRITE_SHEET_DIMENSION_INVALID', 'error', `${sprite.name} sheet width ${raster.width} is not divisible by noOfFrames ${sprite.frameCount}.`);
|
|
726
|
+
const frameWidth = raster.width / safeFrameCount;
|
|
727
|
+
if (sprite.declaredSize !== undefined &&
|
|
728
|
+
(sprite.declaredSize.width !== frameWidth || sprite.declaredSize.height !== raster.height)) {
|
|
729
|
+
add('GUI_SPRITE_DECLARED_SIZE_MISMATCH', 'warning', `${sprite.name} declares ${sprite.declaredSize.width}x${sprite.declaredSize.height}, but a frame is ${frameWidth}x${raster.height}.`);
|
|
730
|
+
}
|
|
731
|
+
if (sprite.frameAnimated && Number.isInteger(frameWidth)) {
|
|
732
|
+
const bounds = Array.from({ length: safeFrameCount }, (_unused, index) => alphaBounds(raster.data, raster.width, raster.height, index * frameWidth, frameWidth));
|
|
733
|
+
const centres = bounds.flatMap((bound) => bound === undefined
|
|
734
|
+
? []
|
|
735
|
+
: [{ x: bound.x + bound.width / 2, y: bound.y + bound.height / 2 }]);
|
|
736
|
+
if (centres.length > 1) {
|
|
737
|
+
const spreadX = Math.max(...centres.map(({ x }) => x)) - Math.min(...centres.map(({ x }) => x));
|
|
738
|
+
const spreadY = Math.max(...centres.map(({ y }) => y)) - Math.min(...centres.map(({ y }) => y));
|
|
739
|
+
if (spreadX > frameWidth * 0.25 || spreadY > raster.height * 0.25)
|
|
740
|
+
add('GUI_ANIMATION_ANCHOR_DRIFT', 'warning', `${sprite.name} visible alpha bounds shift ${spreadX.toFixed(1)}px horizontally and ${spreadY.toFixed(1)}px vertically across equal-size frames.`);
|
|
741
|
+
}
|
|
742
|
+
if (!raster.data.some((value, index) => index % 4 === 3 && value < 255))
|
|
743
|
+
add('GUI_ANIMATION_TRANSPARENCY_ABSENT', 'info', `${sprite.name} has no transparent pixels; verify that an opaque strip is intentional.`);
|
|
744
|
+
}
|
|
745
|
+
if (sprite.frameAnimated) {
|
|
746
|
+
if (!fallbackEdges.has(sprite.id))
|
|
747
|
+
add('GUI_ANIMATION_STATIC_FALLBACK_MISSING', 'warning', `${sprite.name} has no resolvable static fallback sprite.`);
|
|
748
|
+
if (sprite.staticFallback !== undefined && !spriteNames.has(sprite.staticFallback)) {
|
|
749
|
+
const partialFallback = graph.edges.some((edge) => edge.kind === 'static_fallback' &&
|
|
750
|
+
edge.from === sprite.id &&
|
|
751
|
+
!edge.resolved &&
|
|
752
|
+
edge.partialInventory === true);
|
|
753
|
+
add(partialFallback
|
|
754
|
+
? 'GUI_REFERENCE_UNRESOLVED_PARTIAL'
|
|
755
|
+
: 'GUI_ANIMATION_STATIC_FALLBACK_INVALID', partialFallback ? 'warning' : 'error', partialFallback
|
|
756
|
+
? `The partial GUI inventory cannot resolve static fallback ${sprite.staticFallback} for ${sprite.name}; a skipped source could define it.`
|
|
757
|
+
: `${sprite.name} references missing static fallback ${sprite.staticFallback}.`);
|
|
758
|
+
}
|
|
759
|
+
if (sprite.looping === undefined)
|
|
760
|
+
add('GUI_ANIMATION_LOOP_UNDECLARED', 'info', `${sprite.name} relies on the engine default for looping.`);
|
|
761
|
+
if (sprite.playOnShow === undefined)
|
|
762
|
+
add('GUI_ANIMATION_PLAY_ON_SHOW_UNDECLARED', 'info', `${sprite.name} relies on the engine default for play_on_show.`);
|
|
763
|
+
const manifests = manifestsBySprite.get(sprite.name) ?? [];
|
|
764
|
+
if (manifests.length === 0) {
|
|
765
|
+
add('GUI_ANIMATION_SOURCE_PROVENANCE_UNAVAILABLE', 'info', `${sprite.name} is validated from its real frame sheet; no project-owned source manifest is present.`);
|
|
766
|
+
}
|
|
767
|
+
else if (manifests.length > 1) {
|
|
768
|
+
add('GUI_ANIMATION_SOURCE_MANIFEST_DUPLICATE', 'error', `${sprite.name} has more than one animation source manifest.`);
|
|
769
|
+
}
|
|
770
|
+
else {
|
|
771
|
+
const manifest = manifests[0];
|
|
772
|
+
if (manifest !== undefined)
|
|
773
|
+
await validateAnimationSourceManifest(sprite, manifest, graph, catalog, diagnostics);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
const animationValidationCodes = [
|
|
779
|
+
'GUI_SPRITE_FRAME_COUNT_INVALID',
|
|
780
|
+
'GUI_SPRITE_SHEET_DIMENSION_INVALID',
|
|
781
|
+
'GUI_ANIMATION_FRAME_COUNT_INVALID',
|
|
782
|
+
'GUI_ANIMATION_RATE_INVALID',
|
|
783
|
+
'GUI_ANIMATION_SHEET_DIMENSION_INVALID',
|
|
784
|
+
'GUI_ANIMATION_STATIC_FALLBACK_MISSING',
|
|
785
|
+
'GUI_ANIMATION_SOURCE_MANIFEST_DUPLICATE',
|
|
786
|
+
'GUI_ANIMATION_SOURCE_HASH_MISMATCH',
|
|
787
|
+
'GUI_ANIMATION_SOURCE_FRAME_COUNT_MISMATCH',
|
|
788
|
+
'GUI_ANIMATION_SOURCE_FRAME_DIMENSION_MISMATCH',
|
|
789
|
+
'GUI_ANIMATION_SOURCE_FRAMES_NOT_DISTINCT',
|
|
790
|
+
'GUI_ANIMATION_SOURCE_SHEET_CELL_MISMATCH',
|
|
791
|
+
'GUI_ANIMATION_SOURCE_SETTINGS_MISMATCH',
|
|
792
|
+
'GUI_ANIMATION_SOURCE_STATIC_FALLBACK_CONTENT_MISMATCH',
|
|
793
|
+
'GUI_ANIMATION_ANCHOR_DRIFT',
|
|
794
|
+
'GUI_ANIMATION_SOURCE_MANIFEST_DUPLICATE',
|
|
795
|
+
'GUI_ANIMATION_SOURCE_HASH_MISMATCH',
|
|
796
|
+
'GUI_ANIMATION_SOURCE_FRAME_COUNT_MISMATCH',
|
|
797
|
+
'GUI_ANIMATION_SOURCE_FRAME_DIMENSION_MISMATCH',
|
|
798
|
+
'GUI_ANIMATION_SOURCE_FRAMES_NOT_DISTINCT',
|
|
799
|
+
'GUI_ANIMATION_SOURCE_SHEET_CELL_MISMATCH',
|
|
800
|
+
'GUI_ANIMATION_SOURCE_SETTINGS_MISMATCH',
|
|
801
|
+
'GUI_ANIMATION_SOURCE_STATIC_FALLBACK_CONTENT_MISMATCH',
|
|
802
|
+
];
|
|
803
|
+
export async function validateGuiAnimations(graph, files, selectedSpriteNames, catalog = new GuiAssetCatalog(graph, files)) {
|
|
804
|
+
const diagnostics = validationCollector();
|
|
805
|
+
await validateGuiAnimationsInto(graph, files, selectedSpriteNames, catalog, diagnostics);
|
|
806
|
+
return validationResult(diagnostics.values(), animationValidationCodes);
|
|
807
|
+
}
|
|
808
|
+
function validateResolutionDriftInto(scenes, diagnostics) {
|
|
809
|
+
const baseline = scenes[0];
|
|
810
|
+
if (baseline !== undefined) {
|
|
811
|
+
for (let sceneIndex = 1; sceneIndex < scenes.length; sceneIndex += 1) {
|
|
812
|
+
const scene = scenes[sceneIndex];
|
|
813
|
+
if (scene === undefined)
|
|
814
|
+
continue;
|
|
815
|
+
const targets = new Map(scene.elements.map((element) => [`${element.sourceId}:${element.rowIndex ?? -1}`, element]));
|
|
816
|
+
for (const element of baseline.elements) {
|
|
817
|
+
const target = targets.get(`${element.sourceId}:${element.rowIndex ?? -1}`);
|
|
818
|
+
if (target === undefined)
|
|
819
|
+
continue;
|
|
820
|
+
const left = {
|
|
821
|
+
x: element.unclippedRect.x / baseline.resolution.width,
|
|
822
|
+
y: element.unclippedRect.y / baseline.resolution.height,
|
|
823
|
+
};
|
|
824
|
+
const right = {
|
|
825
|
+
x: target.unclippedRect.x / scene.resolution.width,
|
|
826
|
+
y: target.unclippedRect.y / scene.resolution.height,
|
|
827
|
+
};
|
|
828
|
+
const drift = Math.hypot(left.x - right.x, left.y - right.y);
|
|
829
|
+
if (drift > 0.03)
|
|
830
|
+
diagnostics.push(issue('GUI_RESOLUTION_DRIFT', 'warning', 'layout', `${element.name} drifts ${(drift * 100).toFixed(1)}% of normalized viewport position between ${baseline.resolution.width}x${baseline.resolution.height} and ${scene.resolution.width}x${scene.resolution.height}.`, target));
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
export function validateResolutionDrift(scenes) {
|
|
836
|
+
const diagnostics = validationCollector();
|
|
837
|
+
validateResolutionDriftInto(scenes, diagnostics);
|
|
838
|
+
return validationResult(diagnostics.values(), ['GUI_RESOLUTION_DRIFT']);
|
|
839
|
+
}
|
|
840
|
+
function validateStateMatrixInto(scenes, diagnostics) {
|
|
841
|
+
const available = new Set(scenes.map(({ scenario }) => scenario.state));
|
|
842
|
+
for (const state of allStates) {
|
|
843
|
+
if (!available.has(state))
|
|
844
|
+
diagnostics.push({
|
|
845
|
+
code: 'GUI_STATE_COVERAGE_MISSING',
|
|
846
|
+
severity: 'warning',
|
|
847
|
+
category: 'design',
|
|
848
|
+
message: `State matrix does not include ${state}.`,
|
|
849
|
+
details: { state },
|
|
850
|
+
});
|
|
851
|
+
}
|
|
852
|
+
for (const scene of scenes)
|
|
853
|
+
validateScrollAndTabs(scene, diagnostics);
|
|
854
|
+
}
|
|
855
|
+
export function validateStateMatrix(scenes) {
|
|
856
|
+
const diagnostics = validationCollector();
|
|
857
|
+
validateStateMatrixInto(scenes, diagnostics);
|
|
858
|
+
return validationResult(diagnostics.values(), [
|
|
859
|
+
'GUI_STATE_COVERAGE_MISSING',
|
|
860
|
+
'GUI_TAB_STATE_CONFLICT',
|
|
861
|
+
]);
|
|
862
|
+
}
|
|
863
|
+
function validationResult(diagnostics, codes) {
|
|
864
|
+
const sorted = sortDiagnostics(diagnostics);
|
|
865
|
+
return {
|
|
866
|
+
diagnostics: sorted,
|
|
867
|
+
checks: codes.map((code) => {
|
|
868
|
+
const count = sorted.filter((diagnostic) => diagnostic.code === code).length;
|
|
869
|
+
return {
|
|
870
|
+
id: code.toLowerCase(),
|
|
871
|
+
passed: count === 0,
|
|
872
|
+
message: count === 0
|
|
873
|
+
? `${code} not detected.`
|
|
874
|
+
: `${code} detected ${count} time${count === 1 ? '' : 's'}.`,
|
|
875
|
+
};
|
|
876
|
+
}),
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
export async function validateGuiScene(graph, scene, files, relatedScenes = [], catalog = new GuiAssetCatalog(graph, files)) {
|
|
880
|
+
if (graph.nodes.length > GUI_GRAPH_MAX_NODES ||
|
|
881
|
+
scene.elements.length > GUI_SCENE_MAX_ELEMENTS ||
|
|
882
|
+
graph.edges.length > GUI_GRAPH_MAX_EDGES) {
|
|
883
|
+
const diagnostic = {
|
|
884
|
+
code: 'GUI_VALIDATION_GRAPH_BUDGET_BLOCKED',
|
|
885
|
+
severity: 'blocker',
|
|
886
|
+
category: 'layout',
|
|
887
|
+
message: 'GUI graph exceeds the fixed validation work ceiling',
|
|
888
|
+
details: {
|
|
889
|
+
nodes: graph.nodes.length,
|
|
890
|
+
maximumNodes: GUI_GRAPH_MAX_NODES,
|
|
891
|
+
elements: scene.elements.length,
|
|
892
|
+
maximumElements: GUI_SCENE_MAX_ELEMENTS,
|
|
893
|
+
edges: graph.edges.length,
|
|
894
|
+
maximumEdges: GUI_GRAPH_MAX_EDGES,
|
|
895
|
+
},
|
|
896
|
+
};
|
|
897
|
+
return {
|
|
898
|
+
diagnostics: [diagnostic],
|
|
899
|
+
checks: [{ id: diagnostic.code, passed: false, message: diagnostic.message }],
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
const diagnostics = validationCollector();
|
|
903
|
+
const work = new GuiValidationWorkBudget();
|
|
904
|
+
diagnostics.pushMany(scene.diagnostics);
|
|
905
|
+
validateOverlapAndGeometry(scene, diagnostics, work);
|
|
906
|
+
validateAlignmentAndSpacing(scene, diagnostics, work);
|
|
907
|
+
validateReferencesAndScript(graph, scene, diagnostics);
|
|
908
|
+
validateScrollAndTabs(scene, diagnostics);
|
|
909
|
+
const selectedSpriteNames = new Set();
|
|
910
|
+
for (const element of scene.elements)
|
|
911
|
+
if (element.sprite !== undefined)
|
|
912
|
+
selectedSpriteNames.add(element.sprite.spriteName);
|
|
913
|
+
await validateGuiAnimationsInto(graph, files, selectedSpriteNames, catalog, diagnostics);
|
|
914
|
+
if (relatedScenes.length > 0)
|
|
915
|
+
validateResolutionDriftInto([scene, ...relatedScenes], diagnostics);
|
|
916
|
+
return validationResult(diagnostics.values(), [
|
|
917
|
+
'GUI_VALIDATION_DIAGNOSTICS_TRUNCATED',
|
|
918
|
+
'GUI_VALIDATION_COMPARISON_BUDGET_BLOCKED',
|
|
919
|
+
'GUI_VALIDATION_ANCESTOR_BUDGET_BLOCKED',
|
|
920
|
+
'GUI_VISIBLE_OVERLAP',
|
|
921
|
+
'GUI_ACCIDENTAL_CLIPPING',
|
|
922
|
+
'GUI_TEXT_OVERFLOW',
|
|
923
|
+
'GUI_INCONSISTENT_ALIGNMENT',
|
|
924
|
+
'GUI_INCONSISTENT_SPACING',
|
|
925
|
+
'GUI_INVALID_SIZE',
|
|
926
|
+
'GUI_CHILD_OUTSIDE_CLIPPED_PARENT',
|
|
927
|
+
'GUI_CLICK_BOUNDS_MISMATCH',
|
|
928
|
+
'GUI_INVISIBLE_CLICK_BLOCKER',
|
|
929
|
+
'GUI_CONFLICTING_CLICK_REGIONS',
|
|
930
|
+
'GUI_MISSING_SPRITE',
|
|
931
|
+
'GUI_MISSING_TEXTURE',
|
|
932
|
+
'GUI_MISSING_FONT',
|
|
933
|
+
'GUI_MISSING_LOCALISATION',
|
|
934
|
+
'GUI_ANIMATION_FRAME_COUNT_INVALID',
|
|
935
|
+
'GUI_ANIMATION_SHEET_DIMENSION_INVALID',
|
|
936
|
+
'GUI_ANIMATION_STATIC_FALLBACK_MISSING',
|
|
937
|
+
'GUI_PARENT_WINDOW_INVALID',
|
|
938
|
+
'GUI_SCRIPTED_CONTEXT_INVALID',
|
|
939
|
+
'GUI_Z_ORDER_RISK',
|
|
940
|
+
'GUI_SCROLL_ROW_CUTOFF',
|
|
941
|
+
'GUI_RESOLUTION_DRIFT',
|
|
942
|
+
'GUI_TAB_STATE_CONFLICT',
|
|
943
|
+
'GUI_BUTTON_EFFECT_MISSING',
|
|
944
|
+
'GUI_BUTTON_TRIGGER_MISSING',
|
|
945
|
+
'GUI_COST_MISMATCH',
|
|
946
|
+
'GUI_COST_DISPLAY_CONFLICT',
|
|
947
|
+
'GUI_AI_EQUIVALENT_MISSING',
|
|
948
|
+
'GUI_RENDER_FIELD_UNSUPPORTED',
|
|
949
|
+
]);
|
|
950
|
+
}
|
|
951
|
+
//# sourceMappingURL=validators.js.map
|