diff --git a/core/client.js b/core/client.js index f589d1d5..ef9e36cf 100644 --- a/core/client.js +++ b/core/client.js @@ -1,5 +1,6 @@ import {LitElement, html, css, svg} from '/static/lit/lit-all.min.js'; +let cm6; let gSocket; let gCurrentFile; @@ -201,7 +202,7 @@ class TfFilesElement extends LitElement { let buffer = await file.arrayBuffer(); let text = new TextDecoder('latin1').decode(buffer); gFiles[file.name] = { - doc: new CodeMirror.Doc(text, guessMode(file.name)), + doc: new cm6.EditorState.create({doc: text, extensions: cm6.extensions}), buffer: buffer, generation: -1, isNew: true, @@ -435,7 +436,7 @@ function is_edit_only() { return window.location.search == '?editonly=1' || window.innerWidth < 1024; } -function edit() { +async function edit() { if (editing()) { return; } @@ -444,33 +445,15 @@ function edit() { document.getElementById("editPane").style.display = 'flex'; document.getElementById('viewPane').style.display = is_edit_only() ? 'none' : 'flex'; - ensureLoaded([ - {tagName: "script", attributes: {src: "/codemirror/codemirror.min.js"}}, - {tagName: "link", attributes: {rel: "stylesheet", href: "/codemirror/base16-dark.min.css"}}, - {tagName: "link", attributes: {rel: "stylesheet", href: "/codemirror/matchesonscrollbar.min.css"}}, - {tagName: "link", attributes: {rel: "stylesheet", href: "/codemirror/dialog.min.css"}}, - {tagName: "link", attributes: {rel: "stylesheet", href: "/codemirror/codemirror.min.css"}}, - {tagName: "link", attributes: {rel: "stylesheet", href: "/codemirror/lint.css"}}, - {tagName: "script", attributes: {src: "/codemirror/trailingspace.min.js"}}, - {tagName: "script", attributes: {src: "/codemirror/dialog.min.js"}}, - {tagName: "script", attributes: {src: "/codemirror/search.min.js"}}, - {tagName: "script", attributes: {src: "/codemirror/searchcursor.min.js"}}, - {tagName: "script", attributes: {src: "/codemirror/jump-to-line.min.js"}}, - {tagName: "script", attributes: {src: "/codemirror/matchesonscrollbar.min.js"}}, - {tagName: "script", attributes: {src: "/codemirror/annotatescrollbar.min.js"}}, - {tagName: "script", attributes: {src: "/codemirror/javascript.min.js"}}, - {tagName: "script", attributes: {src: "/codemirror/css.min.js"}}, - {tagName: "script", attributes: {src: "/codemirror/xml.min.js"}}, - {tagName: "script", attributes: {src: "/codemirror/htmlmixed.min.js"}}, - {tagName: "script", attributes: {src: "/codemirror/lint.js"}}, - {tagName: "script", attributes: {src: "/codemirror/jshint.min.js"}}, - {tagName: "script", attributes: {src: "/codemirror/javascript-lint.min.js"}}, - ], function() { - load().catch(function(error) { - alert(error); - closeEditor(); - }); - }); + try { + cm6 = await import('/codemirror/cm6.js'); + gEditor = cm6.TildeFriendsEditorView(document.getElementById("editor")); + gEditor.onDocChange = updateFiles; + await load(); + } catch (error) { + alert(`${error.message}\n\n${error.stack}`); + closeEditor(); + } } function trace() { @@ -491,73 +474,51 @@ function loadFile(name, id) { } return response.text(); }).then(function(text) { - gFiles[name].doc = new CodeMirror.Doc(text, guessMode(name)); + gFiles[name].doc = cm6.EditorState.create({doc: text, extensions: cm6.extensions}); + gFiles[name].original = gFiles[name].doc.doc.toString(); if (!Object.values(gFiles).some(x => !x.doc)) { openFile(Object.keys(gFiles).sort()[0]); } }); } -function load(path) { - return fetch((path || url()) + 'view').then(function(response) { - if (!response.ok) { - if (response.status == 404) { - return null; - } else { - throw new Error(response.status + ' ' + response.statusText); - } +async function load(path) { + let response = await fetch((path || url()) + 'view'); + if (!response.ok) { + if (response.status == 404) { + return null; + } else { + throw new Error(response.status + ' ' + response.statusText); } - return response.json(); - }).then(function(json) { - if (!gEditor) { - gEditor = CodeMirror.fromTextArea(document.getElementById("editor"), { - 'theme': 'base16-dark', - 'lineNumbers': true, - 'tabSize': 4, - 'indentUnit': 4, - 'indentWithTabs': true, - 'showTrailingSpace': true, - 'gutters': ['CodeMirror-lint-markers'], - 'mode': {'js': 'javascript'}[(path || url()).split('.').pop()], - 'lint': { - 'options': { - esversion: 2021, - varstmt: true, - }, - }, - }); - gEditor.on('changes', function() { - updateFiles(); - }); - } - gFiles = {}; - let isApp = false; - let promises = []; + } + let json = await response.json(); + gFiles = {}; + let isApp = false; + let promises = []; - if (json && json['type'] == 'tildefriends-app') { - isApp = true; - Object.keys(json['files']).forEach(function(name) { - gFiles[name] = {}; - promises.push(loadFile(name, json['files'][name])); - }); - if (Object.keys(json['files']).length == 0) { - document.getElementById("editPane").style.display = 'flex'; - } - gApp = json; - gApp.emoji = gApp.emoji || '📦'; - document.getElementById('icon').innerHTML = gApp.emoji; - } - if (!isApp) { + if (json && json['type'] == 'tildefriends-app') { + isApp = true; + Object.keys(json['files']).forEach(function(name) { + gFiles[name] = {}; + promises.push(loadFile(name, json['files'][name])); + }); + if (Object.keys(json['files']).length == 0) { document.getElementById("editPane").style.display = 'flex'; - let text = '// New script.\n'; - gCurrentFile = 'app.js'; - gFiles[gCurrentFile] = { - doc: new CodeMirror.Doc(text, guessMode(gCurrentFile)), - }; - openFile(gCurrentFile); } - return Promise.all(promises); - }); + gApp = json; + gApp.emoji = gApp.emoji || '📦'; + document.getElementById('icon').innerHTML = gApp.emoji; + } + if (!isApp) { + document.getElementById("editPane").style.display = 'flex'; + let text = '// New script.\n'; + gCurrentFile = 'app.js'; + gFiles[gCurrentFile] = { + doc: cm6.EditorState.create({doc: text, extensions: cm6.extensions}), + }; + openFile(gCurrentFile); + } + return Promise.all(promises); } function closeEditor() { @@ -573,8 +534,8 @@ function explodePath() { function save(save_to) { document.getElementById("save").disabled = true; if (gCurrentFile) { - gFiles[gCurrentFile].doc = gEditor.getDoc(); - if (!gFiles[gCurrentFile].isNew && !gFiles[gCurrentFile].doc.isClean(gFiles[gCurrentFile].doc.changeGeneration())) { + gFiles[gCurrentFile].doc = gEditor.state; + if (!gFiles[gCurrentFile].isNew && !gFiles[gCurrentFile].doc.doc.toString() == gFiles[gCurrentFile].original) { delete gFiles[gCurrentFile].buffer; } } @@ -592,7 +553,7 @@ function save(save_to) { let promises = []; for (let name of Object.keys(gFiles)) { let file = gFiles[name]; - if (!file.isNew && file.doc.isClean(file.generation)) { + if (!file.isNew && file.doc.doc.toString() == file.original) { continue; } @@ -603,7 +564,7 @@ function save(save_to) { headers: { 'Content-Type': 'application/binary', }, - body: file.buffer ?? file.doc.getValue(), + body: file.buffer ?? file.doc.doc.toString(), }).then(function(response) { if (!response.ok) { throw new Error('Saving "' + name + '": ' + response.status + ' ' + response.statusText); @@ -648,7 +609,7 @@ function save(save_to) { }).finally(function() { document.getElementById("save").disabled = false; Object.values(gFiles).forEach(function(file) { - file.generation = file.doc.changeGeneration(); + file.original = file.doc.doc.toString(); }); updateFiles(); }); @@ -1029,11 +990,13 @@ function connectSocket(path) { } function openFile(name) { - let newDoc = (name && gFiles[name]) ? gFiles[name].doc : new CodeMirror.Doc("", guessMode(name)); - let oldDoc = gEditor.swapDoc(newDoc); + let newDoc = (name && gFiles[name]) ? gFiles[name].doc : cm6.EditorState.create({doc: "", extensions: cm6.extensions}); + let oldDoc = gEditor.state; + gEditor.setState(newDoc); + if (gFiles[gCurrentFile]) { gFiles[gCurrentFile].doc = oldDoc; - if (!gFiles[gCurrentFile].isNew && gFiles[gCurrentFile].doc.isClean(oldDoc.generation)) { + if (!gFiles[gCurrentFile].isNew && gFiles[gCurrentFile].doc.doc.toString() == oldDoc.doc.toString()) { delete gFiles[gCurrentFile].buffer; } } @@ -1046,7 +1009,7 @@ function updateFiles() { let files = document.getElementsByTagName("tf-files-pane")[0]; if (files) { files.files = Object.fromEntries(Object.keys(gFiles).map(file => [file, { - clean: gFiles[file].doc.isClean(gFiles[file].generation), + clean: (file == gCurrentFile ? gEditor.state.doc.toString() : gFiles[file].doc.doc.toString()) == gFiles[file].original, }])); files.current = gCurrentFile; } @@ -1055,7 +1018,7 @@ function updateFiles() { function makeNewFile(name) { gFiles[name] = { - doc: new CodeMirror.Doc("", guessMode(name)), + doc: cm6.EditorState.create({extensions: cm6.extensions}), generation: -1, }; openFile(name); diff --git a/core/index.html b/core/index.html index 8052543a..988f3c2d 100644 --- a/core/index.html +++ b/core/index.html @@ -27,11 +27,7 @@