From a76d65da238501dc0687bd6e89daae7f144448bf Mon Sep 17 00:00:00 2001 From: Fred Sauer Date: Fri, 20 Feb 2026 19:34:20 -0800 Subject: [PATCH] reformat src/ide/ui.ts --- src/ide/ui.ts | 600 +++++++++++++++++++++++++------------------------- 1 file changed, 301 insertions(+), 299 deletions(-) diff --git a/src/ide/ui.ts b/src/ide/ui.ts index f0f6b268..68edb4e6 100644 --- a/src/ide/ui.ts +++ b/src/ide/ui.ts @@ -2,93 +2,95 @@ // 8bitworkshop IDE user interface import * as localforage from "localforage"; -import { CodeProject, createNewPersistentStore, LocalForageFilesystem, OverlayFilesystem, ProjectFilesystem, WebPresetsFileSystem } from "./project"; -import { WorkerResult, WorkerError, FileData } from "../common/workertypes"; -import { ProjectWindows } from "./windows"; -import { Platform, Preset, DebugSymbols, DebugEvalCondition, isDebuggable, EmuState } from "../common/baseplatform"; -import { PLATFORMS, EmuHalt } from "../common/emu"; -import { Toolbar } from "./toolbar"; -import { getFilenameForPath, getFilenamePrefix, highlightDifferences, byteArrayToString, compressLZG, stringToByteArray, - byteArrayToUTF8, isProbablyBinary, getWithBinary, getBasePlatform, getRootBasePlatform, hex, loadScript, decodeQueryString, parseBool, getCookie } from "../common/util"; +import { DebugEvalCondition, DebugSymbols, EmuState, isDebuggable, Platform, Preset } from "../common/baseplatform"; +import { EmuHalt, PLATFORMS } from "../common/emu"; import { StateRecorderImpl } from "../common/recorder"; -import { getRepos, parseGithubURL } from "./services"; -import Split = require('split.js'); +import { + byteArrayToUTF8, decodeQueryString, getBasePlatform, getCookie, getFilenameForPath, getFilenamePrefix, + getRootBasePlatform, getWithBinary, hex, highlightDifferences, isProbablyBinary, loadScript, parseBool, stringToByteArray +} from "../common/util"; +import { FileData, WorkerError, WorkerResult } from "../common/workertypes"; import { importPlatform } from "../platform/_index"; -import { DisassemblerView, ListingView, PC_LINE_LOOKAHEAD , SourceEditor } from "./views/editors"; -import { AddressHeatMapView, BinaryFileView, MemoryMapView, MemoryView, ProbeLogView, ProbeSymbolView, RasterStackMapView, ScanlineIOView, VRAMMemoryView } from "./views/debugviews"; +import { gaEvent, gaPageView } from "./analytics"; +import { alertError, alertInfo, fatalError, setWaitDialog } from "./dialogs"; +import { CodeProject, createNewPersistentStore, LocalForageFilesystem, OverlayFilesystem, ProjectFilesystem, WebPresetsFileSystem } from "./project"; +import { getRepos, parseGithubURL } from "./services"; +import { _downloadAllFilesZipFile, _downloadCassetteFile, _downloadProjectZipFile, _downloadROMImage, _downloadSourceFile, _downloadSymFile, _getCassetteFunction, _recordVideo, _shareEmbedLink } from "./shareexport"; +import { _importProjectFromGithub, _loginToGithub, _logoutOfGithub, _publishProjectToGithub, _pullProjectFromGithub, _pushProjectToGithub, _removeRepository, importProjectFromGithub } from "./sync"; +import { Toolbar } from "./toolbar"; import { AssetEditorView } from "./views/asseteditor"; import { isMobileDevice } from "./views/baseviews"; +import { AddressHeatMapView, BinaryFileView, MemoryMapView, MemoryView, ProbeLogView, ProbeSymbolView, RasterStackMapView, ScanlineIOView, VRAMMemoryView } from "./views/debugviews"; +import { DisassemblerView, ListingView, PC_LINE_LOOKAHEAD, SourceEditor } from "./views/editors"; import { CallStackView, DebugBrowserView } from "./views/treeviews"; +import { ProjectWindows } from "./windows"; +import Split = require('split.js'); import DOMPurify = require("dompurify"); -import { alertError, alertInfo, fatalError, setWaitDialog, setWaitProgress } from "./dialogs"; -import { _importProjectFromGithub, _loginToGithub, _logoutOfGithub, _publishProjectToGithub, _pullProjectFromGithub, _pushProjectToGithub, _removeRepository, importProjectFromGithub } from "./sync"; -import { gaEvent, gaPageView } from "./analytics"; -import { _downloadAllFilesZipFile, _downloadCassetteFile, _downloadProjectZipFile, _downloadROMImage, _downloadSourceFile, _downloadSymFile, _getCassetteFunction, _recordVideo, _shareEmbedLink } from "./shareexport"; // external libs (TODO) declare var Tour; -declare var $ : JQueryStatic; // use browser jquery +declare var $: JQueryStatic; // use browser jquery -// query string +// query string interface UIQueryString { - platform? : string; + platform?: string; options?: string; - repo? : string; - file? : string; - electron? : string; - importURL? : string; - githubURL? : string; - localfs? : string; - newfile? : string; - embed? : string; - ignore? : string; - force? : string; - highlight? : string; - file0_name? : string; - file0_data? : string; - file0_type? : string; + repo?: string; + file?: string; + electron?: string; + importURL?: string; + githubURL?: string; + localfs?: string; + newfile?: string; + embed?: string; + ignore?: string; + force?: string; + highlight?: string; + file0_name?: string; + file0_data?: string; + file0_type?: string; tool?: string; } /// EXPORTED GLOBALS (TODO: remove) -export var qs = decodeQueryString(window.location.search||'?') as UIQueryString; +export var qs = decodeQueryString(window.location.search || '?') as UIQueryString; -export var platform_id : string; // platform ID string (platform) -export var store_id : string; // store ID string (repo || platform) -export var repo_id : string; // repository ID (repo) -export var platform : Platform; // emulator object -export var current_project : CodeProject; // current CodeProject object -export var projectWindows : ProjectWindows; // window manager -export var lastDebugState : EmuState; // last debug state (object) +export var platform_id: string; // platform ID string (platform) +export var store_id: string; // store ID string (repo || platform) +export var repo_id: string; // repository ID (repo) +export var platform: Platform; // emulator object +export var current_project: CodeProject; // current CodeProject object +export var projectWindows: ProjectWindows; // window manager +export var lastDebugState: EmuState; // last debug state (object) // private globals var compparams; // received build params from worker -var platform_name : string; // platform name (after setPlatformUI) +var platform_name: string; // platform name (after setPlatformUI) var toolbar = $("#controls_top"); -var uitoolbar : Toolbar; -var stateRecorder : StateRecorderImpl; -var userPaused : boolean; // did user explicitly pause? -var current_output : any; // current ROM (or other object) -var current_preset : Preset; // current preset object (if selected) -var store : LocalForage; // persistent store +var uitoolbar: Toolbar; +var stateRecorder: StateRecorderImpl; +var userPaused: boolean; // did user explicitly pause? +var current_output: any; // current ROM (or other object) +var current_preset: Preset; // current preset object (if selected) +var store: LocalForage; // persistent store const isElectron = parseBool(qs.electron); const isEmbed = parseBool(qs.embed); -type DebugCommandType = null - | 'toline' | 'step' | 'stepout' | 'stepover' +type DebugCommandType = null + | 'toline' | 'step' | 'stepout' | 'stepover' | 'tovsync' | 'stepback' | 'restart'; var lastDebugInfo; // last debug info (CPU text) var debugCategory; // current debug category var debugTickPaused = false; var recorderActive = false; -var lastViewClicked : string = null; -var lastDebugCommand : DebugCommandType = null; +var lastViewClicked: string = null; +var lastDebugCommand: DebugCommandType = null; var errorWasRuntime = false; var lastBreakExpr = "c.PC == 0x6000"; @@ -159,12 +161,12 @@ const TOOL_TO_HELPURL = { 'acme': 'https://raw.githubusercontent.com/sehugg/acme/main/docs/QuickRef.txt', } -function newWorker() : Worker { +function newWorker(): Worker { // TODO: return new Worker("https://8bitworkshop.com.s3-website-us-east-1.amazonaws.com/dev/gen/worker/bundle.js"); return new Worker("./gen/worker/bundle.js"); } -const hasLocalStorage : boolean = function() { +const hasLocalStorage: boolean = function () { try { const key = "__some_random_key_you_are_not_going_to_use__"; localStorage.setItem(key, key); @@ -178,7 +180,7 @@ const hasLocalStorage : boolean = function() { // wrapper for localstorage class UserPrefs { - setLastPreset(id:string) { + setLastPreset(id: string) { if (hasLocalStorage && !isEmbed) { if (repo_id && platform_id && !isElectron) localStorage.setItem("__lastrepo_" + platform_id, repo_id); @@ -191,11 +193,11 @@ class UserPrefs { unsetLastPreset() { if (hasLocalStorage && !isEmbed) { delete qs.file; - localStorage.removeItem("__lastid_"+store_id); + localStorage.removeItem("__lastid_" + store_id); } } getLastPreset() { - return hasLocalStorage && !isEmbed && localStorage.getItem("__lastid_"+store_id); + return hasLocalStorage && !isEmbed && localStorage.getItem("__lastid_" + store_id); } getLastPlatformID() { return hasLocalStorage && !isEmbed && localStorage.getItem("__lastplatform"); @@ -216,7 +218,7 @@ var userPrefs = new UserPrefs(); // https://developers.google.com/web/updates/2016/06/persistent-storage function requestPersistPermission(interactive: boolean, failureonly: boolean) { if (navigator.storage && navigator.storage.persist) { - navigator.storage.persist().then(persistent=>{ + navigator.storage.persist().then(persistent => { console.log("requestPersistPermission =", persistent); if (persistent) { interactive && !failureonly && alertInfo("Your browser says it will persist your local file edits, but you may want to back up your work anyway."); @@ -229,7 +231,7 @@ function requestPersistPermission(interactive: boolean, failureonly: boolean) { } } -function getCurrentPresetTitle() : string { +function getCurrentPresetTitle(): string { if (!current_preset) return current_project.mainPath || "ROM"; else @@ -237,7 +239,7 @@ function getCurrentPresetTitle() : string { } async function newFilesystem() { - var basefs : ProjectFilesystem = new WebPresetsFileSystem(platform_id); + var basefs: ProjectFilesystem = new WebPresetsFileSystem(platform_id); if (isElectron) { console.log('using electron with local filesystem', alternateLocalFilesystem); return new OverlayFilesystem(basefs, alternateLocalFilesystem); @@ -253,10 +255,10 @@ async function initProject() { current_project = new CodeProject(newWorker(), platform_id, platform, filesystem); current_project.remoteTool = qs.tool || null; projectWindows = new ProjectWindows($("#workspace")[0] as HTMLElement, current_project); - current_project.callbackBuildResult = (result:WorkerResult) => { + current_project.callbackBuildResult = (result: WorkerResult) => { setCompileOutput(result); }; - current_project.callbackBuildStatus = (busy:boolean) => { + current_project.callbackBuildStatus = (busy: boolean) => { setBusyStatus(busy); }; } @@ -280,7 +282,7 @@ function newDropdownListItem(id, text) { $(a).addClass("dropdown-item-checked"); a.appendChild(document.createTextNode(text)); li.appendChild(a); - return {li, a}; + return { li, a }; } function refreshWindowList() { @@ -292,7 +294,7 @@ function refreshWindowList() { ul.append(document.createElement("hr")); separate = false; } - let {li,a} = newDropdownListItem(id, name); + let { li, a } = newDropdownListItem(id, name); ul.append(li); if (createfn) { var onopen = (id, wnd) => { @@ -301,14 +303,14 @@ function refreshWindowList() { }; projectWindows.setCreateFunc(id, createfn); projectWindows.setShowFunc(id, onopen); - $(a).click( (e) => { + $(a).click((e) => { projectWindows.createOrShow(id); lastViewClicked = id; }); } } - function loadEditor(path:string) { + function loadEditor(path: string) { var tool = platform.getToolForFilename(path); // hack because .h files can be DASM or CC65 if (tool == 'dasm' && path.endsWith(".h") && getCurrentMainFilename().endsWith(".c")) { @@ -318,7 +320,7 @@ function refreshWindowList() { return new SourceEditor(path, mode); } - function addEditorItem(id:string) { + function addEditorItem(id: string) { addWindowItem(id, getFilenameForPath(id), () => { var data = current_project.getFile(id); if (typeof data === 'string') @@ -332,7 +334,7 @@ function refreshWindowList() { addEditorItem(current_project.mainPath); // add other source files - current_project.iterateFiles( (id, text) => { + current_project.iterateFiles((id, text) => { if (text && id != current_project.mainPath) { addEditorItem(id); } @@ -412,7 +414,7 @@ function refreshWindowList() { }); } -function highlightLines(path:string, hispec:string) { +function highlightLines(path: string, hispec: string) { if (hispec) { var toks = qs.highlight.split(','); var start = parseInt(toks[0]) - 1; @@ -422,7 +424,7 @@ function highlightLines(path:string, hispec:string) { } } -function loadMainWindow(preset_id:string) { +function loadMainWindow(preset_id: string) { // we need this to build create functions for the editor refreshWindowList(); // show main file @@ -433,7 +435,7 @@ function loadMainWindow(preset_id:string) { highlightLines(preset_id, qs.highlight); } -async function loadProject(preset_id:string) { +async function loadProject(preset_id: string) { // set current file ID // TODO: this is done twice (mainPath and mainpath!) current_project.mainPath = preset_id; @@ -458,14 +460,14 @@ async function loadProject(preset_id:string) { } } -function reloadProject(id:string) { +function reloadProject(id: string) { // leave repository == '/' if (id == '/') { - qs = {repo:'/'}; + qs = { repo: '/' }; } else if (id.indexOf('://') >= 0) { var urlparse = parseGithubURL(id); if (urlparse) { - qs = {repo:urlparse.repopath}; + qs = { repo: urlparse.repopath }; } } else { qs.platform = platform_id; @@ -474,16 +476,16 @@ function reloadProject(id:string) { gotoNewLocation(); } -async function getSkeletonFile(fileid:string) : Promise { +async function getSkeletonFile(fileid: string): Promise { var ext = platform.getToolForFilename(fileid); try { - return await $.get( "presets/"+getBasePlatform(platform_id)+"/skeleton."+ext, 'text'); - } catch(e) { + return await $.get("presets/" + getBasePlatform(platform_id) + "/skeleton." + ext, 'text'); + } catch (e) { alertError("Could not load skeleton for " + platform_id + "/" + ext + "; using blank file"); } } -function checkEnteredFilename(fn : string) : boolean { +function checkEnteredFilename(fn: string): boolean { if (fn.indexOf(" ") >= 0) { alertError("No spaces in filenames, please."); return false; @@ -494,9 +496,9 @@ function checkEnteredFilename(fn : string) : boolean { function _createNewFile(e) { // TODO: support spaces bootbox.prompt({ - title:"Enter the name of your new main source file.", - placeholder:"newfile" + platform.getDefaultExtension(), - callback:(filename) => { + title: "Enter the name of your new main source file.", + placeholder: "newfile" + platform.getDefaultExtension(), + callback: (filename) => { if (filename && filename.trim().length > 0) { if (!checkEnteredFilename(filename)) return; if (filename.indexOf(".") < 0) { @@ -550,14 +552,14 @@ function handleFileUpload(files: FileList) { } else { var path = f.name; var reader = new FileReader(); - reader.onload = function(e) { + reader.onload = function (e) { var arrbuf = (e.target).result as ArrayBuffer; - var data : FileData = new Uint8Array(arrbuf); + var data: FileData = new Uint8Array(arrbuf); // convert to UTF8, unless it's a binary file if (isProbablyBinary(path, data)) { //gotoMainFile = false; } else { - data = byteArrayToUTF8(data).replace('\r\n','\n'); // convert CRLF to LF + data = byteArrayToUTF8(data).replace('\r\n', '\n'); // convert CRLF to LF } // store in local forage projectWindows.updateFile(path, data); @@ -586,26 +588,26 @@ async function _openLocalDirectory(e) { version: 2.0 }); await lstore.setItem(storekey, fsdata); - qs = {localfs: repoid}; + qs = { localfs: repoid }; gotoNewLocation(true); } -async function promptUser(message: string) : Promise { - return new Promise( (resolve, reject) => { +async function promptUser(message: string): Promise { + return new Promise((resolve, reject) => { bootbox.prompt(DOMPurify.sanitize(message), (result) => { resolve(result); }); }); } -async function getLocalFilesystem(repoid: string) : Promise { - const options = {mode:'readwrite'}; +async function getLocalFilesystem(repoid: string): Promise { + const options = { mode: 'readwrite' }; var storekey = '__localfs__' + repoid; var lstore = localforage.createInstance({ name: storekey, version: 2.0 }); - var fsdata : any = await lstore.getItem(storekey); + var fsdata: any = await lstore.getItem(storekey); var dirHandle = fsdata.handle as any; console.log(fsdata, dirHandle); var granted = await dirHandle.queryPermission(options); @@ -615,8 +617,8 @@ async function getLocalFilesystem(repoid: string) : Promise { granted = await dirHandle.requestPermission(options); } if (granted !== 'granted') { - alertError(`Could not get permission to access filesystem.`); - return; + alertError(`Could not get permission to access filesystem.`); + return; } return { getFileData: async (path) => { @@ -635,11 +637,11 @@ async function getLocalFilesystem(repoid: string) : Promise { } } -export function getCurrentMainFilename() : string { +export function getCurrentMainFilename(): string { return getFilenameForPath(current_project.mainPath); } -export function getCurrentEditorFilename() : string { +export function getCurrentEditorFilename(): string { return getFilenameForPath(projectWindows.getActiveID()); } @@ -648,17 +650,17 @@ function _revertFile(e) { var wnd = projectWindows.getActive(); if (wnd && wnd.setText) { var fn = projectWindows.getActiveID(); - $.get( "presets/"+getBasePlatform(platform_id)+"/"+fn, (text) => { + $.get("presets/" + getBasePlatform(platform_id) + "/" + fn, (text) => { bootbox.confirm("Reset '" + DOMPurify.sanitize(fn) + "' to default?", (ok) => { if (ok) { wnd.setText(text); } }); }, 'text') - .fail(() => { - if (repo_id) alertError("Can only revert built-in examples. If you want to revert all files, You can pull from the repository."); - else alertError("Can only revert built-in examples."); - }); + .fail(() => { + if (repo_id) alertError("Can only revert built-in examples. If you want to revert all files, You can pull from the repository."); + else alertError("Can only revert built-in examples."); + }); } else { alertError("Cannot revert the active window. Please choose a text file."); } @@ -670,7 +672,7 @@ function _deleteFile(e) { var fn = projectWindows.getActiveID(); bootbox.confirm("Delete '" + DOMPurify.sanitize(fn) + "'?", (ok) => { if (ok) { - store.removeItem(fn).then( () => { + store.removeItem(fn).then(() => { // if we delete what is selected if (qs.file == fn) { userPrefs.unsetLastPreset(); @@ -692,15 +694,15 @@ function _renameFile(e) { if (wnd && wnd.getPath && current_project.getFile(wnd.getPath())) { var fn = projectWindows.getActiveID(); bootbox.prompt({ - title: "Rename '" + DOMPurify.sanitize(fn) + "' to?", + title: "Rename '" + DOMPurify.sanitize(fn) + "' to?", value: fn, callback: (newfn) => { var data = current_project.getFile(wnd.getPath()); if (newfn && newfn != fn && data) { if (!checkEnteredFilename(newfn)) return; - store.removeItem(fn).then( () => { + store.removeItem(fn).then(() => { return store.setItem(newfn, data); - }).then( () => { + }).then(() => { updateSelector(); alert("Renamed " + fn + " to " + newfn); // need alert() so it pauses if (fn == current_project.mainPath) { @@ -721,16 +723,16 @@ function populateExamples(sel) { let files = {}; let optgroup; const PRESETS = platform.getPresets ? platform.getPresets() : []; - for (var i=0; i").attr('label','Examples: ' + preset.category).appendTo(sel); + optgroup = $("").attr('label', 'Examples: ' + preset.category).appendTo(sel); } else if (!optgroup) { - optgroup = $("").attr('label','Examples').appendTo(sel); + optgroup = $("").attr('label', 'Examples').appendTo(sel); } - optgroup.append($("").attr('label','Repositories').appendTo(sel); + let optgroup = $("").attr('label', 'Repositories').appendTo(sel); for (let repopath in repos) { var repo = repos[repopath]; if (repo.platform_id && getBasePlatform(repo.platform_id) == getBasePlatform(platform_id)) { @@ -753,26 +755,26 @@ function populateRepos(sel) { } } -async function populateFiles(sel:JQuery, category:string, prefix:string, foundFiles:{}) { +async function populateFiles(sel: JQuery, category: string, prefix: string, foundFiles: {}) { let keys = await store.keys(); if (!keys) keys = []; let optgroup; for (var i = 0; i < keys.length; i++) { let key = keys[i]; if (key.startsWith(prefix) && !foundFiles[key]) { - if (!optgroup) optgroup = $("").attr('label',category).appendTo(sel); + if (!optgroup) optgroup = $("").attr('label', category).appendTo(sel); let name = key.substring(prefix.length); - optgroup.append($("