Skip to content

Commit

Permalink
adds tabs to the editor
Browse files Browse the repository at this point in the history
  • Loading branch information
heapwolf committed Mar 24, 2024
1 parent e1a907b commit c6d26fa
Show file tree
Hide file tree
Showing 13 changed files with 372 additions and 148 deletions.
3 changes: 3 additions & 0 deletions socket.ini
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,9 @@ height = 80%
; The initial width of the first window in pixels or as a percentage of the screen.
width = 80%

backgroundColorDark = "rgba(0, 0, 0, 1)"
backgroundColorLight = "rgba(255, 255, 255, 1)"

; Maximum height of the window in pixels or as a percentage of the screen.
; default value: 100%
; max_height = 100%
Expand Down
238 changes: 194 additions & 44 deletions src/components/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,151 @@ globalThis.MonacoEnvironment = {
}
}

class EditorTabs extends Tonic {
selectedTabId = null
scrollLeft = 0

constructor () {
super()

this.state = {
tabs: new Map(),
...this.state
}
}

mousewheel (e) {
this.state.scrollLeft = this.firstElementChild.scrollLeft
}

updated () {
this.firstElementChild.scrollLeft = this.state.scrollLeft
}

add (node) {
const parent = this.props.parent
const count = this.state.tabs.size

const tab = {
label: node.label,
id: node.id,
path: node.id,
model: monaco.editor.createModel(),
state: null,
unsaved: false,
index: count + 1
}

tab.model.onDidChangeContent((...args) => editor.changes(tab, ...args))

this.state.tabs.set(node.id, tab)
parent.editor.setModel(tab.model)
this.selectTab(tab.id)

this.reRender()
}

remove (id) {
if (this.state.tabs.has(id)) {
this.state.tabs.delete(id)
}

this.reRender()
}

setCurrentTabValue (data) {
const tab = this.state.tabs.get(this.state.selectedTabId)
if (!tab) return

tab.model.setValue(data)
}

selectTab (id) {
if (!this.state.tabs.has(id)) return
const parent = this.props.parent

// if there was a previously selected tab, unselect it
const previouslySelected = this.state.tabs.get(this.state.selectedTabId)

if (previouslySelected) {
previouslySelected.state = parent.editor.saveViewState()
previouslySelected.selected = false
}

const tab = this.state.tabs.get(id)
tab.selected = true

if (tab.state) {
parent.editor.restoreViewState(tab.state)
}

this.state.selectedTabId = id
parent.editor.setModel(tab.model)
parent.editor.focus()
this.reRender()
}

click (e) {
const el = Tonic.match(e.target, '[data-event]')
if (!el) return

const { event } = el.dataset

if (event === 'select') {
this.selectTab(el.dataset.id)
}

if (event === 'close') {
const parent = el.closest('.tab')
const id = parent.dataset.id
if (!this.state.tabs.has(id)) return

const tab = this.state.tabs.get(id)
this.remove(id)

// if this tab was selected
if (this.state.selectedTabId === id) {
// check if there are any other tabs
if (this.state.tabs.size > 0) {
const tabs = [...this.state.tabs.values()]
const previousSibling = tabs.find(t => t.index < tab.index)
if (previousSibling) {
previousSibling.selected = true
this.state.selectedTabId = previousSibling.id
this.selectTab(previousSibling.id)
}
}
}

this.reRender()
}
}

async render () {
let tabs

if (this.state.tabs?.size) {
tabs = [...this.state.tabs.values()].map(tab => {
const selected = tab.selected ? 'selected' : ''
const unsaved = tab.unsaved ? 'unsaved' : ''

return this.html`
<div class="tab ${selected} ${unsaved}" data-event="select" data-id="${tab.id}">
<div class="label">${tab.label}</div>
<div class="close"><tonic-button type="icon" symbol-id="close" data-event="close" size="18px"></tonic-button></div>
</div>
`
})
}

return this.html`
<header class="component">${tabs}</header>
`
}
}

Tonic.add(EditorTabs)

class AppEditor extends Tonic {
get value () {
return this.editor.getValue()
Expand All @@ -56,15 +201,13 @@ class AppEditor extends Tonic {
this.editor.getModel().getValueInRange(this.editor.getSelection())
}

async writeToDisk (data) {
if (!this.state.projectNode && this.state.projectNode.isDirectory) return

async writeToDisk (pathToFile, data) {
const app = this.props.parent

try {
await fs.promises.writeFile(this.state.projectNode.id, data)
await fs.promises.writeFile(pathToFile, data)
} catch (err) {
console.error(`Unable to write to ${this.state.projectNode.id}`, err)
console.error(`Unable to write to ${pathToFile}`, err)
}

app.reloadPreviewWindows()
Expand All @@ -77,7 +220,15 @@ class AppEditor extends Tonic {

const app = this.props.parent

if (!projectNode.isDirectory && this.editor) {
if (!projectNode.isDirectory) {
const tabs = this.querySelector('editor-tabs')

if (projectNode.label === 'settings.json' && projectNode.parent.id === 'root') {
projectNode.isRootSettingsFile = true
}

tabs.add(projectNode)

const ext = path.extname(projectNode.id)

const mappings = app.state.settings.extensionLanguageMappings
Expand All @@ -90,11 +241,15 @@ class AppEditor extends Tonic {
data = JSON.stringify(JSON.parse(data), null, 2)
} catch {}
}
this.editor.setValue(data)

tabs.setCurrentTabValue(data)
}
}

refreshColors (theme) {
refreshColors (event) {
const isDark = event || (window.matchMedia('(prefers-color-scheme: dark)'))
const theme = isDark.matches ? 'tonic-dark' : 'tonic-light'

const styles = window.getComputedStyle(document.body)

const colors = {
Expand Down Expand Up @@ -174,9 +329,34 @@ class AppEditor extends Tonic {
}
}

async refreshSettings () {
async updateSettings (options) {
const app = this.props.parent
this.editor.updateOptions(app.state.settings?.editorOptions || {})
this.editor.updateOptions(options || app.state.settings?.editorOptions || {})
}

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
}

coTerminal.info('Settings file updated.')
app.activatePreviewWindows()
}

clearTimeout(this.debouncePropertiesRerender)
this.debouncePropertiesRerender = setTimeout(() => {
const coProperties = document.querySelector('app-properties')
coProperties.reRender()
}, 1024)

this.writeToDisk(tab.path, value)
}

connected () {
Expand All @@ -194,49 +374,19 @@ class AppEditor extends Tonic {
renderLineHighlight: 'none'
})

this.refreshSettings()

const model = this.editor.getModel()

model.onDidChangeContent(async () => {
const currentProject = app.state.currentProject
if (!currentProject) return

const value = this.editor.getValue()
const coTerminal = document.querySelector('app-terminal')

if (currentProject.label === 'settings.json' && currentProject.parent.id === 'root') {
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(() => {
const coProperties = document.querySelector('app-properties')
coProperties.reRender()
}, 1024)

this.writeToDisk(value)
})
this.updateSettings()

window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
this.refreshColors(event.matches ? 'tonic-dark' : 'tonic-light')
this.refreshColors(event)
})

const isDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
this.refreshColors(isDark ? 'tonic-dark' : 'tonic-light')
this.refreshColors()
this.loadAPIs()
}

render () {
return this.html`
<editor-tabs id="editor-tabs" parent=${this}></editor-tabs>
<div class="editor"></div>
`
}
Expand Down
19 changes: 17 additions & 2 deletions src/components/properties.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Tonic from '@socketsupply/tonic'
import fs from 'socket:fs'
import path from 'socket:path'
import process from 'socket:process'
import { exec } from 'socket:child_process'
import { Encryption, sha256 } from 'socket:network'

Expand Down Expand Up @@ -167,8 +168,7 @@ class AppProperties extends Tonic {
if (hasBundle) {
const { data } = await app.db.projects.get(bundleId)
project = data
} else if (cwd) {
console.log('NO BUNDLE FOUND, CREATING', bundleId, cwd)
} else if (currentProject?.isDirectory) {
//
// The clusterId is hard coded for now.
//
Expand Down Expand Up @@ -263,6 +263,21 @@ class AppProperties extends Tonic {
>
${previewWindows}
</tonic-accordion-section>
<tonic-accordion-section
name="build-target"
id="build-target"
label="Build Target"
>
<div class="build-controls">
<tonic-select id="device" value="${process.platform}" title="Build Target Platform">
<option value="ios-simulator" data-value="--platform=ios-simulator">iOS Simulator</option>
<option value="android-emulator" data-value="--platform=android-emulator">Android Emulator</option>
<option value="linux" data-value="" disabled>Linux</option>
<option value="darwin" data-value="">MacOS</option>
<option value="win32" data-value="" disabled>Windows</option>
</tonic-select>
</div>
</tonic-accordion-section>
<h3>Project Settings</h3>
<tonic-accordion-section
Expand Down
8 changes: 6 additions & 2 deletions src/components/sprite.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ class AppSprite extends Tonic {
c0.09-0.09,0.21-0.16,0.31-0.24C46.94,2.33,47.88,2.06,48.82,1.77z"/>
</symbol>
<symbol id="collaborate-icon" viewBox="0 -960 960 960">
<path fill="currentColor" d="M358.769-339.769q24.015 0 41.046-16.738 17.031-16.737 17.031-40.339t-17.031-40.34q-17.031-16.737-41.046-16.737-23.189 0-39.633 16.737-16.444 16.738-16.444 40.34 0 23.602 16.444 40.339 16.444 16.738 39.633 16.738Zm242.462 0q23.602 0 40.339-16.738 16.738-16.737 16.738-40.339t-16.738-40.34q-16.737-16.737-40.339-16.737t-40.724 16.737q-17.122 16.738-17.122 40.34 0 23.602 17.122 40.339 17.122 16.738 40.724 16.738ZM479.956-546.077q24.032 0 41.077-16.66 17.044-16.659 17.044-40.692t-17.031-40.417Q504.014-660.231 480-660.231q-23.959 0-40.402 16.444-16.444 16.444-16.444 40.402 0 24.015 16.384 40.661 16.385 16.647 40.418 16.647ZM480.4-120q-75.176 0-140.294-28.339-65.119-28.34-114.247-77.422-49.127-49.082-77.493-114.213Q120-405.106 120-480.366q0-74.491 28.339-140.069 28.34-65.578 77.422-114.206 49.082-48.627 114.213-76.993Q405.106-840 480.366-840q74.491 0 140.069 28.339 65.578 28.34 114.206 76.922 48.627 48.582 76.993 114.257Q840-554.806 840-480.4q0 75.176-28.339 140.294-28.34 65.119-76.922 114.062-48.582 48.944-114.257 77.494Q554.806-120 480.4-120Zm.1-30.769q136.885 0 232.808-96.039 95.923-96.038 95.923-233.692 0-136.885-95.736-232.808Q617.76-809.231 480-809.231q-137.154 0-233.192 95.736Q150.769-617.76 150.769-480q0 137.154 96.039 233.192 96.038 96.039 233.692 96.039ZM480-480Z"/>
</symbol>
<symbol id="search-icon" viewBox="0 0 100 100">
<path d="M55.4,18c-13.8,0-25.1,11.2-25.1,25.1c0,5.6,1.9,10.7,5,14.9L17.1,76.2l5.2,5.2l18.2-18.2c4.2,3.1,9.3,5,14.9,5
c13.8,0,25.1-11.2,25.1-25.1S69.3,18,55.4,18z M55.4,60.8c-9.8,0-17.8-8-17.8-17.8s8-17.8,17.8-17.8s17.8,8,17.8,17.8
Expand Down Expand Up @@ -207,12 +211,12 @@ class AppSprite extends Tonic {
c0,0.7,0.5,1.3,1.2,1.3C84.6,70,85.1,69.4,85.1,68.7z"/>
</symbol>
<symbol id="plus" viewBox="0 0 100 100">
<symbol id="plus-icon" viewBox="0 0 100 100">
<line fill="none" stroke="currentColor" stroke-width="4" stroke-miterlimit="0" x1="46.5" y1="15.4" x2="46.5" y2="86.7"/>
<line fill="none" stroke="currentColor" stroke-width="4" stroke-miterlimit="0" x1="82.2" y1="51.1" x2="10.9" y2="51.1"/>
</symbol>
<symbol id="package" viewBox="0 0 100 100">
<symbol id="package-icon" viewBox="0 0 100 100">
<path fill="none" stroke="currentColor" stroke-width="2"
d="M89.2,26.3L89.2,26.3L70,14.9L50.9,3.4l0,0c-0.1,0-0.1-0.1-0.2-0.1c-0.1,0-0.2,0-0.2,0.1l0,0l-38.5,23l0,0l-0.1,0.1
c0,0,0,0,0,0.1c0,0.1-0.1,0.1-0.1,0.2v46c0,0.2,0.1,0.3,0.2,0.4l38.3,23l0,0c0.1,0,0.1,0.1,0.2,0.1s0.2,0,0.2-0.1l0,0l38.5-23
Expand Down
1 change: 1 addition & 0 deletions src/components/subscribe.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export class DialogSubscribe extends TonicDialog {
}

await app.db.projects.put(bundleId, project)
await app.initNetwork()
}
}

Expand Down
108 changes: 102 additions & 6 deletions src/css/component-editor.css

Large diffs are not rendered by default.

Loading

0 comments on commit c6d26fa

Please sign in to comment.