diff --git a/gulpfile.js b/gulpfile.js index 222b501..6152b74 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -12,6 +12,7 @@ const child_process = require('child_process'); const merge = require('merge-stream'); const zip = require('gulp-zip'); var packageJson = JSON.parse(fs.readFileSync('./package.json')); +const connect = require('gulp-connect'); function minifyJs(fileName) { const content = fs.readFileSync(fileName, 'utf8'); @@ -199,6 +200,14 @@ gulp.task('cleanup', function() { return child_process.execSync('rm -rf build'); }); +gulp.task('start-preview-server', function() { + connect.server({ + root: 'preview', + port: 7888, + https: false + }); +}); + gulp.task('release', function(callback) { runSequence( 'runWebpack', diff --git a/package.json b/package.json index d3c3527..0db70e3 100644 --- a/package.json +++ b/package.json @@ -3,10 +3,10 @@ "version": "3.6.2", "description": "A blazing fast & offline web playground", "scripts": { - "start": "if-env NODE_ENV=production && npm run -s serve || npm run -s dev", + "start": "npm run -s dev && gulp start-preview-server", "build": "preact build --template src/index.html --no-prerender --service-worker false", "serve": "preact build && preact serve", - "dev": "preact watch --template src/index.html --https --no-prerender", + "dev": "preact watch --template src/index.html --no-prerender", "lint": "eslint src", "test": "jest", "precommit": "lint-staged", @@ -65,6 +65,7 @@ "copy-webpack-plugin": "^4.5.1", "esprima": "^4.0.0", "firebase": "^5.5.8", + "gulp-connect": "^5.7.0", "jszip": "^3.1.5", "preact": "^8.2.6", "preact-compat": "^3.17.0", diff --git a/preview/index.html b/preview/index.html new file mode 100644 index 0000000..da28569 --- /dev/null +++ b/preview/index.html @@ -0,0 +1 @@ +Loading... diff --git a/preview/service-worker.js b/preview/service-worker.js new file mode 100644 index 0000000..ad2ce4c --- /dev/null +++ b/preview/service-worker.js @@ -0,0 +1,60 @@ +const CACHE_NAME = 'webmaker-vfiles'; +self.addEventListener('fetch', function(event) { + // console.log('fetch event', event.request.url, event.request); + event.respondWith( + caches.open(CACHE_NAME).then(function(cache) { + return cache + .match(event.request) + .then(response => { + // console.log('responding with ', response); + if (response !== undefined) { + return response; + } + return fetch(event.request); + }) + .catch(function(e) { + // Fall back to just fetch()ing the request if some unexpected error + // prevented the cached response from being valid. + console.warn( + 'Couldn\'t serve response for "%s" from cache: %O', + event.request.url, + e + ); + return fetch(event.request); + }); + }) + ); + // } +}); + +function getContentType(url) { + if (url.match(/\.html$/)) { + return 'text/html; charset=UTF-8'; + } else if (url.match(/\.css$/)) { + return 'text/css; charset=UTF-8'; + } else if (url.match(/\.js$/)) { + return 'application/javascript; charset=UTF-8'; + } else if (url.match(/\.json$/)) { + return 'application/json; charset=UTF-8'; + } + return 'text/html; charset=UTF-8'; +} + +self.addEventListener('message', function(e) { + // console.log('message aya sw main', e.data); + caches.open(CACHE_NAME).then(function(cache) { + for (const url in e.data) { + if (Object.prototype.hasOwnProperty.call(e.data, url)) { + // console.log('Received data', url, e.data[url]) + cache.put( + url, + new Response(e.data[url], { + headers: { + 'Content-Type': getContentType(url) + } + }) + ); + } + } + }); +}); diff --git a/preview/talk.html b/preview/talk.html new file mode 100644 index 0000000..e546cb2 --- /dev/null +++ b/preview/talk.html @@ -0,0 +1,52 @@ +Hello + + + + + diff --git a/src/components/CodeEditor.jsx b/src/components/CodeEditor.jsx index 06eae83..ceb59e6 100644 --- a/src/components/CodeEditor.jsx +++ b/src/components/CodeEditor.jsx @@ -244,8 +244,6 @@ export default class CodeEditor extends Component { } async initEditor() { - console.log('init editor'); - this.editorReadyDeferred = deferred(); await this.loadDeps(); diff --git a/src/components/ContentWrap.jsx b/src/components/ContentWrap.jsx index b7bae61..eff5fb9 100644 --- a/src/components/ContentWrap.jsx +++ b/src/components/ContentWrap.jsx @@ -69,6 +69,13 @@ export default class ContentWrap extends Component { } componentDidMount() { this.props.onRef(this); + + // Listen for logs from preview frame + window.addEventListener('message', e => { + if (e.data && e.data.logs) { + this.onMessageFromConsole(...e.data.logs); + } + }); } onHtmlCodeChange(editor, change) { @@ -160,7 +167,7 @@ export default class ContentWrap extends Component { : `${location.origin}`; var src = `filesystem:${origin}/temporary/preview.html`; if (this.detachedWindow) { - this.detachedWindow.postMessage(src, '*'); + this.detachedWindow.postMessage({ url: src }, '*'); } else { this.frame.src = src; } diff --git a/src/components/ContentWrapFiles.jsx b/src/components/ContentWrapFiles.jsx index b222174..503f040 100644 --- a/src/components/ContentWrapFiles.jsx +++ b/src/components/ContentWrapFiles.jsx @@ -22,6 +22,9 @@ import { commandPaletteService } from '../commandPaletteService'; import { PreviewDimension } from './PreviewDimension'; const minCodeWrapSize = 33; +const PREVIEW_FRAME_HOST = window.DEBUG + ? 'http://localhost:7888' + : `https://preview.${location.host}`; /* global htmlCodeEl */ @@ -114,6 +117,13 @@ export default class ContentWrapFiles extends Component { } componentDidMount() { this.props.onRef(this); + + // Listen for logs from preview frame + window.addEventListener('message', e => { + if (e.data && e.data.logs) { + this.onMessageFromConsole(...e.data.logs); + } + }); this.commandPaletteSubscriptions = []; this.commandPaletteSubscriptions.push( commandPaletteService.subscribe(SWITCH_FILE_EVENT, file => { @@ -226,7 +236,7 @@ export default class ContentWrapFiles extends Component { ); // Namespace all file paths to '/user' because thats what the service worker // recognizes. - const files = linearizeFiles(assignFilePaths(duplicateFiles, '/user')); + const files = linearizeFiles(assignFilePaths(duplicateFiles, '')); files.forEach(file => { obj[file.path] = file.content || ''; @@ -245,13 +255,19 @@ export default class ContentWrapFiles extends Component { } }); - navigator.serviceWorker.controller.postMessage(obj); + // navigator.serviceWorker.controller.postMessage(obj); + window.talkFrame.contentWindow.postMessage(obj, '*'); if (this.detachedWindow) { log('✉️ Sending message to detached window'); - this.detachedWindow.postMessage({ contents: '/user/index.html' }, '*'); + this.detachedWindow.postMessage( + { url: `${PREVIEW_FRAME_HOST}/index.html` }, + '*' + ); } else { - this.frame.src = '/user/index.html'; + setTimeout(() => { + this.frame.src = `${PREVIEW_FRAME_HOST}/index.html`; + }, 10); } } cleanupErrors() { @@ -516,6 +532,38 @@ export default class ContentWrapFiles extends Component { this.editor.focus(); } + detachPreview() { + if (this.detachedWindow) { + this.detachedWindow.focus(); + return; + } + const iframeBounds = this.frame.getBoundingClientRect(); + const iframeWidth = iframeBounds.width; + const iframeHeight = iframeBounds.height; + document.body.classList.add('is-detached-mode'); + + this.detachedWindow = window.open( + './preview.html', + 'Web Maker', + `width=${iframeWidth},height=${iframeHeight},resizable,scrollbars=yes,status=1` + ); + // Trigger initial render in detached window + setTimeout(() => { + this.setPreviewContent(true); + }, 1500); + + var intervalID = window.setInterval(checkWindow => { + if (this.detachedWindow && this.detachedWindow.closed) { + clearInterval(intervalID); + document.body.classList.remove('is-detached-mode'); + this.detachedWindow = null; + // Update main frame preview to get latest changes (which were not + // getting reflected while detached window was open) + this.setPreviewContent(true); + } + }, 500); + } + onMessageFromConsole() { const logs = [...arguments].map(arg => { if ( @@ -570,7 +618,7 @@ export default class ContentWrapFiles extends Component { this.onMessageFromConsole('> ' + e.target.value); /* eslint-disable no-underscore-dangle */ - this.frame.contentWindow._wmEvaluate(e.target.value); + this.frame.contentWindow.postMessage({ exprToEval: e.target.value }, '*'); /* eslint-enable no-underscore-dangle */ @@ -650,11 +698,12 @@ export default class ContentWrapFiles extends Component {