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)