diff --git a/src/components/editor.js b/src/components/editor.js index ca8c203..946f8af 100644 --- a/src/components/editor.js +++ b/src/components/editor.js @@ -1,5 +1,6 @@ import fs from 'socket:fs' import path from 'socket:path' +import { sha256 } from 'socket:network' import * as monaco from 'monaco-editor' import Tonic from '@socketsupply/tonic' @@ -74,6 +75,7 @@ class EditorTabs extends Tonic { path: node.id, model: monaco.editor.createModel(), state: null, + hash: null, unsaved: false, index: count + 1 } @@ -95,10 +97,15 @@ class EditorTabs extends Tonic { this.reRender() } - setCurrentTabValue (data) { + get tab () { + return this.state.tabs.get(this.state.selectedTabId) + } + + async setCurrentTabValue (data) { const tab = this.state.tabs.get(this.state.selectedTabId) if (!tab) return + tab.hash = await sha256(data) tab.model.setValue(data) } @@ -127,7 +134,7 @@ class EditorTabs extends Tonic { this.reRender() } - click (e) { + async click (e) { const el = Tonic.match(e.target, '[data-event]') if (!el) return @@ -138,11 +145,32 @@ class EditorTabs extends Tonic { } if (event === 'close') { - const parent = el.closest('.tab') - const id = parent.dataset.id + const parentTab = el.closest('.tab') + const id = parentTab.dataset.id if (!this.state.tabs.has(id)) return const tab = this.state.tabs.get(id) + + if (tab.unsaved) { + this.selectTab(id) + + const coDialogConfirm = document.querySelector('dialog-confirm') + const result = await coDialogConfirm.prompt({ + type: 'question', + message: 'This file has changes, what do you want to do?', + buttons: [ + { label: 'Abandon', value: 'abandon' }, + { label: 'Save', value: 'save' } + ] + }) + + if (!result.abandon && !result.save) return + + if (result.save) { + await this.props.parent.saveCurrentTab() + } + } + this.remove(id) // if this tab was selected @@ -201,11 +229,37 @@ class AppEditor extends Tonic { this.editor.getModel().getValueInRange(this.editor.getSelection()) } - async writeToDisk (pathToFile, data) { + async saveCurrentTab () { + const coTerminal = document.querySelector('app-terminal') + const coProperties = document.querySelector('app-properties') + const coTabs = document.querySelector('editor-tabs') + + if (!coTabs.tab) return + const app = this.props.parent + const value = this.editor.getValue() + + if (coTabs.tab?.isRootSettingsFile) { + try { + app.state.settings = JSON.parse(value) + } catch (err) { + coTerminal.error(`Unable to parse settings file (${err.message})`) + return + } + + coTerminal.info('Settings file updated.') + app.activatePreviewWindows() + } + + clearTimeout(this.debouncePropertiesRerender) + this.debouncePropertiesRerender = setTimeout(() => { + coProperties.reRender() + }, 512) try { - await fs.promises.writeFile(pathToFile, data) + await fs.promises.writeFile(coTabs.tab.path, value) + coTabs.tab.unsaved = false + coTabs.reRender() } catch (err) { console.error(`Unable to write to ${pathToFile}`, err) } @@ -242,7 +296,7 @@ class AppEditor extends Tonic { } catch {} } - tabs.setCurrentTabValue(data) + await tabs.setCurrentTabValue(data) } } @@ -336,27 +390,22 @@ class AppEditor extends Tonic { async changes (tab, ...args) { const value = this.editor.getValue() - const coTerminal = document.querySelector('app-terminal') - - if (tab.isRootSettingsFile) { - try { - app.state.settings = JSON.parse(value) - } catch (err) { - coTerminal.error(`Unable to parse settings file (${err.message})`) - return - } + const app = this.props.parent - coTerminal.info('Settings file updated.') - app.activatePreviewWindows() + if (app.state.settings.previewMode) { + this.saveCurrentTab() + return } - clearTimeout(this.debouncePropertiesRerender) - this.debouncePropertiesRerender = setTimeout(() => { - const coProperties = document.querySelector('app-properties') - coProperties.reRender() - }, 1024) + const hash = await sha256(value) + + if (hash !== tab.hash) { + tab.unsaved = true + tab.hash = hash - this.writeToDisk(tab.path, value) + const tabs = this.querySelector('editor-tabs') + tabs.reRender() + } } connected () { diff --git a/src/index.html b/src/index.html index 2448b73..63ebfe9 100644 --- a/src/index.html +++ b/src/index.html @@ -18,6 +18,7 @@ + diff --git a/src/index.js b/src/index.js index ea7ba39..2fe96e4 100644 --- a/src/index.js +++ b/src/index.js @@ -20,6 +20,7 @@ import { AppProject } from './components/project.js' import { AppProperties } from './components/properties.js' import { AppSprite } from './components/sprite.js' import { AppEditor } from './components/editor.js' +import { DialogConfirm } from './components/confirm.js' import { DialogPublish } from './components/publish.js' import { DialogSubscribe } from './components/subscribe.js' @@ -550,11 +551,11 @@ class AppView extends Tonic { Evaluate Source: r + CommandOrControl + Shift Toggle Realtime Preview: k + CommandOrControl + Shift --- - Android: s + CommandOrControl - iOS: s + CommandOrControl - Linux: s + CommandOrControl - MacOS: s + CommandOrControl - Windows: s + CommandOrControl + Android: _ + iOS: _ + Linux: _ + MacOS: _ + Windows: _ ; ` @@ -583,6 +584,12 @@ class AppView extends Tonic { break } + case 'Save': { + const coEditor = document.querySelector('app-editor') + coEditor.saveCurrentTab() + break + } + case 'Find': { const coEditor = document.querySelector('app-editor') coEditor.editor.getAction('actions.find').run() @@ -748,6 +755,13 @@ class AppView extends Tonic { > + + + ` } @@ -760,6 +774,7 @@ window.onload = () => { Tonic.add(AppSprite) Tonic.add(AppTerminal) Tonic.add(AppView) + Tonic.add(DialogConfirm) Tonic.add(DialogPublish) Tonic.add(DialogSubscribe) Tonic.add(ViewHome)