Nano Banana Pro
Agent skill for nano-banana-pro
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Sign in to like and favorite skills
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This is the FHIR Validator Wrapper - a Kotlin Multiplatform project that provides CLI, Desktop GUI, and Web-based validation server for FHIR resources. It combines:
A publicly hosted instance: https://validator.fhir.org/
./gradlew build # Full build (JVM + JS) ./gradlew jvmJar # Build JVM JAR only ./gradlew jsJar # Build JS frontend only ./gradlew clean # Clean build artifacts
./gradlew run # Run local server (localhost:8080) java -jar build/libs/validator-wrapper-jvm-*.jar -startServer # Full-stack server java -jar build/libs/validator-wrapper-jvm-*.jar -gui # Desktop app mode
./gradlew test # Run all tests ./gradlew jvmTest # JVM tests only ./gradlew jsTest # JS tests only (uses Karma + Chrome Headless) ./gradlew jsBrowserTest # JS browser tests
To run a single test class:
./gradlew jvmTest --tests "ClassName" ./gradlew jvmTest --tests "controller.validation.*"
Version is managed via semantic versioning plugin. Check
version.properties for current version.
./gradlew printVersion # Print current version
The codebase uses Kotlin's
expect/actual pattern to share code across platforms:
src/ ├── commonMain/ # Shared models, constants, expected declarations │ ├── kotlin/ │ │ ├── model/ # Data models (ValidationRequest, ValidationResponse, etc.) │ │ └── constants/ # API endpoints, FHIR formats, MIME types │ └── resources/ # Static content (index.html, Swagger UI) ├── jvmMain/ # JVM server implementation │ ├── kotlin/ │ │ ├── Server.kt # Main entry point (3 execution modes) │ │ ├── Module.kt # Ktor configuration │ │ ├── controller/ # REST route handlers (by feature) │ │ ├── api/ # Dependency injection (Koin) │ │ └── utils/ # Caching, file handling ├── jsMain/ # React frontend with Context API │ ├── kotlin/ │ │ ├── Main.kt # Bootstrap entry & context provider setup │ │ ├── App.kt # Root React component (consumes contexts) │ │ ├── api/ # HTTP client (Ktor client) │ │ ├── context/ # React Context providers & state │ │ │ ├── AppScreenContext.kt # Screen navigation state │ │ │ ├── LocalizationContext.kt # i18n & language state │ │ │ └── ValidationContext.kt # Validation state (files, config, IGs) │ │ ├── ui/components/ # React UI hierarchy (context consumers) │ │ ├── utils/ # Helper functions │ │ └── css/ # Styling utilities
Entry Point & Execution Modes (Server.kt:39):
-startServer: Full-stack hosted server (default)-gui: Desktop app with embedded Chromium windowDependency Injection (Koin):
ControllersInjection.kt and ApiInjection.ktFeature-Based Controllers: Each feature has a dedicated controller directory with:
Example structure:
controller/ ├── validation/ │ ├── ValidationController.kt # Interface │ ├── ValidationControllerImpl.kt # Implementation │ └── ValidationModule.kt # Routes: post("/validate") ├── ig/ # Implementation Guides ├── version/ # Version info └── terminology/ # Terminology server validation
Key Backend Classes:
ValidationServiceFactory: Creates validation service instances (wraps HAPI FHIR validator)SessionCacheFactory: Manages session-based caching with GuavaPackageCacheDownloaderRunnable: Preloads package cache on startup (controlled by PRELOAD_CACHE env var)React Context State Management:
The frontend uses React's Context API for centralized state management with three context providers:
Context Provider Hierarchy:
// Main.kt - Provider nesting order AppScreenProvider { // Outermost - Screen navigation LocalizationProvider { // Middle - i18n & translations ValidationProvider { // Innermost - Validation state (most complex) App { ... } } } }
The Three Contexts:
AppScreenContext (
context/AppScreenContext.kt):
appScreen: AppScreen (VALIDATOR | SETTINGS enum)setAppScreen: (AppScreen) -> UnitLocalizationContext (
context/LocalizationContext.kt):
polyglot: Polyglot, selectedLanguage: Language, isLoading: BooleansetLanguage: (Language) -> Unitwindow.navigator.languages, async loads translations via getPolyglotPhrases()ValidationContext (
context/ValidationContext.kt) - Most Complex:
validationContext, sessionId, presets, igPackageInfoSet, extensionSet, profileSet, bundleValidationRuleSetcurrentManualEntryText, manualValidationOutcome, manualValidatingInProgressuploadedFiles: List<ValidationOutcome>updateValidationContext(), setSessionId(), uploadFile(), deleteFile(), addValidationOutcome(), toggleFileValidationInProgress(), updateManualEntryText(), setManualValidationOutcome(), toggleManualValidationInProgress(), updateIgPackageInfoSet(), updateExtensionSet(), updateProfileSet(), updateBundleValidationRuleSet()State Flow Pattern:
Context.Consumer { ctx -> ... }ctx?.updateValidationContext?.invoke(newValue)setState { ... } updates internal stateAsync Operations:
MainScope().launch { ... } (replaces Redux-Thunk)ValidationProvider fetches presets on mount)Context Consumption Pattern:
// Single context LocalizationContext.Consumer { localizationContext -> val polyglot = localizationContext?.polyglot ?: Polyglot() // Use polyglot for translations } // Multiple contexts (nested) LocalizationContext.Consumer { localizationContext -> ValidationContext.Consumer { validationContext -> val polyglot = localizationContext?.polyglot ?: Polyglot() val files = validationContext?.uploadedFiles ?: emptyList() Button { onClick = { validationContext?.uploadFile?.invoke(fileInfo) } } } }
UI Component Hierarchy:
Main.kt: Wraps App in 3 nested context providersApp.kt: Root component, consumes all three contexts to route between screensui/components/: React components consuming contexts as needed
header/: Language selector (Header.kt, LanguageSelect.kt), status indicatorstabs/: Upload tab (FileUploadTab.kt), manual entry tab (ManualEntryTab.kt)options/: Settings page (OptionsPage.kt), IG selector, preset selectorvalidation/: Results display, issue list, filtered viewsbuttons/, footer/: Reusable componentskotlinWrappersVersion=1.0.0-pre.561)LocalizationContext for translations; validation-heavy components also consume ValidationContextReact Component (consumes Context) ↓ invoke callback OR launch coroutine MainScope().launch { ... } ↓ suspend function call Ktor HTTP Client (JS) - suspend functions in api/ValidatorApi.kt ↓ POST /validate Ktor Server Routes (JVM) ↓ DI injection ValidationController ↓ factory ValidationServiceFactory ↓ calls HAPI FHIR Validation Library ↓ response ValidationResponse (JSON) ↓ callback invocation Context Provider setState { ... } ↓ context value changes All Consumer Components Re-render
Key Differences from Previous Redux Architecture:
setState handles updatesKey API Endpoints:
POST /validate: Submit validation requestGET /validator/version: App & validator versionsGET /validator/presets: Available validation presetsGET /ig: List implementation guides (with search)GET /igVersions/{packageName}: Versions of specific IGGET /versions: Available FHIR versionsPOST /terminology: Validate terminology server URLModels use
expect/actual pattern:
expect class@SerializableExample:
ValidationContext
expect class ValidationContextactual typealias ValidationContext = org.hl7.fhir.validation.ValidationContextactual data class ValidationContext(...) { /* serializable fields */ }Session Tracking:
ValidationContext maintains session IDs via sessionId state; SessionCacheFactory manages server-side caching
React State Immutability: Context providers use React's
setState to trigger re-renders; state updates create new objects using Kotlin's .copy() for data classes
Preset Configuration System: Preloaded validation configurations for common FHIR version + IG combinations
Localization: Browser's
navigator.languages auto-detects preferred language; Polyglot.js handles translations
Hybrid Rendering: Server serves static HTML + compiled JS bundle; desktop mode embeds same frontend in Chromium
Validation Context Builder Pattern:
.setFhirVersion().setIGs().build() for configuration
In January 2025, the project migrated from Redux to React Context API alongside the Kotlin 1.8.21 upgrade.
Why the Migration:
Redux Slices → Context Providers Mapping:
localizationSlice → LocalizationContextvalidationSessionSlice + validationContextSlice + manualEntrySlice + uploadedResourceSlice + presetsSlice → Consolidated into ValidationContextappScreenSlice → AppScreenContextKey Pattern Changes:
| Aspect | Redux (Before) | Context (After) |
|---|---|---|
| State Distribution | Store → mapStateToProps → Props | Context Provider → Consumer |
| Action Dispatching | | |
| Async Operations | Redux-Thunk middleware | with coroutines |
| Component Connection | | |
Important Implementation Notes:
val polyglot = ctx?.polyglot ?: Polyglot()Consumer blocks (can be verbose)MainScope defined at module level in Main.kt and reused across components for coroutinesRelated Commits:
02f2762: Remove redux dependencies4edcec1: Remove redux + minimal React context implementation8c519c5: Eliminate prop drilling by using contextThe project uses tightly coupled dependencies. Always update related versions together. See comments in
gradle.properties.
Current Major Versions (as of Kotlin 1.8.21 upgrade):
kotlinVersion = 1.8.21 (updated from 1.6.21)kotlinxHtmlVersion = 0.8.0 (updated from 0.7.5)kotlinxCoroutinesVersion = 1.7.1 (updated from 1.6.3)kotlinxSerializationVersion = 1.5.1 (updated from 1.3.2)kotlinWrappersVersion = 1.0.0-pre.561 (updated from 0.0.1-pre.332-kotlin-1.6.21)ktorVersion = 2.3.0 (updated from 2.0.2)jacksonVersion = 2.15.0 (updated from 2.12.6)koinVersion = 3.4.0 (updated from 3.2.1)Important: Redux and React-Redux dependencies have been completely removed. State management now uses React Context API.
Build Configuration Updates:
kotlin.js.compiler=ir - Explicitly enables IR (Intermediate Representation) compiler for JavaScriptkotlin.daemon.jvmargs=-Xmx2048m - Increases memory for Kotlin daemoncssSupport { enabled.set(true) } instead of cssSupport.enabled = trueReference projects for safe version combinations:
The
fhirCoreVersion in gradle.properties determines which HAPI FHIR validation library is used. This is the core validation engine.
ENVIRONMENT: Deployment type (defaults to 'dev'). Check application.conf for available types.PRELOAD_CACHE: Set to "true" to preload package cache on startup (useful for production)Backend (JVM):
controller/<feature>/<Feature>Module.ktModule.kt (main Ktor module)ControllersInjection.kt or ApiInjection.ktFrontend (JS):
Identify which context needs to change:
AppScreenContext: For new screens/navigationLocalizationContext: For new translations/languagesValidationContext: For validation config, files, or results (most common)Update context provider (
context/<ContextName>.kt):
State classexternal interface ValidationContextValue)render() method, typically wrapping setState { ... }Add API function in
api/ValidatorApi.kt if server communication is needed (use suspend functions)
Update or create component in
ui/components/:
Context.Consumer { ctx -> ... } patternctx?.updateSomething?.invoke(newValue)MainScope().launch { ... } with proper error handlingval value = ctx?.value ?: defaultExample: Adding file deletion to ValidationContext:
// In ValidationContext.kt provider val deleteFile: (FileInfo) -> Unit = { fileInfo -> setState { uploadedFiles = uploadedFiles.filter { it.fileInfo != fileInfo } } } contextValue.deleteFile = deleteFile // In component ValidationContext.Consumer { validationContext -> Button { onClick = { validationContext?.deleteFile?.invoke(fileInfo) } } }
testApplication for integration testing routesio.ktor:ktor-server-test-host for testing Ktor endpointspull-request-pipeline.yml)master-branch-pipeline.yml)release-branch-pipeline.yml)Hosted on Azure Pipelines: https://dev.azure.com/fhir-pipelines/validator-wrapper