import type { ReactiveController } from 'lit' import type { NeuralNetwork } from '@/app' import type { FileConfig } from '@/types/file_config' import type { FileConfigV1 } from '@/types/file_config_v1' import { AlertUtils } from '@/utils/alert_utils' export class ConfigurationController implements ReactiveController { host: NeuralNetwork constructor(host: NeuralNetwork) { this.host = host host.addController(this) } // HOST LIFECYCLE - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - hostConnected() { // add event listeners for configuration related events on host this.host.renderRoot.addEventListener( 'initiate-import', (_e: Event) => void this.initiateImport() ) this.host.renderRoot.addEventListener( 'import-config', (e: CustomEvent) => this.importConfig(e.detail) ) this.host.renderRoot.addEventListener( 'export-config', (_e: Event) => void this.exportConfig() ) } // METHODS - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - async initiateImport(): Promise { const [handle] = await window.showOpenFilePicker({ types: [ { description: 'JSON', accept: { 'application/json': ['.json'] }, }, ], }) const file = await handle.getFile() try { const text = await file.text() let fileConfig: FileConfig = await JSON.parse(text) switch (fileConfig.version) { case 1: { if ( !( Object.hasOwn(fileConfig, 'settings') && Object.hasOwn(fileConfig, 'qAndA') && Object.hasOwn(fileConfig, 'availableDataSets') && Object.hasOwn(fileConfig, 'dataSet') && Object.hasOwn(fileConfig, 'layerConfs') && Object.hasOwn(fileConfig, 'layerConnectionConfs') && Object.hasOwn(fileConfig, 'trainOptions') ) ) { throw new Error('The config you imported seems to be broken :(') } const config: FileConfigV1 = { version: 1, settings: fileConfig.settings, qAndA: fileConfig.qAndA, availableDataSets: fileConfig.availableDataSets, dataSet: fileConfig.dataSet, layerConfs: fileConfig.layerConfs, layerConnectionConfs: fileConfig.layerConnectionConfs, trainOptions: fileConfig.trainOptions, } this.importConfig(config) break } default: { throw new Error( 'The version of the config file is not compatible with the widget version you currently use!' ) } } } catch (err: unknown) { const error = err as Error AlertUtils.spawn({ message: error.message, variant: 'danger', icon: 'x-circle', }) } } importConfig(config: FileConfigV1): void { // only overwrite settings and help when in editable mode, else ignore the // imported ones if (this.host.editable) { this.host.settings = { ...config.settings } this.host.qAndA = [...config.qAndA] } this.host.dataSet = config.dataSet this.host.availableDataSets = config.availableDataSets this.host.layerConfs = config.layerConfs this.host.layerConnectionConfs = config.layerConnectionConfs this.host.trainOptions = config.trainOptions this.host.modelController.discardModel() this.host.panel = 'network' this.host.selected = {} AlertUtils.spawn({ message: `The imported config was successfully loaded!`, variant: 'success', icon: 'check-circle', }) // TODO this is not really nice and unreliable, but how to fix it? setTimeout(() => { this.host.canvas.fit() }, 1000) } async exportConfig(): Promise { const config: FileConfigV1 = { version: 1, settings: this.host.settings, qAndA: this.host.qAndA, availableDataSets: this.host.availableDataSets, dataSet: this.host.dataSet, layerConfs: this.host.layerConfs, layerConnectionConfs: this.host.layerConnectionConfs, trainOptions: this.host.trainOptions, } const configJSON = JSON.stringify(config) const handle = await window.showSaveFilePicker({ suggestedName: 'export.json', types: [ { description: 'JSON', accept: { 'application/json': ['.json'] }, }, ], }) const writer = await handle.createWritable() await writer.write(configJSON) await writer.close() AlertUtils.spawn({ message: `The current configuration was successfully exported!`, variant: 'success', icon: 'check-circle', }) } }