Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/ViperIDE.html
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@
<label for="zoom">Zoom:</label>
<select id="zoom">
<option value="0.80">80%</option>
<option value="1.00">100%</option>
<option value="1.00" selected>100%</option>
<option value="1.10">110%</option>
<option value="1.25">125%</option>
<option value="1.50">150%</option>
Expand Down
33 changes: 17 additions & 16 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { ConnectionUID } from './connection_uid.js'
import translations from '../build/translations.json'
import { parseStackTrace, validatePython, disassembleMPY, minifyPython, prettifyPython } from './python_utils.js'
import { MicroPythonWASM } from './emulator.js'
import { getSetting, onSettingChange, updateSetting } from './settings.js'

import { marked } from 'marked'
import { UAParser } from 'ua-parser-js'
Expand Down Expand Up @@ -176,7 +177,7 @@ async function prepareNewPort(type) {
toastr.error('Try Chrome, Edge, Opera, Brave', 'WebSerial and WebUSB are not supported')
return
}
if (typeof navigator.serial === 'undefined' || QID('force-serial-poly').checked) {
if (typeof navigator.serial === 'undefined' || getSetting('force-serial-poly')) {
console.log('Using WebSerial polyfill')
new_port = new WebSerial(webSerialPolyfill)
} else {
Expand Down Expand Up @@ -231,7 +232,7 @@ export async function connectDevice(type) {

analytics.track('Device Port Connected', Object.assign({ connection: type }, await port.getInfo()))

if (QID('interrupt-device').checked) {
if (getSetting('interrupt-device')) {
// TODO: detect WDT and disable it temporarily

const raw = await MpRawMode.begin(port)
Expand Down Expand Up @@ -438,7 +439,7 @@ function _updateFileTree(fs_tree, fs_stats)
QS(`#menu-file-tree [data-fn="${fn}"]`).classList.add("open")
}

if (QID('advanced-mode').checked) {
if (getSetting("advanced-mode")) {
fileTree.insertAdjacentHTML('beforeend', `<div>
<a href="#" class="name" onclick="app.fileClick('~sysinfo.md');return false;"><i class="fa-regular fa-message fa-fw"></i> sysinfo.md&nbsp;</a>
<span class="menu-action">virtual</span>
Expand Down Expand Up @@ -539,12 +540,12 @@ async function _loadContent(fn, content, editorElement) {
if (content instanceof Uint8Array && !willDisasm) {
hexViewer(content.buffer, editorElement)
editor = null
} else if (fn.endsWith('.md') && QID('render-markdown').checked) {
} else if (fn.endsWith('.md') && getSetting('render-markdown')) {
editorElement.innerHTML = `<div class="marked-viewer">` + marked(content) + `</div>`
editor = null
} else {
let readOnly = false
if (fn.endsWith('.json') && QID('expand-minify-json').checked) {
if (fn.endsWith('.json') && getSetting('expand-minify-json')) {
try {
// Prettify JSON
content = JSON.stringify(JSON.parse(content), null, 2)
Expand All @@ -559,7 +560,7 @@ async function _loadContent(fn, content, editorElement) {

editorElement.innerHTML = '' // Clear existing content
editor = await createNewEditor(editorElement, fn, content, {
wordWrap: QID('use-word-wrap').checked,
wordWrap: getSetting('use-word-wrap'),
devInfo,
readOnly,
})
Expand Down Expand Up @@ -592,7 +593,7 @@ export async function saveCurrentFile() {
}

let content = editor.state.doc.toString()
if (editorFn.endsWith('.json') && QID('expand-minify-json').checked) {
if (editorFn.endsWith('.json') && getSetting('expand-minify-json')) {
try {
// Minify JSON
content = JSON.stringify(JSON.parse(content))
Expand Down Expand Up @@ -731,7 +732,7 @@ async function _raw_installPkg(raw, pkg, { version=null } = {}) {
const pkg_info = await rawInstallPkg(raw, pkg, {
version,
dev: dev_info,
prefer_source: QID('install-package-source').checked,
prefer_source: getSetting('install-package-source'),
})
if (pkg_info.version) {
toastr.success(`Installed ${pkg_info.name}@${pkg_info.version}`)
Expand Down Expand Up @@ -967,10 +968,9 @@ export function applyTranslation() {

const currentLang = i18next.resolvedLanguage || 'en';

const lang_sel = QID('lang')
lang_sel.value = currentLang
lang_sel.addEventListener('change', async function() {
await i18next.changeLanguage(this.value)
updateSetting('lang', currentLang)
onSettingChange('lang', async function(newValue) {
await i18next.changeLanguage(newValue)
applyTranslation()
})

Expand Down Expand Up @@ -1040,10 +1040,8 @@ export function applyTranslation() {
}
}

const zoom_sel = QID('zoom')
zoom_sel.value = '1.00'
zoom_sel.addEventListener('change', async function() {
const size = 14 * parseFloat(this.value)
onSettingChange('zoom', function(newValue) {
const size = 14 * parseFloat(newValue)
document.documentElement.style.setProperty('--font-size', (size).toFixed(1) + 'px')
term.options.fontSize = (size * 0.9).toFixed(1)
})
Expand Down Expand Up @@ -1114,6 +1112,9 @@ export function applyTranslation() {
}
})

// set zoom level in newly created terminal
updateSetting('zoom', getSetting('zoom'))

const fitAddon = new FitAddon()
term.loadAddon(fitAddon)
fitAddon.fit()
Expand Down
123 changes: 123 additions & 0 deletions src/settings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { QID } from './utils.js'


const settingsElement = QID("menu-settings")
let callbacks = new Map()
let settings = _loadSettings()

/**
*
* @param {string} setting The name of the setting to query
* @returns {boolean} Returns the value of the setting if found, else undefined
*/
export function getSetting(setting) {
return settings[setting]
}


/**
*
* @param {string} setting The name of the setting to update
* @param {string|boolean} newValue The new value to set the setting to (string for dropdowns, boolean for checkboxes)
*/
export function updateSetting(setting, newValue) {
// set the DOM
const settingElement = settingsElement.querySelector(`#${setting}`)
if (settingElement.tagName == "SELECT") {
settingElement.value = newValue
} else if (settingElement.type == "checkbox") {
settingElement.checked = newValue
} else {
console.error(`Element is not <select> or <input type="checkbox">: ${settingElement}`)
}

// set our local cache
settings[setting] = newValue

// inform any subscribers
_notify(setting, newValue)

// persist to local storage
_persistSettings(settings)
}


/**
*
* @param {string} setting The name of the setting to set a callback for
* @param {function(string):void} callback A callback function that will receive the new value of the setting
*/
export function onSettingChange(setting, callback) {
if (!callbacks.has(setting)) {
callbacks.set(setting, [])
}
callbacks.get(setting).push(callback)
}


settingsElement.addEventListener("change", (event) => {
settings = _persistSettings()
_notify(event.target.id, settings[event.target.id])
})


function _loadSettings() {
// get settings from either localstorage or read from the DOM (and populate local storage)
let loadedSettings = JSON.parse(localStorage.getItem("settings"))
if (!loadedSettings) {
_persistSettings()
loadedSettings = JSON.parse(localStorage.getItem("settings"))
}

function _setLoadedValue(setting, loadedValue, domValue, setter) {
// if we loaded nothing, then don't try to set the DOM (perhaps a brand new setting and
// therefore should use the default)
if (loadedValue != undefined) {
// set the loaded value to the DOM
setter(loadedValue)
} else {
loadedSettings[setting] = domValue
loadedValue = domValue
}

// notify any code that might need to know about what we loaded
_notify(setting, loadedValue)
}

// loop over all DOM settings elements and load them with the value from local storage
settingsElement.querySelectorAll("input[type='checkbox']").forEach(element => {
_setLoadedValue(element.id, loadedSettings[element.id], element.checked, (value) => element.checked = value)
})
settingsElement.querySelectorAll("select").forEach(element => {
_setLoadedValue(element.id, loadedSettings[element.id], element.value, (value) => element.value = value)
})

return loadedSettings
}


function _persistSettings(newSettings = undefined) {
if (!newSettings) {
// nothing passed into us, so lets read from the DOM and persist that
newSettings = new Object()
settingsElement.querySelectorAll("input[type='checkbox']").forEach(element => {
newSettings[element.id] = element.checked
})
settingsElement.querySelectorAll("select").forEach(element => {
newSettings[element.id] = element.value
})
}

localStorage.setItem("settings", JSON.stringify(newSettings))
return newSettings
}


function _notify(setting, newValue) {
// If there are any callbacks for this setting update, then let's call them
if (callbacks.has(setting)) {
for (let callback of callbacks.get(setting)) {
callback(newValue)
}
}
}