diff --git a/bust-cache.js b/bust-cache.js
new file mode 100644
index 0000000..bc0e1ae
--- /dev/null
+++ b/bust-cache.js
@@ -0,0 +1,6 @@
+const fs = require('fs');
+const { version } = require('./package.json');
+
+let swFile = fs.readFileSync('./service-worker.js', 'utf-8');
+swFile = swFile.replace(/LATEST_VERSION = '[^']+';/, `LATEST_VERSION = '${version}';`);
+fs.writeFileSync('./service-worker.js', swFile);
diff --git a/package.json b/package.json
index be14bae..8b6f007 100644
--- a/package.json
+++ b/package.json
@@ -1,13 +1,14 @@
{
"name": "duinoapp-client",
- "version": "3.0.1",
+ "version": "3.1.0",
"author": "Fraser Bullock",
"license": "GPL-3.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
- "lint": "vue-cli-service lint"
+ "lint": "vue-cli-service lint",
+ "version": "node bust-cache.js && git add package.json service-worker.js"
},
"dependencies": {
"@feathersjs/feathers": "^4.5.8",
@@ -18,8 +19,11 @@
"debounce-promise": "^3.1.2",
"feathers-localstorage": "^5.1.1",
"feathers-vuex": "^3.12.3",
+ "fflate": "^0.4.4",
+ "file-saver": "^2.0.5",
"intel-hex": "^0.1.2",
"lodash": "^4.17.15",
+ "monaco-editor-webpack-plugin": "<=1.8.2",
"register-service-worker": "^1.7.1",
"roboto-fontface": "*",
"sass": "^1.26.7",
diff --git a/service-worker.js b/service-worker.js
new file mode 100644
index 0000000..185910e
--- /dev/null
+++ b/service-worker.js
@@ -0,0 +1,36 @@
+// service-worker.js
+
+workbox.core.setCacheNameDetails({ prefix: 'd4' });
+
+// Do not touch this line
+const LATEST_VERSION = '3.1.0';
+
+self.addEventListener('activate', (event) => {
+ console.log(`%c ${LATEST_VERSION} `, 'background: #ddd; color: #0000ff');
+ if (caches) {
+ caches.keys().then((arr) => {
+ arr.forEach((key) => {
+ if (key.indexOf('d4-precache') < -1) {
+ caches.delete(key).then(() => console.log(`%c Cleared ${key}`, 'background: #333; color: #ff0000'));
+ } else {
+ caches.open(key).then((cache) => {
+ cache.match('version').then((res) => {
+ if (!res) {
+ cache.put('version', new Response(LATEST_VERSION, { status: 200, statusText: LATEST_VERSION }));
+ } else if (res.statusText !== LATEST_VERSION) {
+ caches.delete(key).then(() => console.log(`%c Cleared Cache ${LATEST_VERSION}`, 'background: #333; color: #ff0000'));
+ } else console.log(`%c Great you have the latest version ${LATEST_VERSION}`, 'background: #333; color: #00ff00');
+ });
+ });
+ }
+ });
+ });
+ }
+});
+
+workbox.skipWaiting();
+workbox.clientsClaim();
+
+self.__precacheManifest = [].concat(self.__precacheManifest || []);
+workbox.precaching.suppressWarnings();
+workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
diff --git a/src/App.vue b/src/App.vue
index 626ef9e..488ed06 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -117,11 +117,16 @@ export default {
else setTimeout(() => this.checkSerialReady(), 100);
},
},
- mounted() {
+ async mounted() {
this.checkSerialReady();
- this.$FeathersVuex.api.File.find();
- this.$FeathersVuex.api.Project.find();
- this.$FeathersVuex.api.Setting.find();
+ this.$FeathersVuex.api.File.find({ query: { $limit: 9999999 } });
+ this.$FeathersVuex.api.Project.find({ query: { $limit: 9999999 } });
+ await this.$FeathersVuex.api.Setting.find({ query: { $limit: 9999999 } });
+ const { Setting } = this.$FeathersVuex.api;
+ const { data } = Setting.findInStore({ query: { key: 'editor' } });
+ // eslint-disable-next-line no-console
+ console.log(data[0]);
+ this.$vuetify.theme.dark = /(dark)|(black)/.test(data[0]?.value?.theme ?? '');
},
};
diff --git a/src/components/files/add-file.vue b/src/components/files/add-file.vue
new file mode 100644
index 0000000..9188d2e
--- /dev/null
+++ b/src/components/files/add-file.vue
@@ -0,0 +1,157 @@
+
+
+
+
+
+
+
+ Add File
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{project.ref}}/{{location}}{{name}}{{ext}}
+
+
+
+
+
+
+ Cancel
+
+
+ Add File
+
+
+
+
+
+
+
+
+
diff --git a/src/components/files/add-folder.vue b/src/components/files/add-folder.vue
new file mode 100644
index 0000000..fa8f0d7
--- /dev/null
+++ b/src/components/files/add-folder.vue
@@ -0,0 +1,133 @@
+
+
+
+
+
+
+
+ Add Folder
+
+
+
+
+
+
+
+
+
+
+ {{project.ref}}/{{location}}{{name}}/
+
+
+
+
+
+
+ Cancel
+
+
+ Add Folder
+
+
+
+
+
+
+
+
+
diff --git a/src/components/files/editor.vue b/src/components/files/editor.vue
index 9686d71..8ec3092 100644
--- a/src/components/files/editor.vue
+++ b/src/components/files/editor.vue
@@ -1,38 +1,27 @@
-
diff --git a/src/plugins/bundler.js b/src/plugins/bundler.js
new file mode 100644
index 0000000..6b884ba
--- /dev/null
+++ b/src/plugins/bundler.js
@@ -0,0 +1,79 @@
+/* eslint-disable class-methods-use-this */
+// import moment from 'dayjs';
+import {
+ zip, unzip, strToU8, strFromU8,
+} from 'fflate';
+import { saveAs } from 'file-saver';
+
+const getType = (ref) => {
+ switch (ref.split('.').pop()) {
+ case 'ino':
+ return 'text/x-arduino';
+ case 'c':
+ case 'cpp':
+ case 'h':
+ return 'text/x-c';
+ case 'txt':
+ return 'text/plain';
+ case 'md':
+ return 'text/markdown';
+ default:
+ return '';
+ }
+};
+
+class Bundler {
+ install(Vue) {
+ // eslint-disable-next-line no-param-reassign
+ Vue.$bundler = this;
+ // eslint-disable-next-line no-param-reassign
+ Vue.prototype.$bundler = this;
+ this.Vue = Vue;
+ }
+
+ async createFile(project, fileObj, pathName, main) {
+ const { File } = this.Vue.$FeathersVuex.api;
+ const contentType = getType(pathName);
+ if (!contentType) return;
+ const file = new File({
+ name: main ? `${project.ref}.ino` : pathName,
+ ref: main ? `${project.ref}/${project.ref}.ino` : `${project.ref}/${pathName}`,
+ body: strFromU8(fileObj),
+ contentType,
+ main,
+ projectId: project.uuid,
+ });
+ await file.save();
+ }
+
+ async unzipFile(zipFile) {
+ const zipArr = new Uint8Array(await zipFile.arrayBuffer());
+ return new Promise((resolve, reject) => unzip(zipArr, (err, res) => (err ? reject(err) : resolve(res))));
+ }
+
+ async importProjectFiles(project, unzipped, inoFile) {
+ const fileNames = Object.keys(unzipped);
+ const inoPath = inoFile.replace(/\/[^/]+.ino$/, '');
+ await Promise.all(
+ fileNames
+ .filter((i) => !inoPath || i.indexOf(`${inoPath}/`) === 0)
+ .map((i) => this.createFile(project, unzipped[i], i.replace(`${inoPath}/`, ''), i === inoFile)),
+ );
+ return project;
+ }
+
+ async exportProject(project) {
+ const { File } = this.Vue.$FeathersVuex.api;
+ const { data: files } = File.findInStore({ query: { projectId: project.uuid } });
+ // eslint-disable-next-line no-param-reassign
+ const fileObj = files.reduce((a, file) => { a[file.ref] = strToU8(file.body); return a; }, {});
+ // eslint-disable-next-line no-console
+ console.log(files, fileObj);
+ const zipped = await new Promise((resolve, reject) => zip(fileObj, (err, res) => (err ? reject(err) : resolve(res))));
+ // eslint-disable-next-line no-console
+ console.log(zipped);
+ saveAs(new Blob([zipped.buffer]), `${project.ref}.zip`);
+ }
+}
+
+export default new Bundler();
diff --git a/src/plugins/index.js b/src/plugins/index.js
index 70f30d1..0445f14 100644
--- a/src/plugins/index.js
+++ b/src/plugins/index.js
@@ -3,11 +3,13 @@ import './serial';
import FlagIcon from 'vue-flag-icon';
import CompileServer from './compile-server';
import CurrentStore from './current-store';
+import Bundler from './bundler';
Vue.use(FlagIcon);
Vue.use(CurrentStore);
Vue.use(CompileServer);
+Vue.use(Bundler);
// CompileServer.on('console.log', console.log);
// CompileServer.on('console.error', console.error);
diff --git a/src/registerServiceWorker.js b/src/registerServiceWorker.js
index e32aa92..81ff588 100644
--- a/src/registerServiceWorker.js
+++ b/src/registerServiceWorker.js
@@ -21,6 +21,9 @@ if (process.env.NODE_ENV === 'production') {
},
updated() {
console.log('New content is available; please refresh.');
+ setTimeout(() => {
+ window.location.reload(true);
+ }, 3000);
},
offline() {
console.log('No internet connection found. App is running in offline mode.');
diff --git a/src/store/tools.js b/src/store/tools.js
index eaaa016..7003894 100644
--- a/src/store/tools.js
+++ b/src/store/tools.js
@@ -34,5 +34,8 @@ export const settingsDefaults = {
editor: {
autoSaveInterval: 10,
theme: 'vs',
+ fontSize: 14,
+ wordWrap: 'off',
+ scrollBeyondLastLine: true,
},
};
diff --git a/src/views/Code.vue b/src/views/Code.vue
index a738fa0..32affac 100644
--- a/src/views/Code.vue
+++ b/src/views/Code.vue
@@ -2,13 +2,39 @@
-
-
-
- mdi-folder-multiple-outline
- {{currentProject ? currentProject.name : 'Select a Project'}}
- mdi-chevron-down
-
+
+
+
+
+
+ mdi-folder-multiple-outline
+ {{currentProject ? currentProject.name : 'Select a Project'}}
+ mdi-chevron-down
+
+
+
+ {{currentProject ? currentProject.name : 'Select a Project'}}
+
+
+
+
+
+
+
+ mdi-folder-plus-outline
+
+
+
+
+
+
+
+
+ mdi-file-plus-outline
+
+
+
+
+
+
+
-
+
@@ -30,16 +59,22 @@