diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml index bccc091..275e89d 100644 --- a/.github/workflows/default.yml +++ b/.github/workflows/default.yml @@ -29,12 +29,12 @@ jobs: restore-keys: | ${{ runner.os }}-node- - name: install dependencies - run: npm install + run: npm ci - name: build run: npm run build - name: package run: npm run package - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: truncated-cube-website path: website/truncated-website-build.tar.gz diff --git a/website/.env.online b/website/.env.online new file mode 100644 index 0000000..35c7162 --- /dev/null +++ b/website/.env.online @@ -0,0 +1 @@ +VITE_API_URL=https://lamp.mercotui.com/ diff --git a/website/.gitignore b/website/.gitignore index 11f5d71..f136aea 100644 --- a/website/.gitignore +++ b/website/.gitignore @@ -2,6 +2,8 @@ node_modules /dist +./*.tar.gz + # local env files .env.local .env.*.local diff --git a/website/package-lock.json b/website/package-lock.json index aaf8b6a..acc6074 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -15,6 +15,7 @@ "core-js": "^3.37.1", "lodash": "^4.17.21", "miragejs": "^0.2.0-alpha.3", + "monaco-editor": "^0.52.0", "roboto-fontface": "*", "utf8": "^3.0.0", "vue": "^3.4.31", @@ -3144,6 +3145,11 @@ "pretender": "^3.4.7" } }, + "node_modules/monaco-editor": { + "version": "0.52.0", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.52.0.tgz", + "integrity": "sha512-OeWhNpABLCeTqubfqLMXGsqf6OmPU6pHM85kF3dhy6kq5hnhuVS1p3VrEW/XhWHc71P2tHyS5JFySD8mgs1crw==" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", diff --git a/website/package.json b/website/package.json index da393f9..1e642cc 100644 --- a/website/package.json +++ b/website/package.json @@ -3,6 +3,7 @@ "version": "0.0.0", "scripts": { "dev": "vite", + "dev online": "vite -m online", "build": "vite build", "preview": "vite preview", "lint": "eslint . --fix --ignore-path .gitignore", @@ -22,6 +23,7 @@ "core-js": "^3.37.1", "lodash": "^4.17.21", "miragejs": "^0.2.0-alpha.3", + "monaco-editor": "^0.52.0", "roboto-fontface": "*", "utf8": "^3.0.0", "vue": "^3.4.31", diff --git a/website/src/components/CodeEditor.vue b/website/src/components/CodeEditor.vue index a12632f..29610f9 100644 --- a/website/src/components/CodeEditor.vue +++ b/website/src/components/CodeEditor.vue @@ -2,8 +2,8 @@ - - + + Enter Script Name @@ -20,13 +20,15 @@ Save Save As - Delete + Delete + Run in Emulator - Delete {{script_name}} ? + Delete {{ script_name }} ? Cancel @@ -38,12 +40,8 @@ - - - - - - + + @@ -54,84 +52,91 @@ import axios from 'axios'; import base64 from 'base-64'; import utf8 from 'utf8'; - export default { - data() { - return { - save_overlay_opened: false, - delete_overlay_opened: false, - type: ["template"], - content: "Loading", - } - }, +import * as monaco from 'monaco-editor' +import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker' +import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker' + +export default { + editor: null, + + data() { + return { + save_overlay_opened: false, + delete_overlay_opened: false, + type: ["template"], + content: "Loading", + } + }, - computed: { - save_enabled: function () { - return !(this.type.includes("default") || this.type.includes("template") || this.script_name === "") - }, - delete_enabled: function () { - return !(this.type.includes("default") || this.type.includes("template")) - }, + computed: { + save_enabled: function () { + return !(this.type.includes("default") || this.type.includes("template") || this.script_name === "") }, + delete_enabled: function () { + return !(this.type.includes("default") || this.type.includes("template")) + }, + }, + + props: { + script_name: String + }, + + mounted() { + self.MonacoEnvironment = { + getWorker(_, label) { + if (label === 'typescript' || label === 'javascript') { + return new tsWorker() + } + return new editorWorker() + } + } + this.editor = monaco.editor.create(document.getElementById('monaco-container'), { + value: "Loading", + language: 'javascript', + theme: "vs-dark", + }); - props: { - script_name: String + if(this.script_name) { + axios.get('/api/scripts/' + this.script_name + '/').then(response => { + const bytes = base64.decode(response.data.script); + const decoded_script = utf8.decode(bytes); + this.editor.setValue(decoded_script); + this.type = response.data.type; + }); + } else { + this.editor.setValue(""); + } + }, + + methods: { + save: function () { + var bytes = utf8.encode(this.editor.getValue()); + var encoded_script = base64.encode(bytes); + + axios.put('/api/scripts/' + this.script_name + '/', { + name: this.script_name, + script: encoded_script, + type: this.type, + }); }, - components: { - editor: require('vue2-ace-editor'), + saveAs: function () { + this.save_overlay_opened = false; + this.type = ["animation"] + this.save(); + this.$router.push('/script/' + this.script_name); }, - mounted() { - axios.get('/api/scripts/' + this.script_name + '/').then(response => { - var bytes = base64.decode(response.data.script); - this.content = utf8.decode(bytes); - this.type = response.data.type - }) + deleteScript: function () { + axios.delete('/api/scripts/' + this.script_name + '/').then(() => { + this.$router.push('/'); + }); }, - methods: { - editorInit: function (editor) { - require('brace/ext/language_tools') //language extension prerequsite... - require('brace/mode/javascript') //language - require('brace/mode/less') - require('brace/theme/monokai') - require('brace/snippets/javascript') //snippet - editor.setOptions({ - maxLines: 30, - wrap: true, - enableBasicAutocompletion: true, - autoScrollEditorIntoView: true - }); - }, - - save: function () { - var bytes = utf8.encode(this.content); - var encoded_script = base64.encode(bytes); - - axios.put('/api/scripts/' + this.script_name + '/', { - name: this.script_name, - script: encoded_script, - type: this.type, - }); - }, - - saveAs: function () { - this.save_overlay_opened = false; - this.type = ["animation"] - this.save(); - this.$router.push('/scriptview/' + this.script_name); - }, - - deleteScript: function () { - axios.delete('/api/scripts/' + this.script_name + '/').then(() => { - this.$router.push('/'); - }); - }, - - runInEmulator: function () { - this.$emit('runInEmulator', this.content) - }, - } + runInEmulator: function () { + this.$emit('runInEmulator', this.editor.getValue()) + }, } +} diff --git a/website/src/main.js b/website/src/main.js index c2a3a0b..249759e 100644 --- a/website/src/main.js +++ b/website/src/main.js @@ -8,7 +8,8 @@ import App from './App.vue' import { createApp } from 'vue' import {makeServer} from "./mock-server" -if (process.env.NODE_ENV === "development") { +if (import.meta.env.DEV && import.meta.env.MODE !== "online") { + // If we are in development mode, and not using a proxy for our API calls, then start a mock API server makeServer() } diff --git a/website/src/plugins/router.js b/website/src/plugins/router.js index d0e0cfe..eab8f6a 100644 --- a/website/src/plugins/router.js +++ b/website/src/plugins/router.js @@ -9,7 +9,7 @@ import {createMemoryHistory, createRouter} from 'vue-router' // Views import IndexView from '../views/IndexView'; -// import ScriptView from '../views/ScriptView'; +import ScriptView from '../views/ScriptView'; import DrawView from '../views/DrawView'; const router = new createRouter({ @@ -18,8 +18,8 @@ const router = new createRouter({ {path: '/', component: IndexView}, {path: '/draw/', component: DrawView, props: true}, {path: '/draw/:name', component: DrawView, props: true}, - // {path: '/script/', component: ScriptView, props: true}, - // {path: '/script/:name', component: ScriptView, props: true}, + {path: '/script/', component: ScriptView, props: true}, + {path: '/script/:name', component: ScriptView, props: true}, ], }) diff --git a/website/src/views/IndexView.vue b/website/src/views/IndexView.vue index 68f6015..5f4eddd 100644 --- a/website/src/views/IndexView.vue +++ b/website/src/views/IndexView.vue @@ -4,7 +4,7 @@ Images - + Draw mdi-brush @@ -49,33 +49,33 @@ export default { computed: { animations: function () { - var animation_scripts = this.scripts.filter(script => { + const animation_scripts = this.scripts.filter(script => { return script.type.includes("animation") && !script.type.includes("template") && !script.type.includes("temporary"); - }) + }); return Array.from(animation_scripts, script => script.name).sort(); }, images: function () { - var image_scripts = this.scripts.filter(script => { + const image_scripts = this.scripts.filter(script => { return script.type.includes("image") && !script.type.includes("template") && !script.type.includes("temporary"); - }) + }); return Array.from(image_scripts, script => script.name).sort(); }, animation_template: function () { - var animation_template_scripts = this.scripts.filter(script => { + const animation_template_scripts = this.scripts.filter(script => { return script.type.includes("animation") && script.type.includes("template"); - }) + }); return Array.from(animation_template_scripts, script => script.name).sort()[0]; }, image_template: function () { - var image_template_scripts = this.scripts.filter(script => { + const image_template_scripts = this.scripts.filter(script => { return script.type.includes("image") && script.type.includes("template"); - }) + }); return Array.from(image_template_scripts, script => script.name).sort()[0]; } }, mounted() { - axios.get('/api/scripts/').then(response => (this.scripts = response.data.scripts)) + axios.get('/api/scripts/').then(response => (this.scripts = response.data.scripts || [])) }, methods: { @@ -85,7 +85,7 @@ export default { }); }, openScript: function (script_name) { - this.$router.push('/script/' + script_name) + this.$router.push('/script/' + (script_name || "")) }, openDrawing: function (drawing_name) { this.$router.push('/draw/' + drawing_name) diff --git a/website/src/views/ScriptView.vue b/website/src/views/ScriptView.vue index bfc5b98..ed2488c 100644 --- a/website/src/views/ScriptView.vue +++ b/website/src/views/ScriptView.vue @@ -1,14 +1,8 @@ - - - + diff --git a/website/vite.config.mjs b/website/vite.config.mjs index 4e78c02..e61f7f3 100644 --- a/website/vite.config.mjs +++ b/website/vite.config.mjs @@ -1,47 +1,61 @@ // Plugins import Vue from '@vitejs/plugin-vue' -import Vuetify, { transformAssetUrls } from 'vite-plugin-vuetify' +import Vuetify, {transformAssetUrls} from 'vite-plugin-vuetify' import ViteFonts from 'unplugin-fonts/vite' // Utilities -import { defineConfig } from 'vite' -import { fileURLToPath, URL } from 'node:url' +import {defineConfig, loadEnv} from 'vite' +import {fileURLToPath, URL} from 'node:url' // https://vitejs.dev/config/ -export default defineConfig({ - plugins: [ - Vue({ - template: { transformAssetUrls } - }), - // https://github.com/vuetifyjs/vuetify-loader/tree/master/packages/vite-plugin#readme - Vuetify({ - autoImport: true, - }), - ViteFonts({ - google: { - families: [{ - name: 'Roboto', - styles: 'wght@100;300;400;500;700;900', - }], +export default defineConfig(({mode}) => { + const env = loadEnv(mode, process.cwd()) + return { + plugins: [ + Vue({ + template: {transformAssetUrls} + }), + // https://github.com/vuetifyjs/vuetify-loader/tree/master/packages/vite-plugin#readme + Vuetify({ + autoImport: true, + }), + ViteFonts({ + google: { + families: [{ + name: 'Roboto', + styles: 'wght@100;300;400;500;700;900', + }], + }, + }), + ], + define: {'process.env': {}}, + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)) + }, + extensions: [ + '.js', + '.json', + '.jsx', + '.mjs', + '.ts', + '.tsx', + '.vue', + ], }, - }), - ], - define: { 'process.env': {} }, - resolve: { - alias: { - '@': fileURLToPath(new URL('./src', import.meta.url)) - }, - extensions: [ - '.js', - '.json', - '.jsx', - '.mjs', - '.ts', - '.tsx', - '.vue', - ], - }, - server: { - port: 3000, - }, -}) + server: { + port: 3000, + // If we are in online development mode, then start an API proxy + ... env.MODE === "online" ? { + proxy: { + '/api': { + target: env.VITE_API_URL, + changeOrigin: true, + secure: true, + }, + }, + } : {} + }, + } + } +)