Compare commits

..

5 Commits

Author SHA1 Message Date
44f03f771b
implements PLN-0106
All checks were successful
JaCoCo Coverage #### Project Overview No changes detected, that affect the code coverage. * Line Coverage: 61.49% (17397/28294) * Branch Coverage: 52.32% (6724/12852) * Lines of Code: 28294 * Cyclomatic Complexity: 11339 #### Quality Gates Summary Output truncated.
Test / Build skipped: 15, passed: 608
Intrepid/Prometeu/Studio/pipeline/pr-master This commit looks good
2026-07-15 08:32:19 +01:00
ba3af3c01b
implements PLN-0105 2026-07-15 08:29:51 +01:00
66c1b1a033
implements PLN-0104 2026-07-15 08:28:30 +01:00
f8b9c5b27d
implements PLN-0103 2026-07-15 08:26:37 +01:00
e99bb58453
Multi Provider FE content 2026-07-15 08:24:56 +01:00
25 changed files with 758 additions and 113 deletions

View File

@ -1,4 +1,4 @@
{"type":"meta","next_id":{"DSC":66,"AGD":69,"DEC":40,"PLN":103,"LSN":55,"CLSN":1}}
{"type":"meta","next_id":{"DSC":66,"AGD":69,"DEC":41,"PLN":107,"LSN":55,"CLSN":1}}
{"type":"discussion","id":"DSC-0065","status":"open","ticket":"multi-frontend-avoid-premature-abstractions","title":"Evitar abstracoes prematuras na preparacao multi-frontend","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","studio","frontend","architecture","multi-frontend","simplicity"],"agendas":[{"id":"AGD-0068","file":"AGD-0068-multi-frontend-avoid-premature-abstractions.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0064","status":"open","ticket":"multi-frontend-architectural-tests","title":"Testes arquiteturais para fronteiras multi-frontend","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","studio","frontend","architecture","tests","multi-frontend"],"agendas":[{"id":"AGD-0067","file":"AGD-0067-multi-frontend-architectural-tests.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0063","status":"open","ticket":"multi-frontend-synthetic-test-frontend","title":"Frontend sintetico de teste para provar neutralidade do pipeline","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","frontend","tests","backend","multi-frontend"],"agendas":[{"id":"AGD-0066","file":"AGD-0066-multi-frontend-synthetic-test-frontend.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
@ -10,7 +10,7 @@
{"type":"discussion","id":"DSC-0057","status":"open","ticket":"multi-frontend-frontend-backend-contract","title":"Estabilizar contrato entre frontend e backend comum","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","compiler-pbs","ir","backend","multi-frontend"],"agendas":[{"id":"AGD-0060","file":"AGD-0060-multi-frontend-frontend-backend-contract.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0056","status":"open","ticket":"multi-frontend-remove-pbs-branches","title":"Remover verificacoes explicitas de PBS do codigo comum","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","compiler-pbs","studio","frontend","coupling","multi-frontend"],"agendas":[{"id":"AGD-0059","file":"AGD-0059-multi-frontend-remove-pbs-branches.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0055","status":"open","ticket":"multi-frontend-compiler-vs-language-services","title":"Separar compilacao de servicos editoriais de frontend","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","studio","lsp","editor","frontend","multi-frontend"],"agendas":[{"id":"AGD-0058","file":"AGD-0058-multi-frontend-compiler-vs-language-services.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0054","status":"open","ticket":"multi-frontend-provider-contract","title":"Introduzir provider completo de frontend","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","studio","frontend","registry","multi-frontend"],"agendas":[{"id":"AGD-0057","file":"AGD-0057-multi-frontend-provider-contract.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0054","status":"in_progress","ticket":"multi-frontend-provider-contract","title":"Introduzir provider completo de frontend","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["compiler","compiler-general","studio","frontend","registry","multi-frontend"],"agendas":[{"id":"AGD-0057","file":"AGD-0057-multi-frontend-provider-contract.md","status":"accepted","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[{"id":"DEC-0040","file":"DEC-0040-frontendprovider-explicito-como-contrato-de-frontend.md","status":"accepted","created_at":"2026-07-15","updated_at":"2026-07-15","ref_agenda":"AGD-0057"}],"plans":[{"id":"PLN-0103","file":"PLN-0103-specify-frontend-provider-and-registry-contract.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0040"]},{"id":"PLN-0104","file":"PLN-0104-introduce-frontendprovider-registry-api.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0040"]},{"id":"PLN-0105","file":"PLN-0105-wrap-pbs-frontend-as-explicit-provider.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0040"]},{"id":"PLN-0106","file":"PLN-0106-migrate-common-consumers-to-provider-lookup.md","status":"done","created_at":"2026-07-15","updated_at":"2026-07-15","ref_decisions":["DEC-0040"]}],"lessons":[]}
{"type":"discussion","id":"DSC-0053","status":"open","ticket":"pbs-lsp-call-and-type-hierarchy","title":"PBS LSP Call and Type Hierarchy","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["studio","lsp","vscode","compiler-pbs","editor","call-hierarchy","type-hierarchy"],"agendas":[{"id":"AGD-0056","file":"AGD-0056-pbs-lsp-call-and-type-hierarchy.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0052","status":"open","ticket":"pbs-lsp-document-links","title":"PBS LSP Document Links","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["studio","lsp","vscode","compiler-pbs","editor","document-links","imports"],"agendas":[{"id":"AGD-0055","file":"AGD-0055-pbs-lsp-document-links.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}
{"type":"discussion","id":"DSC-0051","status":"open","ticket":"pbs-lsp-diagnostics-ux","title":"PBS LSP Diagnostics UX","created_at":"2026-07-15","updated_at":"2026-07-15","tags":["studio","lsp","vscode","compiler-pbs","editor","diagnostics","ux"],"agendas":[{"id":"AGD-0054","file":"AGD-0054-pbs-lsp-diagnostics-ux.md","status":"open","created_at":"2026-07-15","updated_at":"2026-07-15"}],"decisions":[],"plans":[],"lessons":[]}

View File

@ -2,7 +2,7 @@
id: AGD-0057
ticket: multi-frontend-provider-contract
title: Introduzir provider completo de frontend
status: open
status: accepted
created: 2026-07-15
resolved:
decision:
@ -77,3 +77,18 @@ Comecar por uma auditoria de chamadas atuais ao registry e de instanciacoes dire
- Responsabilidades minimas do provider.
- Onde fica o composition root autorizado a registrar PBS.
## Resolucao aceita
O contrato deve ser introduzido como `FrontendProvider`.
Responsabilidades minimas:
- expor a especificacao do frontend por `specification()`;
- expor o compilador ou fase de compilacao equivalente por `compiler()`;
- expor servicos editoriais por `languageService()` somente como capacidade opcional, quando necessaria para o Studio.
O contrato nao deve incluir descoberta dinamica de plugins, reflection, classpath scanning ou carregamento externo de JARs.
O registro inicial deve continuar explicito no composition root da aplicacao, com `prometeu-app/src/main/java/p/studio/AppContainer.java` como ponto preferencial de composicao. Codigo comum deve consumir o provider via `registry.require(languageId)` e nao deve instanciar diretamente classes PBS, incluindo `PBSFrontendPhaseService`.
PBS permanece o unico provider inicial. Nenhum frontend real novo deve ser criado por esta frente.

View File

@ -0,0 +1,92 @@
---
id: DEC-0040
ticket: multi-frontend-provider-contract
title: FrontendProvider explicito como contrato de frontend
status: accepted
created: 2026-07-15
ref_agenda: AGD-0057
tags: [compiler, compiler-general, studio, frontend, registry, multi-frontend]
---
## Context
This decision belongs to domain owner `compiler/general`, with impact on `studio`.
The repository already has `FrontendRegistryService`, `FrontendSpec`, `FrontendPhaseService`, and `languageId` based selection. Those surfaces are enough to keep PBS working, but common compiler and Studio code still has places where PBS-specific classes or assumptions can leak into composition and consumption paths.
The multi-frontend direction requires a stable provider boundary before removing PBS-specific branches from common code. The boundary must make frontend registration explicit without turning the Studio or compiler into a dynamic plugin platform.
This decision resolves `AGD-0057`.
## Decision
The repository MUST introduce an explicit `FrontendProvider` contract as the common frontend registration unit.
`FrontendProvider` MUST expose the following minimum responsibilities:
- `specification()`: returns the frontend specification, including its stable `languageId`;
- `compiler()`: returns the compiler-facing frontend service or phase equivalent used by the build pipeline;
- `languageService()`: returns an optional editor-facing language service capability for Studio consumers.
`languageService()` MUST be optional. A frontend that can compile but does not provide editor services MUST remain a valid provider.
`FrontendRegistryService` MUST resolve providers by `languageId`. The common consumption shape SHOULD become `provider = registry.require(languageId)` or an equivalent explicit lookup that fails clearly for unknown languages.
PBS MUST be registered as the only initial real provider through a `PBSFrontendProvider` or equivalent concrete provider object. This work MUST NOT introduce another real frontend.
Provider registration MUST remain explicit in the application composition root. `prometeu-app/src/main/java/p/studio/AppContainer.java` is the preferred initial composition point for registering the PBS provider.
Common compiler, build pipeline, and Studio code MUST NOT instantiate PBS-specific frontend services directly. In particular, common code MUST NOT directly instantiate `PBSFrontendPhaseService`; it MUST obtain the relevant service through the provider resolved from the registry.
This decision MUST NOT introduce dynamic plugin discovery, reflection-based discovery, classpath scanning, external JAR loading, or runtime plugin installation.
## Rationale
An explicit provider object gives the project one stable unit for frontend identity, compilation, and optional editorial capability. It keeps `languageId` as the selection key while avoiding scattered direct knowledge of PBS in common code.
Keeping the provider small prevents a premature plugin architecture. The immediate goal is static multi-frontend readiness inside the repository, not external extensibility.
Making language services optional separates build correctness from Studio editor assistance. A frontend should be able to participate in compilation without being forced to implement editor features in the same wave.
Using the application composition root for registration keeps ownership clear: composition may know that PBS exists, but shared compiler and Studio consumers should only know the provider contract.
## Implications
- `FrontendRegistryService` will need to evolve from spec/phase lookups into provider lookup, while preserving compatibility during migration where necessary.
- Existing PBS registration must be wrapped behind a provider object.
- Common consumers that currently request `FrontendSpec` or `FrontendPhaseService` directly may be migrated incrementally to provider lookup.
- Studio catalog code may continue listing specifications, but its source should become provider-derived rather than PBS-specific.
- Build pipeline code should obtain compiler-facing frontend behavior from the resolved provider.
- Error handling for unknown `languageId` must remain explicit and diagnostic-friendly.
- No product or runtime behavior should change for PBS as the only registered provider.
## Propagation Targets
- specs:
- Add or update a `compiler/general` specification for the frontend provider and registry contract.
- Reference Studio-facing optional language service capability where relevant.
- plans:
- Create an implementation plan for provider contract introduction, PBS provider registration, consumer migration, and tests.
- code:
- `prometeu-compiler/prometeu-frontend-registry/src/main/java/p/studio/compiler/FrontendRegistryService.java`
- PBS frontend module provider implementation.
- `prometeu-compiler/prometeu-build-pipeline/...` consumers that resolve frontend services.
- `prometeu-studio/src/main/java/p/studio/projects/ProjectLanguageCatalogService.java`
- `prometeu-app/src/main/java/p/studio/AppContainer.java`
- tests:
- Registry resolves a provider by `languageId`.
- PBS is registered as the only initial real provider.
- Unknown `languageId` fails with a clear error.
- Common code can obtain compiler-facing frontend behavior without directly instantiating `PBSFrontendPhaseService`.
- docs:
- Decision-derived implementation plan.
- Lessons after implementation and housekeeping, if the completed work produces reusable guidance.
## References
- Agenda: AGD-0057
- Source discussion: DSC-0054
## Revision Log
- 2026-07-15: Initial decision drafted from accepted `AGD-0057`.

View File

@ -0,0 +1,71 @@
---
id: PLN-0103
ticket: multi-frontend-provider-contract
title: Specify frontend provider and registry contract
status: done
created: 2026-07-15
ref_decisions: [DEC-0040]
tags: [compiler, compiler-general, studio, frontend, registry, multi-frontend]
---
## Briefing
DEC-0040 locks an explicit `FrontendProvider` contract as the common frontend registration unit. The first implementation step is to make that contract normative in the compiler/general specs before changing code.
Domain owner: `compiler/general`, with impact on `studio`.
## Objective
Specify the frontend provider and registry contract so implementation can introduce the API without relying on implicit behavior or PBS-specific assumptions.
## Dependencies
- Accepted decision: `DEC-0040`.
- Existing compiler specs under `docs/specs/compiler/`.
- Existing Studio/editor references may be linked but must not become the owner of the contract.
## Scope
- Add or update a compiler/general spec section defining `FrontendProvider`.
- Define required provider responsibilities:
- `specification()`;
- `compiler()`;
- optional `languageService()`.
- Define `languageId` as the registry lookup key.
- Define explicit provider registration as the only supported v1 registration model.
- Define PBS as the only initial real provider.
- Define unknown-language failure behavior as explicit and diagnostic-friendly.
- Define that common code must obtain compiler-facing frontend behavior through provider lookup.
## Non-Goals
- Do not specify dynamic plugin discovery.
- Do not specify reflection, classpath scanning, external JAR loading, or runtime plugin installation.
- Do not introduce a second real frontend.
- Do not define the full Studio language-service API beyond optional provider capability.
## Execution Method
1. Inspect existing compiler/general spec organization and choose the nearest owner document for registry and frontend composition.
2. Add a `FrontendProvider` section with normative `MUST` language for required and optional responsibilities.
3. Add a registry section stating that providers are resolved by `languageId` and that unknown languages fail clearly.
4. Add a registration section stating that provider registration is explicit and composition-root-owned.
5. Add a PBS v1 section stating that PBS is the only initial real provider and no new frontend is introduced by this work.
6. Cross-reference Studio only as an optional language-service consumer, not as the owner of the provider contract.
## Acceptance Criteria
- A compiler/general spec defines `FrontendProvider`, registry lookup, explicit registration, and PBS-only initial provider status.
- The spec prohibits plugin discovery, reflection discovery, scanning, external JAR loading, and runtime plugin installation for this decision.
- The spec makes `languageService()` optional and does not require editor support for a compilable frontend.
- The spec is written in English.
## Tests
- Documentation validation through review against `DEC-0040`.
- No code tests are required in this plan.
## Affected Artifacts
- `docs/specs/compiler/...`
- `discussion/workflow/decisions/DEC-0040-frontendprovider-explicito-como-contrato-de-frontend.md`

View File

@ -0,0 +1,69 @@
---
id: PLN-0104
ticket: multi-frontend-provider-contract
title: Introduce FrontendProvider registry API
status: done
created: 2026-07-15
ref_decisions: [DEC-0040]
tags: [compiler, compiler-general, studio, frontend, registry, multi-frontend]
---
## Briefing
DEC-0040 requires common code to resolve frontend behavior through a provider. This plan introduces the API surface in the frontend registry without forcing all consumers to migrate in the same change.
Domain owner: `compiler/general`.
## Objective
Introduce `FrontendProvider` and provider-based registry lookup while preserving existing PBS behavior during migration.
## Dependencies
- `PLN-0103` should define the normative spec first.
- Accepted decision: `DEC-0040`.
- Existing registry module: `prometeu-compiler/prometeu-frontend-registry`.
## Scope
- Add the `FrontendProvider` contract in the frontend registry module.
- Add an optional language-service return shape, using an existing interface if one already exists or a minimal neutral placeholder only if required.
- Add provider registration and provider lookup by `languageId`.
- Add `require(languageId)` or equivalent explicit lookup that fails clearly for unknown languages.
- Preserve compatibility methods during migration if existing consumers still call spec or phase lookup.
## Non-Goals
- Do not register PBS in this plan unless needed for API tests with a simple fixture.
- Do not migrate build pipeline or Studio consumers in this plan.
- Do not add plugin discovery, reflection, scanning, external JAR loading, or runtime plugin installation.
- Do not define a broad editor API.
## Execution Method
1. Inspect `FrontendRegistryService`, `FrontendSpec`, and `FrontendPhaseService` to confirm current method names and package ownership.
2. Add `FrontendProvider` to the registry-facing package with `specification()`, `compiler()`, and optional `languageService()`.
3. Update `FrontendRegistryService` so its primary storage is provider keyed by `languageId`.
4. Add `require(languageId)` or the nearest local naming equivalent for provider lookup.
5. Keep old spec/phase accessor methods as provider-derived compatibility wrappers if they are still used.
6. Add registry unit tests for provider registration, provider lookup, duplicate language handling if currently supported, and unknown-language errors.
## Acceptance Criteria
- `FrontendProvider` exists and exposes all responsibilities required by `DEC-0040`.
- `FrontendRegistryService` can resolve a provider by `languageId`.
- Unknown `languageId` lookup fails with a clear exception or diagnostic message.
- Existing callers can still compile until their migration plan runs.
## Tests
- Registry test: registered provider is returned by `languageId`.
- Registry test: unknown `languageId` fails clearly.
- Registry test: provider-derived spec listing remains correct if compatibility methods remain.
- Run the relevant Gradle test task for `prometeu-frontend-registry`.
## Affected Artifacts
- `prometeu-compiler/prometeu-frontend-registry/src/main/java/p/studio/compiler/FrontendRegistryService.java`
- New `FrontendProvider` contract in the same registry API surface.
- Registry tests under `prometeu-compiler/prometeu-frontend-registry/src/test/...`

View File

@ -0,0 +1,73 @@
---
id: PLN-0105
ticket: multi-frontend-provider-contract
title: Wrap PBS frontend as explicit provider
status: done
created: 2026-07-15
ref_decisions: [DEC-0040]
tags: [compiler, compiler-general, studio, frontend, registry, multi-frontend]
---
## Briefing
DEC-0040 requires PBS to become the only initial real provider behind the new `FrontendProvider` contract. This plan wraps existing PBS frontend behavior without changing PBS product behavior.
Domain owner: `compiler/general`, with implementation in `compiler/pbs` and composition impact in `prometeu-app`.
## Objective
Create and register an explicit PBS provider through the application composition root.
## Dependencies
- `PLN-0104` must introduce `FrontendProvider` and provider lookup first.
- Accepted decision: `DEC-0040`.
- Existing PBS frontend phase/service implementation.
- Application composition root in `prometeu-app`.
## Scope
- Add `PBSFrontendProvider` or equivalent concrete provider object.
- Make the provider return the existing PBS `FrontendSpec`.
- Make the provider return the existing PBS compiler-facing service or phase.
- Return no language service, or the existing PBS language-service adapter only if already present and compatible.
- Register PBS explicitly in `prometeu-app/src/main/java/p/studio/AppContainer.java` or the nearest current application composition point.
- Ensure PBS remains the only initial real provider.
## Non-Goals
- Do not create another real frontend.
- Do not change PBS parsing, semantics, lowering, or runtime output.
- Do not migrate all common consumers in this plan.
- Do not introduce dynamic provider discovery.
## Execution Method
1. Inspect PBS frontend module boundaries and locate the current `FrontendSpec` and `FrontendPhaseService` construction.
2. Add `PBSFrontendProvider` in the PBS frontend module or an existing integration package that can legally depend on PBS.
3. Implement `specification()` and `compiler()` by delegating to the existing PBS objects.
4. Implement `languageService()` as empty unless a current Studio-compatible PBS language service already exists.
5. Update `AppContainer` or the existing composition root to register the PBS provider explicitly with `FrontendRegistryService`.
6. Remove direct PBS registration paths that duplicate the provider registration if they become redundant.
7. Add tests proving only PBS is registered as the real provider in the default composition path.
## Acceptance Criteria
- PBS is represented by a concrete provider object.
- Default application composition registers PBS explicitly as a provider.
- PBS remains functional through existing compile/build flows.
- No additional real frontend is introduced.
- No dynamic discovery mechanism is added.
## Tests
- Provider test: `PBSFrontendProvider.specification()` returns the PBS language id.
- Provider test: `PBSFrontendProvider.compiler()` returns working compiler-facing behavior.
- Composition test: default registry contains PBS as the only real provider.
- Existing PBS compile/build tests continue to pass.
## Affected Artifacts
- PBS frontend module under `prometeu-compiler/frontends/prometeu-frontend-pbs/...`
- `prometeu-app/src/main/java/p/studio/AppContainer.java`
- Registry or application composition tests.

View File

@ -0,0 +1,74 @@
---
id: PLN-0106
ticket: multi-frontend-provider-contract
title: Migrate common consumers to provider lookup
status: done
created: 2026-07-15
ref_decisions: [DEC-0040]
tags: [compiler, compiler-general, studio, frontend, registry, multi-frontend]
---
## Briefing
DEC-0040 forbids common compiler, build pipeline, and Studio code from directly instantiating PBS-specific frontend services. After the registry API and PBS provider exist, consumers must move to provider lookup.
Domain owner: `compiler/general`, with impact on `studio`.
## Objective
Migrate common consumers from direct spec/phase/PBS access to provider-based lookup by `languageId`.
## Dependencies
- `PLN-0104` must provide provider lookup.
- `PLN-0105` must register PBS as a provider.
- Accepted decision: `DEC-0040`.
## Scope
- Audit common compiler, build pipeline, Studio, and app code for direct PBS frontend service construction.
- Replace common code service lookup with provider lookup.
- Make build pipeline frontend execution obtain compiler-facing behavior from `provider.compiler()`.
- Make Studio project language catalog list specifications from providers.
- Keep PBS-specific construction inside PBS provider or composition root only.
- Add or update tests that prove common code does not directly instantiate `PBSFrontendPhaseService`.
## Non-Goals
- Do not remove compatibility registry APIs unless all callers have migrated and the removal is local.
- Do not introduce new frontend behavior.
- Do not change backend IR, validation boundaries, or lifecycle semantics beyond provider lookup.
- Do not implement optional Studio language services that do not already exist.
## Execution Method
1. Search for `PBSFrontendPhaseService`, PBS frontend constructors, `getFrontendPhaseService`, and `getFrontendSpec` usage outside PBS and composition-owned code.
2. Classify each usage as composition-owned, PBS-owned, or common consumer.
3. Update common consumers to resolve `FrontendProvider` by `languageId`.
4. Update `FrontendPhasePipelineStage` so it calls provider compiler behavior rather than direct phase lookup.
5. Update `ProjectLanguageCatalogService` so its catalog is provider-derived.
6. Keep compatibility wrappers only where they reduce migration risk and are provider-derived.
7. Add architectural tests or focused unit tests that fail if common modules instantiate `PBSFrontendPhaseService` directly.
8. Run relevant compiler, build pipeline, Studio, and app tests.
## Acceptance Criteria
- Common compiler/build pipeline/Studio code obtains frontend behavior through provider lookup.
- Direct `PBSFrontendPhaseService` instantiation remains only in PBS provider or approved composition code.
- `ProjectLanguageCatalogService` is provider-derived.
- Unknown `languageId` failures remain clear after consumer migration.
- PBS compile/build behavior remains unchanged.
## Tests
- Unit or integration test: build pipeline resolves PBS through provider and compiles an existing PBS fixture.
- Unit test: Studio language catalog lists PBS from provider specs.
- Architecture test or grep-backed test: common modules do not instantiate `PBSFrontendPhaseService`.
- Existing PBS and build pipeline tests pass.
## Affected Artifacts
- `prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/workspaces/stages/FrontendPhasePipelineStage.java`
- `prometeu-studio/src/main/java/p/studio/projects/ProjectLanguageCatalogService.java`
- Common code currently using `FrontendRegistryService.getFrontendSpec` or `getFrontendPhaseService`.
- Tests in compiler, build pipeline, Studio, and app modules.

View File

@ -14,6 +14,7 @@ Its purpose is to make the compiler usable through explicit entrypoints instead
This document defines:
- the canonical stage order shared by compiler entrypoints,
- the frontend provider and registry boundary used by shared compiler composition,
- the terminal stage and mandatory behavior of `analyze`,
- the terminal stage and mandatory behavior of `compile`,
- the terminal stage and mandatory behavior of `build`,
@ -25,6 +26,7 @@ This document does not define:
- one mandatory Java class or package layout,
- editor-specific source providers or document-session providers,
- frontend-specific semantic facts beyond the minimum shared `AnalysisSnapshot` contract,
- dynamic frontend plugin discovery,
- or one mandatory CLI architecture.
## 3. Authority and Precedence
@ -229,6 +231,26 @@ This authority model MUST NOT:
2. move semantic inline hint policy into LSP,
3. or imply that the host editor may synthesize semantic hints when the frontend did not publish them.
## 8.3 Frontend Provider and Registry Contract
The shared compiler composition surface MUST represent each registered frontend through a `FrontendProvider` or equivalent provider contract.
At minimum, each provider MUST expose:
1. `specification()`, returning the frontend-owned static contract, including its stable `languageId`;
2. `compiler()`, returning the compiler-facing frontend service used by the shared build pipeline;
3. `languageService()`, returning an optional editor-facing language-service capability for tooling consumers.
`languageService()` MUST be optional. A frontend that supports compilation but does not provide editor services MUST remain a valid provider.
The frontend registry MUST resolve providers by `languageId`. A lookup for an unknown `languageId` MUST fail explicitly with a diagnostic-friendly error. Unknown languages MUST NOT silently fall back to PBS or to any other frontend.
Common compiler, build pipeline, and Studio-facing consumers MUST obtain frontend behavior through provider lookup. They MUST NOT directly instantiate frontend-specific compiler services, except inside the frontend provider itself or inside an approved application composition root.
Provider registration MUST be explicit in v1. The registry contract MUST NOT use dynamic plugin discovery, reflection-based discovery, classpath scanning, external JAR loading, or runtime plugin installation to discover frontends.
PBS is the only initial real provider for this contract. PBS MAY be registered by an application composition root, but PBS MUST NOT become an exclusive semantic owner of the shared compiler pipeline.
## 9. Public Surface Rule
The shared compiler public surface MUST expose explicit entrypoints equivalent in meaning to:

View File

@ -7,6 +7,8 @@ dependencies {
implementation(project(":prometeu-infra"))
implementation(project(":prometeu-lsp:prometeu-lsp-api"))
implementation(project(":prometeu-lsp:prometeu-lsp-v1"))
implementation(project(":prometeu-compiler:frontends:prometeu-frontend-pbs"))
implementation(project(":prometeu-compiler:prometeu-frontend-registry"))
implementation(project(":prometeu-packer:prometeu-packer-api"))
implementation(project(":prometeu-packer:prometeu-packer-v1"))
implementation(project(":prometeu-studio"))

View File

@ -2,6 +2,7 @@ package p.studio;
import com.fasterxml.jackson.databind.ObjectMapper;
import p.packer.Packer;
import p.studio.compiler.FrontendRegistryService;
import p.studio.lsp.api.LspServerLifecycle;
import p.studio.lsp.events.StudioEventBus;
import p.studio.lsp.events.StudioPackerEventAdapter;
@ -24,6 +25,7 @@ public final class AppContainer implements Container {
private final LspServerLifecycle lspServerLifecycle;
public AppContainer() {
FrontendRegistryService.bootstrapDefaults();
this.i18nService = new I18nService();
this.themeService = new ThemeService();
this.studioEventBus = new StudioEventBus();

View File

@ -0,0 +1,28 @@
package p.studio.compiler;
import p.studio.compiler.models.FrontendSpec;
import p.studio.compiler.services.FrontendPhaseService;
import p.studio.compiler.services.FrontendProvider;
import p.studio.compiler.services.PBSFrontendPhaseService;
public final class PBSFrontendProvider implements FrontendProvider {
private final FrontendPhaseService compiler;
public PBSFrontendProvider() {
this(new PBSFrontendPhaseService());
}
PBSFrontendProvider(final FrontendPhaseService compiler) {
this.compiler = compiler;
}
@Override
public FrontendSpec specification() {
return PBSDefinitions.PBS;
}
@Override
public FrontendPhaseService compiler() {
return compiler;
}
}

View File

@ -0,0 +1,20 @@
package p.studio.compiler;
import org.junit.jupiter.api.Test;
import p.studio.compiler.services.PBSFrontendPhaseService;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
class PBSFrontendProviderTest {
@Test
void shouldExposePbsSpecificationAndCompiler() {
final var provider = new PBSFrontendProvider();
assertEquals(PBSDefinitions.PBS.getLanguageId(), provider.specification().getLanguageId());
assertInstanceOf(PBSFrontendPhaseService.class, provider.compiler());
assertFalse(provider.languageService().isPresent());
}
}

View File

@ -17,13 +17,7 @@ public class FrontendPhasePipelineStage implements PipelineStage {
@Override
public BuildingIssueSink run(final BuilderPipelineContext ctx, final LogAggregator logs) {
final var frontedSpec = ctx.resolvedWorkspace.frontendSpec();
final var service = FrontendRegistryService.getFrontendPhaseService(frontedSpec.getLanguageId());
if (service.isEmpty()) {
return BuildingIssueSink.empty()
.report(builder -> builder
.error(true)
.message("[BUILD]: unable to find a service for frontend phase: " + frontedSpec.getLanguageId()));
}
final var provider = FrontendRegistryService.require(frontedSpec.getLanguageId());
final var projectTable = ctx.resolvedWorkspace.graph().projectTable();
final var fileTable = ctx.fileTable;
final var frontendPhaseContext = new FrontendPhaseContext(
@ -36,7 +30,7 @@ public class FrontendPhasePipelineStage implements PipelineStage {
assetSurfaceContextLoader.load(ctx.resolvedWorkspace.mainProject().getRootPath()));
final var diagnostics = DiagnosticSink.empty();
final var issues = BuildingIssueSink.empty();
ctx.irBackend = service.get().compile(frontendPhaseContext, diagnostics, logs, issues);
ctx.irBackend = provider.compiler().compile(frontendPhaseContext, diagnostics, logs, issues);
logs.using(log).debug("Frontend phase completed successfully!");
adaptDiagnostics(diagnostics, issues);
return issues;

View File

@ -0,0 +1,59 @@
package p.studio.compiler.specs;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import static org.junit.jupiter.api.Assertions.fail;
class FrontendProviderBoundaryTest {
@Test
void commonModulesMustNotInstantiatePbsFrontendPhaseServiceDirectly() throws IOException {
final var repoRoot = findRepoRoot(Path.of("").toAbsolutePath().normalize());
final var commonSourceRoots = List.of(
repoRoot.resolve("prometeu-compiler/prometeu-build-pipeline/src/main/java"),
repoRoot.resolve("prometeu-compiler/prometeu-deps/src/main/java"),
repoRoot.resolve("prometeu-studio/src/main/java"),
repoRoot.resolve("prometeu-app/src/main/java"));
for (final var sourceRoot : commonSourceRoots) {
if (!Files.exists(sourceRoot)) {
continue;
}
try (final var files = Files.walk(sourceRoot)) {
final var violations = files
.filter(path -> path.toString().endsWith(".java"))
.filter(path -> contains(path, "new PBSFrontendPhaseService("))
.toList();
if (!violations.isEmpty()) {
fail("common modules must resolve frontend compiler behavior through FrontendProvider: " + violations);
}
}
}
}
private boolean contains(final Path path, final String needle) {
try {
return Files.readString(path).contains(needle);
} catch (IOException e) {
throw new IllegalStateException("failed to read " + path, e);
}
}
private Path findRepoRoot(final Path start) {
var current = start;
while (current != null) {
if (Files.exists(current.resolve("settings.gradle.kts"))) {
return current;
}
current = current.getParent();
}
fail("unable to locate repository root from " + start);
return start;
}
}

View File

@ -60,7 +60,7 @@ public final class PrometeuManifestUtils {
.message("[DEPS]: invalid 'target' \"" + dto.target() + "\": " + manifestPathCanon));
}
final var language = StringUtils.isBlank(dto.language())
? FrontendRegistryService.getDefaultFrontendSpec().getLanguageId()
? FrontendRegistryService.getDefaultProvider().specification().getLanguageId()
: dto.language();
final var dependencies = DependencyResolver.resolveDependencies(mainProjectRootPathCanon, dto.dependencies(), issuesLocal);
if (ReadOnlyCollection.isNotEmpty(issuesLocal)) {

View File

@ -55,23 +55,24 @@ public class DiscoverPhase implements DependencyPhase {
ctx.rootStdlib = manifest.get().stdlib();
ctx.target = manifest.get().target();
}
final var frontendSpec = FrontendRegistryService.getFrontendSpec(manifest.get().language());
final var frontendProvider = FrontendRegistryService.getProvider(manifest.get().language());
// Returns empty when language is unknown
if (frontendSpec.isEmpty()) {
if (frontendProvider.isEmpty()) {
issues.report(builder -> builder
.error(true)
.message("[DEPS]: unknown language " + manifest.get().language() + " for project " + manifest.get().name()));
return Optional.empty();
}
final var frontendSpec = frontendProvider.get().specification();
// Enforces consistent language across projects
if (Objects.isNull(ctx.frontendSpec)) {
ctx.frontendSpec = frontendSpec.get();
} else if (!ctx.frontendSpec.getLanguageId().equals(frontendSpec.get().getLanguageId())) {
ctx.frontendSpec = frontendSpec;
} else if (!ctx.frontendSpec.getLanguageId().equals(frontendSpec.getLanguageId())) {
// Reports language inconsistency across projects
issues.report(builder -> builder
.error(true)
.message(String.format("[DEPS]: inconsistent language: [ %s ] has \"%s\" but should be \"%s\"",
manifest.get().name(), frontendSpec.get().getLanguageId(), ctx.frontendSpec.getLanguageId())));
manifest.get().name(), frontendSpec.getLanguageId(), ctx.frontendSpec.getLanguageId())));
return Optional.empty();
}
final var projectInfo = ProjectInfo

View File

@ -5,5 +5,5 @@ plugins {
dependencies {
api(project(":prometeu-infra"))
implementation(project(":prometeu-compiler:prometeu-compiler-core"))
}
api(project(":prometeu-compiler:prometeu-compiler-core"))
}

View File

@ -0,0 +1,7 @@
package p.studio.compiler.services;
/**
* Marker contract for optional frontend-owned editor services.
*/
public interface FrontendLanguageService {
}

View File

@ -0,0 +1,15 @@
package p.studio.compiler.services;
import p.studio.compiler.models.FrontendSpec;
import java.util.Optional;
public interface FrontendProvider {
FrontendSpec specification();
FrontendPhaseService compiler();
default Optional<FrontendLanguageService> languageService() {
return Optional.empty();
}
}

View File

@ -6,7 +6,7 @@ dependencies {
implementation(project(":prometeu-infra"))
implementation(project(":prometeu-compiler:prometeu-compiler-core"))
implementation(project(":prometeu-compiler:prometeu-frontend-api"))
api(project(":prometeu-compiler:prometeu-frontend-api"))
implementation(project(":prometeu-compiler:frontends:prometeu-frontend-pbs"))
}
}

View File

@ -2,7 +2,7 @@ package p.studio.compiler;
import p.studio.compiler.models.FrontendSpec;
import p.studio.compiler.services.FrontendPhaseService;
import p.studio.compiler.services.PBSFrontendPhaseService;
import p.studio.compiler.services.FrontendProvider;
import java.util.HashMap;
import java.util.List;
@ -10,29 +10,68 @@ import java.util.Map;
import java.util.Optional;
public class FrontendRegistryService {
private static final Map<String, FrontendSpec> FRONTEND_SPECS = new HashMap<>();
private static final Map<String, FrontendPhaseService> FRONTEND_PHASE_SERVICES = new HashMap<>();
private static final Map<String, FrontendProvider> FRONTEND_PROVIDERS = new HashMap<>();
static {
FRONTEND_SPECS.put(PBSDefinitions.PBS.getLanguageId(), PBSDefinitions.PBS);
FRONTEND_PHASE_SERVICES.put(PBSDefinitions.PBS.getLanguageId(), new PBSFrontendPhaseService());
bootstrapDefaults();
}
public static void bootstrapDefaults() {
registerIfAbsent(new PBSFrontendProvider());
}
public static FrontendSpec getDefaultFrontendSpec() {
return FRONTEND_SPECS.get(PBSDefinitions.PBS.getLanguageId());
return getDefaultProvider().specification();
}
public static FrontendProvider getDefaultProvider() {
return require(PBSDefinitions.PBS.getLanguageId());
}
public static Optional<FrontendSpec> getFrontendSpec(final String languageId) {
return Optional.ofNullable(FRONTEND_SPECS.get(languageId));
return getProvider(languageId).map(FrontendProvider::specification);
}
public static Optional<FrontendPhaseService> getFrontendPhaseService(final String languageId) {
return Optional.ofNullable(FRONTEND_PHASE_SERVICES.get(languageId));
return getProvider(languageId).map(FrontendProvider::compiler);
}
public static Optional<FrontendProvider> getProvider(final String languageId) {
return Optional.ofNullable(FRONTEND_PROVIDERS.get(languageId));
}
public static FrontendProvider require(final String languageId) {
final var provider = FRONTEND_PROVIDERS.get(languageId);
if (provider == null) {
throw new IllegalArgumentException("Unknown frontend languageId: " + languageId);
}
return provider;
}
public static List<FrontendSpec> listFrontendSpecs() {
return FRONTEND_SPECS.values().stream()
return FRONTEND_PROVIDERS.values().stream()
.map(FrontendProvider::specification)
.sorted((left, right) -> left.getLanguageId().compareToIgnoreCase(right.getLanguageId()))
.toList();
}
public static List<FrontendProvider> listProviders() {
return FRONTEND_PROVIDERS.values().stream()
.sorted((left, right) -> left.specification().getLanguageId().compareToIgnoreCase(right.specification().getLanguageId()))
.toList();
}
static void register(final FrontendProvider provider) {
final var languageId = provider.specification().getLanguageId();
if (FRONTEND_PROVIDERS.containsKey(languageId)) {
throw new IllegalArgumentException("Duplicate frontend languageId: " + languageId);
}
FRONTEND_PROVIDERS.put(languageId, provider);
}
private static void registerIfAbsent(final FrontendProvider provider) {
FRONTEND_PROVIDERS.putIfAbsent(provider.specification().getLanguageId(), provider);
}
}

View File

@ -0,0 +1,44 @@
package p.studio.compiler;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
class FrontendRegistryServiceTest {
@Test
void requireShouldResolveProviderByLanguageId() {
final var provider = FrontendRegistryService.require(PBSDefinitions.PBS.getLanguageId());
assertEquals(PBSDefinitions.PBS.getLanguageId(), provider.specification().getLanguageId());
assertSame(PBSDefinitions.PBS, provider.specification());
}
@Test
void requireShouldFailClearlyForUnknownLanguageId() {
final var thrown = assertThrows(
IllegalArgumentException.class,
() -> FrontendRegistryService.require("unknown-language"));
assertTrue(thrown.getMessage().contains("unknown-language"));
}
@Test
void specListingShouldBeProviderDerived() {
final var specs = FrontendRegistryService.listFrontendSpecs();
assertEquals(1, specs.size());
assertEquals(PBSDefinitions.PBS.getLanguageId(), specs.getFirst().getLanguageId());
}
@Test
void providerListingShouldExposeOnlyPbsByDefault() {
final var providers = FrontendRegistryService.listProviders();
assertEquals(1, providers.size());
assertEquals(PBSDefinitions.PBS.getLanguageId(), providers.getFirst().specification().getLanguageId());
}
}

View File

@ -9,8 +9,8 @@ import java.util.Objects;
public final class ProjectLanguageCatalogService {
public List<ProjectLanguageTemplate> listLanguageTemplates() {
return FrontendRegistryService.listFrontendSpecs().stream()
.map(this::toTemplate)
return FrontendRegistryService.listProviders().stream()
.map(provider -> toTemplate(provider.specification()))
.sorted(Comparator.comparing(ProjectLanguageTemplate::languageId, String.CASE_INSENSITIVE_ORDER))
.toList();
}

View File

@ -0,0 +1,18 @@
package p.studio.projects;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class ProjectLanguageCatalogServiceTest {
@Test
void shouldListLanguagesFromRegisteredProviders() {
final var service = new ProjectLanguageCatalogService();
final var templates = service.listLanguageTemplates();
assertEquals(1, templates.size());
assertEquals("pbs", templates.getFirst().languageId());
}
}

View File

@ -14,13 +14,13 @@
"last_modified" : 1779659315450,
"fingerprint" : "ac565160c0dceb321b8f6c9094f2b5e6ae398396fe32c8ead706f5cc36f465cf",
"metadata" : {
"palette" : {
"rgba8888" : [ 0, 673720574, -523190018, -117903106, 945858814, -1600620290, 1484833022 ]
},
"tile" : {
"width" : 32,
"height" : 32,
"paletteIndices" : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAgICAgICAQAAAAAAAAAAAAABAQEAAAAAAAAAAAAAAAEDBAQEBAQCAQAAAAAAAQEBAQICAgEAAAAAAAAAAAAAAQMEAgUFBQQCAQEBAQECAgICBAQEAgEAAAAAAAAAAAABAwQCBQUFBQQDAwMDAgQEBAQFAgIEAwEBAAAAAAAAAAEDBAIFBQUFAgQEBgYEBQUFBQICAgIEAwMBAQAAAAAAAQMEAgUFBQICAgMDAwIFBQUFBQICAgIGBgMCAQAAAAABAwQCBQUFAgICAgMDAgUFBQUFAgICAwMDBAQCAQEAAAEDBAIFBQUCAgICAwMCBQUFBQUCAgICAwMCAgQFAQAAAQMEAgUFBQICAgIDAwIFBQUFBAQEBAQDAwQEBQEAAAABAwQCBQUFAgICAgMDAgUFBAQFAgICAgYGAgUBAAAAAAEDBAIEBAQEBAICAwMCBQQFBQEBAQEBAwMBAQAAAAAAAQMEBAUFBQUCBAIDAwIEBQEBAAAAAAABAQAAAAAAAAABAwICBQEBAQECBAYGBAUBAAAAAAAAAAAAAAAAAAAAAQECAgIBAAAAAAEDAwICAQAAAAAAAAAAAAAAAAAAAAEBAQEBAQAAAAAAAAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
},
"palette" : {
"rgba8888" : [ 0, 673720574, -523190018, -117903106, 945858814, -1600620290, 1484833022 ]
}
},
"diagnostics" : [ ]
@ -59,13 +59,13 @@
"last_modified" : 1779659315452,
"fingerprint" : "6dd024eb2259efaa03908ff0c3b75eb67f9da56d365e4a35d8e619b71c7507db",
"metadata" : {
"palette" : {
"rgba8888" : [ 0, 255, -540033025, -134746113, -101191169, 945858815, 1501544959, -1617463297, -1583777281, -1583908353, -1617462785, 929015807, 962701823, 690432511 ]
},
"tile" : {
"width" : 32,
"height" : 32,
"paletteIndices" : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAgMDBAEBAQEBAAAAAAAAAAAAAAABAQAAAAAAAAAAAAECBQYGBAIHBwgBAAAAAAAAAAEBAQICAQEAAAAAAAAAAQIFAwMGBQUFBQIBAAAAAAEBCQIHBQUDBAEBAAAAAAABAgUDAwIICAoIBQIBAQEBBwcLCwwIAgYGAwMBAAAAAAECBQMDAgoKCgoKBQMDAwIFBQgCCgIKAgIGAwEAAAAAAQIFAwMCCAgICAgCBQYGBQoKCgoCCgICAwMGAwEAAAABAgUDAggICAgICAICAwMCCAgIAggFBQUGBgYDDQAAAAECBgMCCAgICAgIAgIDAggICAgFBQoCAwMDAwQEAQAAAQIGAwIICAgICAgCAgMCCAgIBQgKAQEBAQEBAQQBAAABAgYDAggICAgIAgICAwIKCAUIAQEAAAAAAAAAAQEAAAECBgMCCAgICAUFAgIDAggFCgEAAAAAAAAAAAAAAAAAAQIGAwIICAUFCAIFBQYLBQgBAAAAAAAAAAAAAAAAAAABAgYFBQUFCAoBAQIDBAIJAQAAAAAAAAAAAAAAAAAAAQECAwIICAgBAQAAAQEBAQEAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
},
"palette" : {
"rgba8888" : [ 0, 255, -540033025, -134746113, -101191169, 945858815, 1501544959, -1617463297, -1583777281, -1583908353, -1617462785, 929015807, 962701823, 690432511 ]
}
},
"diagnostics" : [ ]
@ -76,13 +76,13 @@
"last_modified" : 1779659315453,
"fingerprint" : "c8e30f40473deedb265be26967f6bf5f2ba35a2bd5d0a049dcb8843fa60d5600",
"metadata" : {
"palette" : {
"rgba8888" : [ 0, 538976511, 1368527103, 1907499263, -205315841, -1014890241, -1302257409, -202116097, -207461889, -476950017, 1631658239, -1300082177 ]
},
"tile" : {
"width" : 32,
"height" : 32,
"paletteIndices" : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQICAwMBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBAQBAgIDAwMCAQAAAAAAAAAAAAAAAAAAAAAAAAAAAQUFBAQBAgIDAgICAQAAAAAAAAAAAAAAAAAAAAAAAAABBQEFBAUBAgICAgMBAAAAAAAAAAAAAAAAAAAAAAAAAAABBgEFBQUBAgECAgMBAAAAAAAAAAAAAAAAAAAAAAAAAAEGBwEFBAEBCAECAwEAAAAAAAAAAAAAAAAAAAAAAAAAAQgBBwEEAQgJAQICAQAAAAAAAAAAAAAAAAAAAAAAAAEICAEHAQQBCQYBAgIBAAAAAAAAAAAAAAAAAAAAAAAAAAEGCQkBBQEGAQUBAgEAAAAAAAAAAAAAAAAAAAAAAAAAAAEBBgYBBgEBAQABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQcBAQEBAgIBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAECAwECAwIBAgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQECAQcHBwECAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBAEBAQEBAQECAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAgEJCgoBAgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEJCQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEKAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQELCwoKAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
},
"palette" : {
"rgba8888" : [ 0, 538976511, 1368527103, 1907499263, -205315841, -1014890241, -1302257409, -202116097, -207461889, -476950017, 1631658239, -1300082177 ]
}
},
"diagnostics" : [ ]
@ -93,13 +93,13 @@
"last_modified" : 1779659315454,
"fingerprint" : "58a6a9bc02d9174fdc6bcd6f71f5fb86c47d506812e83cae5b0e23a1f951dbbf",
"metadata" : {
"palette" : {
"rgba8888" : [ 0, 538976511, 1368527103, 1907499263, -205315841, -1014890241, -1302257409, -202116097, -207461889, -476950017, 1631658239, -1300082177 ]
},
"tile" : {
"width" : 32,
"height" : 32,
"paletteIndices" : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQECAgMDAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQEAQICAwMDAgEAAAAAAAAAAAAAAAAAAAAAAAAAAAEFBQQEAQICAwICAgEAAAAAAAAAAAAAAAAAAAAAAAAAAQUBBQQFAQICAgIDAQAAAAAAAAAAAAAAAAAAAAAAAAAAAQYBBQUFAQIBAgIDAQAAAAAAAAAAAAAAAAAAAAAAAAABBgcBBQQBAQgBAgMBAAAAAAAAAAAAAAAAAAAAAAAAAAEIAQcBBAEICQECAgEAAAAAAAAAAAAAAAAAAAAAAAABCAgBBwEEAQkGAQICAQAAAAAAAAAAAAAAAAAAAAAAAAABBgkJAQUBBgEFAQIBAAAAAAAAAAAAAAAAAAAAAAAAAAABAQYGAQYBAQEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEHAQEBAQECAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAgEBBwMBAwMBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBCQoBBwEDAgEBAAAAAAAAAAAAAAAAAAAAAAAAAAABAgEJCQoBAgIBAQIBAAAAAAAAAAAAAAAAAAAAAAAAAAECAgEBAQICAQECAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAgECAQECAgEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQICAgEBAQoKAQAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQELCwoBAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQsLAQEAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
},
"palette" : {
"rgba8888" : [ 0, 538976511, 1368527103, 1907499263, -205315841, -1014890241, -1302257409, -202116097, -207461889, -476950017, 1631658239, -1300082177 ]
}
},
"diagnostics" : [ ]
@ -110,13 +110,13 @@
"last_modified" : 1779659315454,
"fingerprint" : "110c4f835a40bb370c7bb7761afcd69de0d38e75798d20d50f4c203aa56f213a",
"metadata" : {
"palette" : {
"rgba8888" : [ 0, 538976511, 1368527103, 1907499263, -205315841, -1014890241, -1302257409, -202116097, -207461889, -476950017, 1631658239, -1300082177 ]
},
"tile" : {
"width" : 32,
"height" : 32,
"paletteIndices" : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQECAgMDAQEAAAEBAAAAAAAAAAAAAAAAAAAAAAABBAQEAQICAwMDAgEBAwEAAAAAAAAAAAAAAAAAAAAAAAEFBQQEAQICAwICAQIDAQAAAAAAAAAAAAAAAAAAAAAAAAEBBQQFAQICAgICAwEBAAAAAAAAAAAAAAAAAAAAAAAAAQYBBQUFAQIBAgICAQAAAAAAAAAAAAAAAAAAAAAAAAABBgcBBQQFAQgBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAEIAQcBBAUBCQEBBQUBAAAAAAAAAAAAAAAAAAAAAAABCAgBBwgBBAUBAQUBAAAAAAAAAAAAAAAAAAAAAAAAAAABBgkJBgYBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQYGBgYBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQMBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEJCgEHAwECAwEBAQAAAAAAAAAAAAAAAAAAAAAAAAABAQkKAQcCAQIDAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAEKAQEBAQECAgEBAgELAQAAAAAAAAAAAAAAAAAAAAAAAQEKAQICAQEBAgIBAQoLAQAAAAAAAAAAAAAAAAAAAAAAAQEBAQECAgICAQEAAQEAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
},
"palette" : {
"rgba8888" : [ 0, 538976511, 1368527103, 1907499263, -205315841, -1014890241, -1302257409, -202116097, -207461889, -476950017, 1631658239, -1300082177 ]
}
},
"diagnostics" : [ ]
@ -127,13 +127,13 @@
"last_modified" : 1779659315455,
"fingerprint" : "2e6fb2cfbf77f4b3c0d116b9b77a3ae228cdc5671bd18320285824faef9674d3",
"metadata" : {
"palette" : {
"rgba8888" : [ 0, 538976511, 1368527103, 1907499263, -205315841, -1014890241, -1302257409, -202116097, -207461889, -476950017, 1631658239, -1300082177 ]
},
"tile" : {
"width" : 32,
"height" : 32,
"paletteIndices" : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAEBAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQICAwMBAQEBAwEAAAAAAAAAAAAAAAAAAAAAAAABBAQBAgIDAwMCAQMDAQAAAAAAAAAAAAAAAAAAAAAAAQUFBAQBAgIDAgICAwIBAAAAAAAAAAAAAAAAAAAAAAABBQEFBAUBAgICAgMCAQAAAAAAAAAAAAAAAAAAAAAAAAABBgEFBQUBAgECAgIBAAAAAAAAAAAAAAAAAAAAAAAAAAEGBwEFBAUBCAEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAQgBBwEEBQEJAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAEICAEHCQEEBQEFAQAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBCQkGBgEBAQUBAAAAAAAAAAAAAAAAAAAAAAAAAAABCQoBAQEGBgEBAQUBAAAAAAAAAAAAAAAAAAAAAAAAAAEKCgEHAgEBAQMBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBBwICAQIDAwEBAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQECAgMBAQIBAAAAAAAAAAAAAAAAAAAAAAAAAQoBAQIBAgICAgECAQEAAAAAAAAAAAAAAAAAAAAAAAABCgoBAgIBAQEBAgELAQAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQICAgIBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
},
"palette" : {
"rgba8888" : [ 0, 538976511, 1368527103, 1907499263, -205315841, -1014890241, -1302257409, -202116097, -207461889, -476950017, 1631658239, -1300082177 ]
}
},
"diagnostics" : [ ]
@ -144,13 +144,13 @@
"last_modified" : 1779659315455,
"fingerprint" : "c8e30f40473deedb265be26967f6bf5f2ba35a2bd5d0a049dcb8843fa60d5600",
"metadata" : {
"palette" : {
"rgba8888" : [ 0, 538976511, 1368527103, 1907499263, -205315841, -1014890241, -1302257409, -202116097, -207461889, -476950017, 1631658239, -1300082177 ]
},
"tile" : {
"width" : 32,
"height" : 32,
"paletteIndices" : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQICAwMBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBAQBAgIDAwMCAQAAAAAAAAAAAAAAAAAAAAAAAAAAAQUFBAQBAgIDAgICAQAAAAAAAAAAAAAAAAAAAAAAAAABBQEFBAUBAgICAgMBAAAAAAAAAAAAAAAAAAAAAAAAAAABBgEFBQUBAgECAgMBAAAAAAAAAAAAAAAAAAAAAAAAAAEGBwEFBAEBCAECAwEAAAAAAAAAAAAAAAAAAAAAAAAAAQgBBwEEAQgJAQICAQAAAAAAAAAAAAAAAAAAAAAAAAEICAEHAQQBCQYBAgIBAAAAAAAAAAAAAAAAAAAAAAAAAAEGCQkBBQEGAQUBAgEAAAAAAAAAAAAAAAAAAAAAAAAAAAEBBgYBBgEBAQABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQcBAQEBAgIBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAECAwECAwIBAgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQECAQcHBwECAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBAEBAQEBAQECAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAgEJCgoBAgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEJCQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEKAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQELCwoKAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
},
"palette" : {
"rgba8888" : [ 0, 538976511, 1368527103, 1907499263, -205315841, -1014890241, -1302257409, -202116097, -207461889, -476950017, 1631658239, -1300082177 ]
}
},
"diagnostics" : [ ]
@ -161,13 +161,13 @@
"last_modified" : 1779659315456,
"fingerprint" : "94ac699b5e913925cf881e76ff951875147b598b1ee9870187111e7ca0b40d1d",
"metadata" : {
"palette" : {
"rgba8888" : [ 0, 538976511, 1368527103, 1907499263, -205315841, -1014890241, -1302257409, -202116097, -207461889, -476950017, 1631658239 ]
},
"tile" : {
"width" : 32,
"height" : 32,
"paletteIndices" : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQICAwMBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBAQBAgIDAwMCAQAAAAAAAAAAAAAAAAAAAAAAAAAAAQUFBAQBAgIDAgICAQAAAAAAAAAAAAAAAAAAAAAAAAABBQEFBAUBAgICAgMBAAAAAAAAAAAAAAAAAAAAAAAAAAABBgEFBQUBAgECAgMBAAAAAAAAAAAAAAAAAAAAAAAAAAEGBwEFBAEBCAECAwEAAAAAAAAAAAAAAAAAAAAAAAAAAQgBBwEEAQgJAQICAQAAAAAAAAAAAAAAAAAAAAAAAAEICAEHAQQBCQYBAgIBAAAAAAAAAAAAAAAAAAAAAAAAAAEGCQkBBQEGAQUBAgEAAAAAAAAAAAAAAAAAAAAAAAAAAAEBBgYBAQEBAQABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQcBAQEDAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAwMBAgcHAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQMDAwIBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBAIDAgEKCgECAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAECAQEBAQkJAQIBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQECAgICAQECAQoBAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQICAgEKCgEAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBCgoKAQAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
},
"palette" : {
"rgba8888" : [ 0, 538976511, 1368527103, 1907499263, -205315841, -1014890241, -1302257409, -202116097, -207461889, -476950017, 1631658239 ]
}
},
"diagnostics" : [ ]
@ -178,13 +178,13 @@
"last_modified" : 1779659315457,
"fingerprint" : "fc0a623650619ca39c1ce2f9f9e1b0f6951abd194e18ea60c682fae6a769df97",
"metadata" : {
"palette" : {
"rgba8888" : [ 0, 538976511, 1368527103, 1907499263, -205315841, -1014890241, -1302257409, -202116097, -207461889, -476950017, 1631658239, -1300082177 ]
},
"tile" : {
"width" : 32,
"height" : 32,
"paletteIndices" : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQICAwMBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEEBAQBAgIDAwMCAQABAQAAAAAAAAAAAAAAAAAAAAAAAQUFBAQBAgIDAgIBAQMBAAAAAAAAAAAAAAAAAAAAAAAAAQEFBAUBAgICAgIDAwEAAAAAAAAAAAAAAAAAAAAAAAABBgEFBQUBAgECAgIBAAAAAAAAAAAAAAAAAAAAAAAAAAEGBwEFBAUBCAEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAQgBBwEEBQEJAQEFBQEAAAAAAAAAAAAAAAAAAAAAAAEICAEHCAEEBQEBBQEBAAAAAAAAAAAAAAAAAAAAAAAAAAEGCQkGBgEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBBgYGAQIDAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCQEBAQEBAQEHAQAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBBwcDAgEKCgEBAAAAAAAAAAAAAAAAAAAAAAAAAAABAgIBAwMCAQkJAQEAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQIBAgMCAQEBCgEAAAAAAAAAAAAAAAAAAAAAAAABCwsKAQIBAQECAgEKAQAAAAAAAAAAAAAAAAAAAAAAAAABCwsKAQICAgIBCgoBAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
},
"palette" : {
"rgba8888" : [ 0, 538976511, 1368527103, 1907499263, -205315841, -1014890241, -1302257409, -202116097, -207461889, -476950017, 1631658239, -1300082177 ]
}
},
"diagnostics" : [ ]
@ -195,13 +195,13 @@
"last_modified" : 1779659315457,
"fingerprint" : "7ada8efefcd6d069b10846fefb9bd9cff1a1fa4c1565bbebfd68fc83645f0732",
"metadata" : {
"palette" : {
"rgba8888" : [ 0, 538976511, 1368527103, 1907499263, -205315841, -1014890241, -1302257409, -202116097, -207461889, -476950017, 1631658239, -1300082177 ]
},
"tile" : {
"width" : 32,
"height" : 32,
"paletteIndices" : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAAAAAAEBAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQICAwMBAQEBAwEAAAAAAAAAAAAAAAAAAAAAAAABBAQBAgIDAwMCAQMDAQAAAAAAAAAAAAAAAAAAAAAAAQUFBAQBAgIDAgICAwIBAAAAAAAAAAAAAAAAAAAAAAABBQEFBAUBAgICAgMCAQAAAAAAAAAAAAAAAAAAAAAAAAABBgEFBQUBAgECAgIBAAAAAAAAAAAAAAAAAAAAAAAAAAEGBwEFBAUBCAEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAQgBBwEEBQEJAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAEICAEHCQEEBQEFAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEGCQkGBgEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBBgYGAQIDAgEBAAAAAAAAAAAAAAAAAAAAAAAAAAEJAQEBAQEBAgIBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBBwcDAgIBAQoKAQAAAAAAAAAAAAAAAAAAAAAAAAAAAQQBAwMDAgIBCQkBAAAAAAAAAAAAAAAAAAAAAAAAAAEBAgECAwMCAQEBAQEAAAAAAAAAAAAAAAAAAAAAAAABCwYBAgEBAQECAgEKAQAAAAAAAAAAAAAAAAAAAAAAAAELCgYBAgICAgEBAQEAAAAAAAAAAAAAAAAAAAAAAAAAAAEKCgoBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
},
"palette" : {
"rgba8888" : [ 0, 538976511, 1368527103, 1907499263, -205315841, -1014890241, -1302257409, -202116097, -207461889, -476950017, 1631658239, -1300082177 ]
}
},
"diagnostics" : [ ]
@ -250,13 +250,13 @@
"last_modified" : 1776965154257,
"fingerprint" : "8771c3e294d0edbc8dd3aaa6995a6db32166ae6bfb3e063170cc79b298d17e71",
"metadata" : {
"palette" : {
"rgba8888" : [ 1217939711 ]
},
"tile" : {
"width" : 8,
"height" : 8,
"paletteIndices" : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
},
"palette" : {
"rgba8888" : [ 1217939711 ]
}
},
"diagnostics" : [ ]
@ -267,13 +267,13 @@
"last_modified" : 1776965154257,
"fingerprint" : "5d81eef8be13043330b42b546e10beed8ba00dfaa42df67477a8778c2b9484d7",
"metadata" : {
"palette" : {
"rgba8888" : [ 1217939711, 1081622783, 677921023, 676868351, 673720575, -2005909249, -1466932993, 1615868159 ]
},
"tile" : {
"width" : 8,
"height" : 8,
"paletteIndices" : "AAAAAAAAAAAAAAAAAAEBAQABAQEBAQEBAQIDBAQEBAMCAwQFBQUFBAIDBAUFBgYHAgMEBwUFBwcAAgQEBwcHBg=="
},
"palette" : {
"rgba8888" : [ 1217939711, 1081622783, 677921023, 676868351, 673720575, -2005909249, -1466932993, 1615868159 ]
}
},
"diagnostics" : [ ]
@ -284,13 +284,13 @@
"last_modified" : 1776965154258,
"fingerprint" : "3250840c4fe08c134ea966ce7c8fcaccae2c2a45660d7173c5e262a2e5076548",
"metadata" : {
"palette" : {
"rgba8888" : [ 1081622783, 676868351, 673720575, 1615868159, -1466932993, -2005909249 ]
},
"tile" : {
"width" : 8,
"height" : 8,
"paletteIndices" : "AAAAAAABAgIAAAAAAQIDBAAAAAECAwQEAAECAgMDAwMCAgUEBAQEBAMFBAQFBQUFBAQEBQUEBAQEBQUFBAQFBQ=="
},
"palette" : {
"rgba8888" : [ 1081622783, 676868351, 673720575, 1615868159, -1466932993, -2005909249 ]
}
},
"diagnostics" : [ ]
@ -301,13 +301,13 @@
"last_modified" : 1776965154258,
"fingerprint" : "fe3ab2b7eb3b10a042de14484d53e718e6b505edb1ad19053668565bfe3a8f25",
"metadata" : {
"palette" : {
"rgba8888" : [ 673720575, 676868351, 1081622783, -1466932993, 1615868159, -2005909249 ]
},
"tile" : {
"width" : 8,
"height" : 8,
"paletteIndices" : "AAABAgICAgIDBAABAgICAgMDBAABAgICBAQEBAAAAQIDAwMDAwUAAAUFBQUDAwUEAwMDBQUDAwMFBQMDBQUFAw=="
},
"palette" : {
"rgba8888" : [ 673720575, 676868351, 1081622783, -1466932993, 1615868159, -2005909249 ]
}
},
"diagnostics" : [ ]
@ -318,13 +318,13 @@
"last_modified" : 1776965154259,
"fingerprint" : "7fcbf90e1e9a69d6ce3be80ea1901d66c9992f68dc72e4c636dcd2ad52fa2898",
"metadata" : {
"palette" : {
"rgba8888" : [ 1217939711, 1081622783, 676868351, 673720575, 677921023, -2005909249, 1615868159, -1466932993 ]
},
"tile" : {
"width" : 8,
"height" : 8,
"paletteIndices" : "AAAAAAAAAAABAQEAAAAAAAEBAQEBAQEAAgMDAwMCBAEDBQUFBQMCBAYHBwUFAwIEBgYFBQYDAgQHBgYGAwMEAA=="
},
"palette" : {
"rgba8888" : [ 1217939711, 1081622783, 676868351, 673720575, 677921023, -2005909249, 1615868159, -1466932993 ]
}
},
"diagnostics" : [ ]
@ -335,13 +335,13 @@
"last_modified" : 1776965154259,
"fingerprint" : "451fc8d646049944d5aa7c7dc254636eec0151aaff6e2051059304b9d6ebc31e",
"metadata" : {
"palette" : {
"rgba8888" : [ 1217939711, 677921023, 676868351, 673720575, 1615868159, -1466932993, -2005909249 ]
},
"tile" : {
"width" : 8,
"height" : 8,
"paletteIndices" : "AAECAwMEBQUAAAECAwUFBgAAAAIDBQYGAAAAAwMFBgYAAAMEAwUGBgADBgQDBQYGAwYFBAMFBgYDBgUEAwUGBg=="
},
"palette" : {
"rgba8888" : [ 1217939711, 677921023, 676868351, 673720575, 1615868159, -1466932993, -2005909249 ]
}
},
"diagnostics" : [ ]
@ -352,13 +352,13 @@
"last_modified" : 1776965154260,
"fingerprint" : "aa757df4600823ec6655235a1081061777124ccee74b31481bbe402e53635f53",
"metadata" : {
"palette" : {
"rgba8888" : [ -2005909249, -1466932993, 1615868159, 673720575 ]
},
"tile" : {
"width" : 8,
"height" : 8,
"paletteIndices" : "AAABAgIAAAAAAQEAAgIAAQEBAAAAAgIBAQEAAAECAwIBAQAAAQIDAwEBAAABAgMDAQEAAAECAwIBAQEAAgMCAQ=="
},
"palette" : {
"rgba8888" : [ -2005909249, -1466932993, 1615868159, 673720575 ]
}
},
"diagnostics" : [ ]
@ -369,13 +369,13 @@
"last_modified" : 1776965154260,
"fingerprint" : "38162157f5685cce25b835d3a2caee3465fd2ec3ee5db2a5089d7d5ae5ea3bb7",
"metadata" : {
"palette" : {
"rgba8888" : [ -2005909249, 1615868159, -1466932993, 673720575 ]
},
"tile" : {
"width" : 8,
"height" : 8,
"paletteIndices" : "AAAAAQECAAACAAEBAAICAAIBAQAAAAICAQMBAgAAAgIDAwECAAACAgMDAQIAAAICAQMBAgAAAgICAQMBAAICAg=="
},
"palette" : {
"rgba8888" : [ -2005909249, 1615868159, -1466932993, 673720575 ]
}
},
"diagnostics" : [ ]
@ -386,13 +386,13 @@
"last_modified" : 1776965154260,
"fingerprint" : "8cab5b3d553af9ed371bd19fc624763accc90e5d926d300ba31dd8d9d6bab803",
"metadata" : {
"palette" : {
"rgba8888" : [ -1466932993, 1615868159, 673720575, 676868351, 677921023, 1217939711, -2005909249 ]
},
"tile" : {
"width" : 8,
"height" : 8,
"paletteIndices" : "AAABAgIDBAUGAAACAwQFBQYGAAIDBQUFBgYAAgIFBQUGBgACAQIFBQYGAAIBBgIFBgYAAgEABgIGBgACAQAGAg=="
},
"palette" : {
"rgba8888" : [ -1466932993, 1615868159, 673720575, 676868351, 677921023, 1217939711, -2005909249 ]
}
},
"diagnostics" : [ ]
@ -403,13 +403,13 @@
"last_modified" : 1776965154261,
"fingerprint" : "d9ff5a6df0f65f5fcfb8f22376beeb266d429e2c6e9c82f51c12bc6ee4c3fcdc",
"metadata" : {
"palette" : {
"rgba8888" : [ 673720575, 1615868159, -2005909249, -1466932993, 676868351, 677921023, 1217939711 ]
},
"tile" : {
"width" : 8,
"height" : 8,
"paletteIndices" : "AAECAQEAAwIEAAABAQADAwQFBAABAQADBgUEBAABAAAGBgUFBAAAAQYGBgUEAAICBgYGBgAAAgIGBgYEAAEBAg=="
},
"palette" : {
"rgba8888" : [ 673720575, 1615868159, -2005909249, -1466932993, 676868351, 677921023, 1217939711 ]
}
},
"diagnostics" : [ ]
@ -420,13 +420,13 @@
"last_modified" : 1776965154261,
"fingerprint" : "b9ff45c77f7779a6acebcc537c2ddac0e27e91162b656a04dc4772ec5f24fc26",
"metadata" : {
"palette" : {
"rgba8888" : [ -2005909249, -1466932993, 1615868159, 673720575 ]
},
"tile" : {
"width" : 8,
"height" : 8,
"paletteIndices" : "AAEBAgIAAAEAAAICAQEAAAEAAAAAAQEAAQEBAQAAAQEDAgIBAQAAAAIDAgIBAQAAAAIDAgICAQEAAgIDAgICAg=="
},
"palette" : {
"rgba8888" : [ -2005909249, -1466932993, 1615868159, 673720575 ]
}
},
"diagnostics" : [ ]
@ -437,13 +437,13 @@
"last_modified" : 1776965154261,
"fingerprint" : "43f370324fedccda888d49409e2a64f182f08b4143b4bdd5bafaf498feebd21a",
"metadata" : {
"palette" : {
"rgba8888" : [ -1466932993, -2005909249, 1615868159, 673720575 ]
},
"tile" : {
"width" : 8,
"height" : 8,
"paletteIndices" : "AAEBAgIAAAEBAQAAAgIBAQEAAAEBAQEAAAABAQAAAAABAQEAAAICAwEBAAACAgMCAAACAgIDAgECAgICAwICAQ=="
},
"palette" : {
"rgba8888" : [ -1466932993, -2005909249, 1615868159, 673720575 ]
}
},
"diagnostics" : [ ]
@ -454,13 +454,13 @@
"last_modified" : 1776965154262,
"fingerprint" : "cb047e655f4e196f22ca0048149df5dcb959f895faeaeb94920182d4a2d16b38",
"metadata" : {
"palette" : {
"rgba8888" : [ -2005909249, -1466932993, 673720575, 1615868159, 676868351, 677921023, 1217939711 ]
},
"tile" : {
"width" : 8,
"height" : 8,
"paletteIndices" : "AAECAwMAAwIBAQIDAwICBAECAwMCBAUEAgIDAgQEBQYDAgIEBQUGBgAAAgQFBgYGAAACAgYGBgYAAwMCBAYGBg=="
},
"palette" : {
"rgba8888" : [ -2005909249, -1466932993, 673720575, 1615868159, 676868351, 677921023, 1217939711 ]
}
},
"diagnostics" : [ ]
@ -471,13 +471,13 @@
"last_modified" : 1776965154262,
"fingerprint" : "25eba17b67dd5159f5fb9d3edd531d54a44d1c76f997385840dac53517b98886",
"metadata" : {
"palette" : {
"rgba8888" : [ 1217939711, 673720575, -2005909249, 1615868159, 676868351, 677921023 ]
},
"tile" : {
"width" : 8,
"height" : 8,
"paletteIndices" : "AAAAAQICAwMAAAECAgICAwAAAQICAgIDAAABAgIDAQMAAAEDAQQEAQAABAQFBAUEAAAAAAAFAAUAAAAAAAAAAA=="
},
"palette" : {
"rgba8888" : [ 1217939711, 673720575, -2005909249, 1615868159, 676868351, 677921023 ]
}
},
"diagnostics" : [ ]
@ -488,13 +488,13 @@
"last_modified" : 1776965154262,
"fingerprint" : "32ca96c14290de8009fd2db623f55514bed2345b7d5881a80dd3fe6235012854",
"metadata" : {
"palette" : {
"rgba8888" : [ 1615868159, 673720575, 676868351, -2005909249, 677921023, 1217939711 ]
},
"tile" : {
"width" : 8,
"height" : 8,
"paletteIndices" : "AAAAAQAAAQEBAAEBAAEAAAEBAgEBAAADAQICAgEAAAMCAgQCAgEAAAQEBQQEAQADBQUFBQUCAQAFBQUFBQUCAQ=="
},
"palette" : {
"rgba8888" : [ 1615868159, 673720575, 676868351, -2005909249, 677921023, 1217939711 ]
}
},
"diagnostics" : [ ]
@ -505,13 +505,13 @@
"last_modified" : 1776965154263,
"fingerprint" : "7a093fe00c805bcf38d4facff14a6d4c2b8baeaa2d553fa8d3db9f2d76b32f3b",
"metadata" : {
"palette" : {
"rgba8888" : [ 673720575, 1615868159, -2005909249, 676868351, 677921023, 1217939711 ]
},
"tile" : {
"width" : 8,
"height" : 8,
"paletteIndices" : "AAABAQABAQEBAQABAAABAAIBAQAAAwAAAgEBAAMDAwABAQADAwQDAwIBAAQEBQQEAQADBQUFBQUAAwUFBQUFBQ=="
},
"palette" : {
"rgba8888" : [ 673720575, 1615868159, -2005909249, 676868351, 677921023, 1217939711 ]
}
},
"diagnostics" : [ ]
@ -522,13 +522,13 @@
"last_modified" : 1776965154263,
"fingerprint" : "77c5277220421226d16f58b1bcfb4eaf157fa4f6c740c5aaebefaf88fb91c22e",
"metadata" : {
"palette" : {
"rgba8888" : [ 1615868159, -2005909249, 673720575, 1217939711, 676868351, 677921023 ]
},
"tile" : {
"width" : 8,
"height" : 8,
"paletteIndices" : "AAABAQIDAwMAAQEBAQIDAwABAQEBAgMDAAIAAQECAwMCBAQCAAIDAwQFBAUEBAMDBQMFAwMDAwMDAwMDAwMDAw=="
},
"palette" : {
"rgba8888" : [ 1615868159, -2005909249, 673720575, 1217939711, 676868351, 677921023 ]
}
},
"diagnostics" : [ ]