internalisation

This commit is contained in:
Matei Adriel 2019-07-22 15:22:15 +03:00
parent 097c44e86e
commit 0c76f3cdf6
33 changed files with 561 additions and 63 deletions

BIN
docs/assets/files.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

View file

@ -6,6 +6,8 @@ import { handleErrors } from './modules/errors/helpers/handleErrors'
import { initKeyBindings } from './modules/keybindings/helpers/initialiseKeyBindings'
import { initBaseTemplates } from './modules/saving/helpers/initBaseTemplates'
console.clear()
handleErrors()
initKeyBindings()
initBaseTemplates()

View file

@ -1,11 +1,9 @@
import React, { Component, createRef, Ref, RefObject } from 'react'
import FluidCanvas from './FluidCanvas'
import loop from 'mainloop.js'
import { Gate } from '../../simulation/classes/Gate'
import { SimulationRenderer } from '../../simulationRenderer/classes/SimulationRenderer'
import { renderSimulation } from '../../simulationRenderer/helpers/renderSimulation'
import { updateSimulation } from '../../simulationRenderer/helpers/updateSimulation'
import { addGate } from '../../simulation/helpers/addGate'
import { rendererSubject } from '../subjects/rendererSubject'
class Canvas extends Component {
@ -18,8 +16,6 @@ class Canvas extends Component {
rendererSubject.next(this.renderer)
addGate(this.renderer.simulation, 'not')
loop.setDraw(() => {
if (this.renderingContext) {
renderSimulation(this.renderingContext, this.renderer)

View file

@ -0,0 +1,28 @@
import React from 'react'
import ListItem from '@material-ui/core/ListItem'
import ListItemIcon from '@material-ui/core/ListItemIcon'
import ListItemText from '@material-ui/core/ListItemText'
import Icon from '@material-ui/core/Icon'
import { handleCreating } from '../../create-simulation/helpers/handleCreating'
import { useTranslation } from '../../internalisation/helpers/useLanguage'
/**
* The component for the 'Create simulation' button from the top of the sidebar.
*
* The only way i found to apply a different color to the ListItem button was
* by using !important in the scss.
*/
const CreateSimulationButton = () => {
const translation = useTranslation()
return (
<ListItem button className="contained" onClick={handleCreating}>
<ListItemIcon>
<Icon>note_add</Icon>
</ListItemIcon>
<ListItemText>{translation.sidebar.createSimulation}</ListItemText>
</ListItem>
)
}
export default CreateSimulationButton

View file

@ -0,0 +1,93 @@
import React, { useState } from 'react'
import ListItem from '@material-ui/core/ListItem'
import ListItemIcon from '@material-ui/core/ListItemIcon'
import ListItemText from '@material-ui/core/ListItemText'
import Menu from '@material-ui/core/Menu'
import MenuItem from '@material-ui/core/MenuItem'
import Icon from '@material-ui/core/Icon'
import Typography from '@material-ui/core/Typography'
import { BehaviorSubject } from 'rxjs'
import { useObservable } from 'rxjs-hooks'
import { switchTo } from '../../saving/helpers/switchTo'
import { SimulationError } from '../../errors/classes/SimulationError'
import { templateStore } from '../../saving/stores/templateStore'
import { useTranslation } from '../../internalisation/helpers/useLanguage'
/**
* Subject to make React update the dom when new gates are stored
*/
const allGatesSubject = new BehaviorSubject<string[]>([])
/**
* Triggers a dom update by pushing a new value to the
* useObservable hook inside the React component.
*
* It also has the side effect of sorting the template names.
*/
const updateTemplateList = () => {
allGatesSubject.next(templateStore.ls().sort())
}
/**
* Component wich contains the sidebar 'Open simulation' button
*
* @throws SimulationError if the data about a simulation cant be found in localStorage
*/
const LogicGates = () => {
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null)
const simulations = useObservable(() => allGatesSubject, [])
const translation = useTranslation()
const handleClose = () => {
setAnchorEl(null)
}
return (
<>
<ListItem
button
onClick={event => {
updateTemplateList()
setAnchorEl(event.currentTarget)
}}
>
<ListItemIcon>
<Icon>memory</Icon>
</ListItemIcon>
<ListItemText>{translation.sidebar.logicGates}</ListItemText>
</ListItem>
<Menu
keepMounted
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={handleClose}
>
{simulations.map((simulationName, index) => {
const simulationData = templateStore.get(simulationName)
if (!simulationData) {
throw new SimulationError(
`Cannot get data for simulation ${simulationName}`
)
}
return (
<MenuItem
key={index}
onClick={() => {
switchTo(simulationName)
handleClose()
}}
>
<Typography>{simulationName}</Typography>
</MenuItem>
)
})}
</Menu>
</>
)
}
export default LogicGates

View file

@ -5,24 +5,48 @@ import ListItemText from '@material-ui/core/ListItemText'
import Menu from '@material-ui/core/Menu'
import MenuItem from '@material-ui/core/MenuItem'
import Icon from '@material-ui/core/Icon'
import Typography from '@material-ui/core/Typography'
import { saveStore } from '../../saving/stores/saveStore'
import { BehaviorSubject } from 'rxjs'
import { useObservable } from 'rxjs-hooks'
import { rendererSubject } from '../subjects/rendererSubject'
import { currentStore } from '../../saving/stores/currentStore'
import { switchTo } from '../../saving/helpers/switchTo'
import { SimulationError } from '../../errors/classes/SimulationError'
import { icons } from '../constants'
import { useTranslation } from '../../internalisation/helpers/useLanguage'
/**
* Returns a list with the names of all saved simulations
*/
const allSimulations = () => {
return saveStore.ls()
}
/**
* Subject to make React update the dom when new simulations are stored
*/
const allSimulationSubject = new BehaviorSubject<string[]>([])
/**
* Triggers a dom update by pushing a new value to the
* useObservable hook inside the React component.
*
* It also has the side effect of sorting the simulation names.
*/
const updateSimulationList = () => {
allSimulationSubject.next(allSimulations())
allSimulationSubject.next(allSimulations().sort())
}
/**
* Component wich contains the sidebar 'Open simulation' button
*
* @throws SimulationError if the data about a simulation cant be found in localStorage
*/
const OpenSimulation = () => {
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null)
const simulations = useObservable(() => allSimulationSubject, [])
const translation = useTranslation()
const handleClose = () => {
setAnchorEl(null)
}
@ -39,7 +63,9 @@ const OpenSimulation = () => {
<ListItemIcon>
<Icon>folder_open</Icon>
</ListItemIcon>
<ListItemText>Open simulation</ListItemText>
<ListItemText>
{translation.sidebar.openSimulation}
</ListItemText>
</ListItem>
<Menu
@ -48,23 +74,36 @@ const OpenSimulation = () => {
open={Boolean(anchorEl)}
onClose={handleClose}
>
{simulations.map((simulation, index) => (
<MenuItem
key={index}
onClick={() => {
if (rendererSubject.value) {
const renderer = rendererSubject.value
{simulations.map((simulationName, index) => {
const simulationData = saveStore.get(simulationName)
currentStore.set(simulation)
renderer.reloadSave()
}
if (!simulationData) {
throw new SimulationError(
`Cannot get data for simulation ${simulationName}`
)
}
handleClose()
}}
>
{simulation}
</MenuItem>
))}
return (
<MenuItem
key={index}
onClick={() => {
switchTo(simulationName)
handleClose()
}}
>
<ListItemIcon>
<Icon>
{
icons.simulationMode[
simulationData.simulation.mode
]
}
</Icon>
</ListItemIcon>
<Typography>{simulationName}</Typography>
</MenuItem>
)
})}
</Menu>
</>
)

View file

@ -1,35 +1,51 @@
import React, { useState } from 'react'
import React from 'react'
import Drawer from '@material-ui/core/Drawer'
import List from '@material-ui/core/List'
import ListItem from '@material-ui/core/ListItem'
import ListItemIcon from '@material-ui/core/ListItemIcon'
import ListItemText from '@material-ui/core/ListItemText'
import Icon from '@material-ui/core/Icon'
import { handleCreating } from '../../create-simulation/helpers/handleCreating'
import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'
import OpenSimulation from './OpenSimulation'
import CreateSimulationButton from './CreateSimulationButton'
import LogicGates from './LogicGates'
import { makeStyles, createStyles } from '@material-ui/core/styles'
/**
* The width of the sidebar
*/
const sidebarWidth = 240
const drawerWidth = 240
const useStyles = makeStyles((theme: Theme) =>
/**
* The z-index of the sidebar.
*/
const sidebarZIndex = 5
/**
* The styles for the sidebar component
*/
const useStyles = makeStyles(
createStyles({
// This class is applied on the sidebar container
root: {
display: 'flex',
zIndex: 5
zIndex: sidebarZIndex
},
// This is the class of the actual sidebar
drawer: {
width: drawerWidth,
flexShrink: 0,
zIndex: 5
width: sidebarWidth,
zIndex: sidebarZIndex,
flexShrink: 0
},
// This is the class for the surface of the sidebar
drawerPaper: {
padding: '4px',
width: drawerWidth,
background: `#111111`,
zIndex: 5
padding: '4px',
width: sidebarWidth,
zIndex: sidebarZIndex
}
})
)
/**
* The sidebar component
*/
const Sidebar = () => {
const classes = useStyles()
@ -44,18 +60,10 @@ const Sidebar = () => {
paper: classes.drawerPaper
}}
>
<List component="nav" aria-label="Main mailbox folders">
<ListItem
button
className="contained"
onClick={handleCreating}
>
<ListItemIcon>
<Icon>note_add</Icon>
</ListItemIcon>
<ListItemText>Create simulation</ListItemText>
</ListItem>
<List component="nav">
<CreateSimulationButton />
<OpenSimulation />
<LogicGates />
</List>
</Drawer>
</div>

View file

@ -1,5 +1,6 @@
import { createMuiTheme } from '@material-ui/core/styles'
import { red, deepPurple } from '@material-ui/core/colors'
import { simulationMode } from '../saving/types/SimulationSave'
export const theme = createMuiTheme({
palette: {
@ -8,3 +9,20 @@ export const theme = createMuiTheme({
secondary: red
}
})
/**
* Used to get intellisense from visual studio code
*/
export interface IconInterface {
simulationMode: Record<simulationMode, string>
}
/**
* Stores the names of icons reuseed truogh the app
*/
export const icons: IconInterface = {
simulationMode: {
project: 'gamepad',
ic: 'memory'
}
}

View file

@ -4,21 +4,19 @@ import { useObservable } from 'rxjs-hooks'
import { CreateSimulationStore } from '../stores/CreateSimulationStore'
import { simulationMode } from '../../saving/types/SimulationSave'
import Icon from '@material-ui/core/Icon'
import { useTranslation } from '../../internalisation/helpers/useLanguage'
export interface CreateSimulationOption {
mode: simulationMode
icon: string
name: string
}
export const createSimulationOptions: CreateSimulationOption[] = [
{
name: 'project',
mode: 'project',
icon: 'gamepad'
},
{
name: 'integrated circuit',
icon: 'memory',
mode: 'ic'
}
@ -26,6 +24,7 @@ export const createSimulationOptions: CreateSimulationOption[] = [
const CreateSimulation = () => {
const open = useObservable(() => CreateSimulationStore.data.open, false)
const translation = useTranslation()
const closeModal = () => {
CreateSimulationStore.actions.next('quit')
@ -38,7 +37,7 @@ const CreateSimulation = () => {
onClick={closeModal}
>
<div id="create-title">
What kind of simulation do you want to create?
{translation.createSimulation.mode.question}
</div>
<div id="create-options">
@ -57,7 +56,11 @@ const CreateSimulation = () => {
<Icon>{option.icon}</Icon>
</div>
<div className="create-option-name" id={option.mode}>
{option.name}
{
translation.createSimulation.mode.options[
option.mode
]
}
</div>
</div>
))}

View file

@ -1,5 +1,6 @@
import { CreateSimulationStore } from '../stores/CreateSimulationStore'
import { initSimulation } from '../../saving/helpers/initSimulation'
import { switchTo } from '../../saving/helpers/switchTo'
export const handleCreating = async () => {
const options = await CreateSimulationStore.create()
@ -8,5 +9,7 @@ export const handleCreating = async () => {
const simulation = initSimulation(options.name, options.mode)
switchTo(options.name)
return simulation
}

View file

@ -2,6 +2,7 @@ import { BehaviorSubject, Subject } from 'rxjs'
import { take } from 'rxjs/operators'
import { simulationMode } from '../../saving/types/SimulationSave'
import { InputStore } from '../../input/stores/InputStore'
import { CurrentLanguage } from '../../internalisation/stores/currentLanguage'
export type CreateSimulationStoreAction = 'quit' | 'submit'
@ -13,6 +14,8 @@ export const CreateSimulationStore = {
.pipe(take(1))
.toPromise()
const translation = CurrentLanguage.getTranslation()
CreateSimulationStore.close()
if (action === 'quit') {
@ -20,7 +23,7 @@ export const CreateSimulationStore = {
}
const name = await InputStore.get(
'What do you want your simulation to be called?'
translation.createSimulation.name.question
)
if (!name) {

View file

@ -0,0 +1,12 @@
import { supportedLanguages } from './types/supportedLanguages'
import { Translation } from './types/TranslationInterface'
import { EnglishTranslation } from './translations/english'
import { RomanianTranslation } from './translations/romanian'
/**
* Object with all translations
*/
export const translations: Record<supportedLanguages, Translation> = {
english: EnglishTranslation,
['română']: RomanianTranslation
}

View file

@ -0,0 +1,16 @@
import { useObservable } from 'rxjs-hooks'
import currentLanguageSubject from '../subjects/currentLanguageSubject'
import { translations } from '../constants'
/**
* Hook to get the current translation
*/
export const useTranslation = () => {
const currentLanguage = useObservable(
() => currentLanguageSubject,
'english'
)
const translation = translations[currentLanguage]
return translation
}

View file

@ -0,0 +1,53 @@
import { supportedLanguages } from '../types/supportedLanguages'
import { LocalStore } from '../../storage/classes/LocalStore'
import currentLanguageSubject from './../subjects/currentLanguageSubject'
import { SimulationError } from '../../errors/classes/SimulationError'
import { translations } from '../constants'
/**
* Local store containing the current selected language
*/
export const CurrentLanguageLocalStore = new LocalStore<supportedLanguages>(
'language'
)
// This makes sure the language isnt undefined
if (!CurrentLanguageLocalStore.get()) {
CurrentLanguageLocalStore.set('english')
}
currentLanguageSubject.next(CurrentLanguageLocalStore.get() || 'english')
/**
* The preffered interface for interacting with CurrentLanguageLocalStore
*/
const CurrentLanguage = {
set(name: supportedLanguages) {
CurrentLanguageLocalStore.set(name)
currentLanguageSubject.next(name)
},
/**
* Used to get the current selected store
*
* @throws SimulationError if the language cannot be found
*/
get() {
const language = CurrentLanguageLocalStore.get()
if (!language) {
throw new SimulationError(`Current language not found`)
}
return language
},
/**
* Helper to get the current translation outside React components
*/
getTranslation() {
return translations[CurrentLanguage.get()]
}
}
export { CurrentLanguage }

View file

@ -0,0 +1,7 @@
import { BehaviorSubject } from 'rxjs'
import { supportedLanguages } from '../types/supportedLanguages'
/**
* Subject with the current language
*/
export default new BehaviorSubject<supportedLanguages>('english')

View file

@ -0,0 +1,31 @@
import { Translation } from '../types/TranslationInterface'
/**
* The enaglish translation
*/
export const EnglishTranslation: Translation = {
language: 'english',
sidebar: {
createSimulation: 'Create simulation',
logicGates: 'Logic gates',
openSimulation: 'Open simulations'
},
createSimulation: {
mode: {
question: 'What kind of simulation do you want to create?',
options: {
ic: 'Integrated circuit',
project: 'Project'
}
},
name: {
question: 'What do you want your simulation to be called?'
}
},
messages: {
createdSimulation: name => `Succesfully created simulation '${name}'`,
switchedToSimulation: name =>
`Succesfully switched to simulation '${name}'`,
savedSimulation: name => `Succesfully saved simulation '${name}'`
}
}

View file

@ -0,0 +1,32 @@
import { Translation } from '../types/TranslationInterface'
/**
* The romanian translation
*/
export const RomanianTranslation: Translation = {
language: 'română',
sidebar: {
createSimulation: 'Creează o simulație',
openSimulation: 'Deschide o simulație',
logicGates: 'Porți logice'
},
createSimulation: {
mode: {
question: 'Ce fel de simulație vrei să creiezi?',
options: {
ic: 'Circuit integrat',
project: 'Proiect'
}
},
name: {
question: 'Cum vrei să numești simulația?'
}
},
messages: {
createdSimulation: name =>
`Simulația '${name}' a fost creiată cu succes`,
switchedToSimulation: name =>
`Simulația '${name}' a fost deschisă cu succes`,
savedSimulation: name => `Simulația '${name}' a fost salvată cu succes`
}
}

View file

@ -0,0 +1,31 @@
import { supportedLanguages } from './supportedLanguages'
import { simulationMode } from '../../saving/types/SimulationSave'
export type SentenceFactory<T extends string[]> = (...names: T) => string
export type NameSentence = SentenceFactory<[string]>
/**
* The interface all translations need to follow
*/
export interface Translation {
language: supportedLanguages
sidebar: {
createSimulation: string
openSimulation: string
logicGates: string
}
createSimulation: {
mode: {
question: string
options: Record<simulationMode, string>
}
name: {
question: string
}
}
messages: {
createdSimulation: NameSentence
switchedToSimulation: NameSentence
savedSimulation: NameSentence
}
}

View file

@ -0,0 +1,4 @@
/**
* Type containing the names of all supported languages
*/
export type supportedLanguages = 'română' | 'english'

View file

@ -13,5 +13,5 @@
z-index: $modal-index;
color: white;
background-color: $modal-bg-color;
font-family: 'Righteous';
font-family: 'Righteous', cursive;
}

View file

@ -1 +1,6 @@
/**
* Very basic JSON based object cloning
*
* @param state The object to clone
*/
export const cloneState = <T>(state: T): T => JSON.parse(JSON.stringify(state))

View file

@ -0,0 +1,16 @@
import { SimulationRenderer } from '../../simulationRenderer/classes/SimulationRenderer'
/**
* Does the cleanup for switching to another simulation
*
* @param renderer The renderer to clean up
*/
export const dumpSimulation = (renderer: SimulationRenderer) => {
renderer.simulation.dispose()
renderer.lastMousePosition = [0, 0]
renderer.selectedGate = null
renderer.selectedPins = {
end: null,
start: null
}
}

View file

@ -1,8 +1,6 @@
import { SimulationRenderer } from '../../simulationRenderer/classes/SimulationRenderer'
import { Gate, PinWrapper } from '../../simulation/classes/Gate'
import {
TransformState,
RendererState,
CameraState,
SimulationState
} from '../types/SimulationSave'
@ -12,6 +10,10 @@ import { Simulation } from '../../simulation/classes/Simulation'
import { Wire } from '../../simulation/classes/Wire'
import { templateStore } from '../stores/templateStore'
/**
* Contains methods for transforming saved state into the respective class instances
*/
export const fromTransformState = (state: TransformState): Transform => {
return new Transform(state.position, state.scale, state.rotation)
}
@ -25,7 +27,7 @@ export const fromCameraState = (state: CameraState): Camera => {
}
export const fromSimulationState = (state: SimulationState): Simulation => {
const simulation = new Simulation(state.mode)
const simulation = new Simulation(state.mode, state.name)
for (const gateState of state.gates) {
const gate = new Gate(

View file

@ -14,6 +14,10 @@ import { Camera } from '../../simulationRenderer/classes/Camera'
import { Simulation } from '../../simulation/classes/Simulation'
import { Wire } from '../../simulation/classes/Wire'
/**
* Methods for gettings the savable state from class instances
*/
export const getTransformState = (transform: Transform): TransformState => {
return {
position: transform.position,

View file

@ -1,10 +1,20 @@
import { baseTemplates } from '../constants'
import { templateStore } from '../stores/templateStore'
import { SimulationError } from '../../errors/classes/SimulationError'
/**
* Stores the base logic gate templates into localStorage
*
* @throws SimulationError if something is wrong with the template
*/
export const initBaseTemplates = () => {
for (const template of baseTemplates) {
if (template.metadata && template.metadata.name) {
templateStore.set(template.metadata.name, template)
} else {
throw new SimulationError(
`Template ${JSON.stringify(template)} cannot be stored.`
)
}
}
}

View file

@ -4,9 +4,20 @@ import { cloneState } from './cloneState'
import { saveStore } from '../stores/saveStore'
import { toast } from 'react-toastify'
import { createToastArguments } from '../../toasts/helpers/createToastArguments'
import { CurrentLanguage } from '../../internalisation/stores/currentLanguage'
/**
* Inits a simulation by:
* 1) first initialising the place in localstorage
* where the simulation will be saved
* 2) notifying the used about that
*
* @param name - the name of the simulation
* @param mode - the mode of the simulation
*/
export const initSimulation = (name: string, mode: simulationMode) => {
const state = cloneState(baseSave)
const translation = CurrentLanguage.getTranslation()
state.simulation.name = name
state.simulation.mode = mode
@ -15,7 +26,7 @@ export const initSimulation = (name: string, mode: simulationMode) => {
toast.success(
...createToastArguments(
`Successfully created simulation ${name}`,
translation.messages.createdSimulation(name),
'check'
)
)

View file

@ -5,16 +5,32 @@ import { getRendererState } from './getState'
import { saveStore } from '../stores/saveStore'
import { toast } from 'react-toastify'
import { createToastArguments } from '../../toasts/helpers/createToastArguments'
import { CurrentLanguage } from '../../internalisation/stores/currentLanguage'
/**
* Saves the state from a renderer in localStorage,
* then notifies the user about it
*
* @throws SimulationError if the simulation name
* cannot found in the currentStore
*
* @param renderer - the renderer to saev the state of
*/
export const save = (renderer: SimulationRenderer) => {
const current = currentStore.get()
if (current) {
const state = getRendererState(renderer)
const translation = CurrentLanguage.getTranslation()
saveStore.set(current, state)
toast(...createToastArguments(`Succesfully saved ${current}`, 'save'))
toast(
...createToastArguments(
translation.messages.savedSimulation(current),
'save'
)
)
} else {
throw new SimulationError(
'Cannot save without knowing the name of the active simulation'

View file

@ -0,0 +1,42 @@
import { currentStore } from '../stores/currentStore'
import { rendererSubject } from '../../core/subjects/rendererSubject'
import { SimulationError } from '../../errors/classes/SimulationError'
import { toast } from 'react-toastify'
import { createToastArguments } from '../../toasts/helpers/createToastArguments'
import { dumpSimulation } from './dumpSimulation'
import { CurrentLanguage } from '../../internalisation/stores/currentLanguage'
/**
* Used to switch to a simulation
*
* @throws SimulationError if theres no renderer stored in the rendererSubject
*
* @param simulationName The name of the simulation to switch to
*
* @example
* switchTo()
* switchTo('test')
*
*/
export const switchTo = (simulationName: string = 'default') => {
if (rendererSubject.value) {
const renderer = rendererSubject.value
const translation = CurrentLanguage.getTranslation()
dumpSimulation(renderer)
currentStore.set(simulationName)
renderer.reloadSave()
toast(
...createToastArguments(
translation.messages.switchedToSimulation(simulationName),
'arrow_right_alt'
)
)
} else {
throw new SimulationError(
`Renderer not found while trying to switch to simulation '${simulationName}'`
)
}
}

View file

@ -1,8 +1,12 @@
import { LocalStore } from '../../storage/classes/LocalStore'
import { defaultSimulationName } from '../constants'
/**
* Stores the name of the current simulation
*/
const currentStore = new LocalStore<string>('currentSave')
// This makes sure the store isnt empty
if (!currentStore.get()) {
currentStore.set(defaultSimulationName)
}

View file

@ -1,4 +1,7 @@
import { LocalStore } from '../../storage/classes/LocalStore'
import { RendererState } from '../types/SimulationSave'
/**
* This store is used to save all simulations.
*/
export const saveStore = new LocalStore<RendererState>('saves')

View file

@ -1,6 +1,9 @@
import { LocalStore } from '../../storage/classes/LocalStore'
import { GateTemplate } from '../../simulation/types/GateTemplate'
/**
* This store is used to save all logic gate templates
*/
export const templateStore = new LocalStore<DeepPartial<GateTemplate>>(
'templates'
)

View file

@ -27,6 +27,7 @@ import { wireConnectedToGate } from '../helpers/wireConnectedToGate'
import { updateMouse, handleScroll } from '../helpers/scaleCanvas'
import { RefObject } from 'react'
import { Singleton } from '@eix-js/utils'
import { dumpSimulation } from '../../saving/helpers/dumpSimulation'
export class SimulationRenderer {
public mouseDownOutput = new Subject<MouseEventInfo>()
@ -200,6 +201,8 @@ export class SimulationRenderer {
this.lastMousePosition = this.camera.toWordPostition(event.position)
})
dumpSimulation(this)
this.reloadSave()
this.initKeyBindings()
}
@ -222,7 +225,6 @@ export class SimulationRenderer {
if (!save) return
if (!(save.simulation || save.camera)) return
this.simulation.dispose()
this.simulation = fromSimulationState(save.simulation)
this.camera = fromCameraState(save.camera)
} catch (e) {

View file

@ -11,4 +11,5 @@
.toast-content-container > #toast-content-icon {
font-size: 2em;
margin: 10px;
}