Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Live Preview Desktop first draft #1261

Merged
merged 13 commits into from
Jan 5, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions src/LiveDevelopment/LiveDevMultiBrowser.js
Original file line number Diff line number Diff line change
Expand Up @@ -606,10 +606,12 @@ define(function (require, exports, module) {
_createLiveDocumentForFrame(initialDoc);

// start listening for requests
_server.start();
_server.start()
.then(()=>{
// open browser to the url
_open(initialDoc);
});

// open browser to the url
_open(initialDoc);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/document/DocumentManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ define(function (require, exports, module) {
* If all you need is the Document's getText() value, use the faster getDocumentText() instead.
*
* @param {!string} fullPath
* @param {!object} fileObj actual File|RemoteFile or some other protocol adapter handle
* @param {object?} fileObj actual File|RemoteFile or some other protocol adapter handle
* @return {$.Promise} A promise object that will be resolved with the Document, or rejected
* with a FileSystemError if the file is not yet open and can't be read from disk.
*/
Expand Down
11 changes: 11 additions & 0 deletions src/extensions/default/HealthData/SendToAnalytics.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,17 @@ define(function (require, exports, module) {
Number(healthReport["AppStartupTime"]));
Metrics.valueEvent(PERFORMANCE, "startup", "ModuleDepsResolved",
Number(healthReport["ModuleDepsResolved"]));
Metrics.valueEvent(PERFORMANCE, "startup", "PhStore", PhStore._storageBootstrapTime);
if(Phoenix.browser.isTauri) {
Metrics.valueEvent(PERFORMANCE, "startup", "tauriBoot", window._tauriBootVars.bootstrapTime);
}
if(window.nodeSetupDonePromise) {
window.nodeSetupDonePromise
.then(()=>{
window.PhNodeEngine && window.PhNodeEngine._nodeLoadTime
&& Metrics.valueEvent(PERFORMANCE, "startup", "nodeBoot", window.PhNodeEngine._nodeLoadTime);
});
}
}

// Themes
Expand Down
283 changes: 169 additions & 114 deletions src/extensions/default/Phoenix-live-preview/StaticServer.js

Large diffs are not rendered by default.

16 changes: 12 additions & 4 deletions src/extensions/default/Phoenix-live-preview/markdown.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,18 @@
<title>Markdown- Phoenix</title>


<link rel="stylesheet" href="{{{BOOTSTRAP_LIB_CSS}}}">
<link href="{{{HIGHLIGHT_JS_CSS}}}" rel="stylesheet">
<script src="{{{HIGHLIGHT_JS}}}"></script>
<link rel="stylesheet" href="{{{GFM_CSS}}}" />
<style>
{{{BOOTSTRAP_LIB_CSS}}}
</style>
<style>
{{{HIGHLIGHT_JS_CSS}}}
</style>
<script>
{{{HIGHLIGHT_JS}}}
</script>
<style>
{{{GFM_CSS}}}
</style>
<script type="text/javascript">
function inIframe () {
try {
Expand Down
1 change: 1 addition & 0 deletions src/extensions/default/Phoenix-live-preview/panel.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
title="live preview server"
src="about:blank"
style="width:100%;"
sandbox="allow-same-origin allow-scripts"
hidden>
</iframe>
</div>
Expand Down
2 changes: 2 additions & 0 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@
const appLocalDirPromise = window.__TAURI__.path.appLocalDataDir();
const tempDirPromise = window.__TAURI__.os.tempdir();
window._tauriBootVars = {};
const tauriBootStartTime = Date.now();
window._tauriBootVarsPromise = Promise.all([appNamePromise, documentDirPromise,
appLocalDirPromise, tempDirPromise])
.then((results) => {
Expand All @@ -164,6 +165,7 @@

window._tauriBootVars.appLocalDir = results[2];
window._tauriBootVars.tempDir = results[3];
window._tauriBootVars.bootstrapTime = Date.now() - tauriBootStartTime;
localStorage.setItem(TAURI_BOOT_VARS_LOCALSTORAGE_KEY, JSON.stringify(window._tauriBootVars));
});
}
Expand Down
164 changes: 164 additions & 0 deletions src/live-preview.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Phoenix Live Preview Loader...</title>
<style>
html, body {
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
}
iframe {
width: 100%;
height: 100%;
border: none; /* Removes the default border around an iframe */
}
</style>
<script>
const TRUSTED_ORIGINS = {
'http://localhost:8000': true, // phcode dev server
'http://localhost:8001': true, // phcode dev live preview server
'http://localhost:5000': true, // playwright tests
'http://127.0.0.1:8000': true, // phcode dev server
'http://127.0.0.1:8001': true, // phcode dev live preview server
'http://127.0.0.1:5000': true, // playwright tests
'https://phcode.live': true, // phcode prod live preview server
'https://phcode.dev': true,
'https://dev.phcode.dev': true,
'https://staging.phcode.dev': true,
'https://create.phcode.dev': true
};

const pageLoaderID = crypto.randomUUID();
function setupNavigationWatcher(controllingPhoenixInstanceID) {
let livepreviewServerIframe = document.getElementById("live-preview-server-iframe");
const LOG_LIVE_PREVIEW_KEY= "logLivePreview";
let loggingEnabled = localStorage.getItem(LOG_LIVE_PREVIEW_KEY) || "false";
const isLoggingEnabled = loggingEnabled.toLowerCase() === 'true';
function _debugLog(...args) {
if(isLoggingEnabled) {
console.log(...args);
}
}
const LOADER_BROADCAST_ID = `live-preview-loader-${controllingPhoenixInstanceID}`;
const navigatorChannel = new BroadcastChannel(LOADER_BROADCAST_ID);
const LIVE_PREVIEW_MESSENGER_CHANNEL = `live-preview-messenger-${controllingPhoenixInstanceID}`;
const livePreviewChannel = new BroadcastChannel(LIVE_PREVIEW_MESSENGER_CHANNEL);
navigatorChannel.onmessage = (event) => {
_debugLog("Live Preview loader channel: Browser received event from Phoenix: ", JSON.stringify(event.data));
const type = event.data.type;
switch (type) {
case "REDIRECT_PAGE":
const url = event.data.url;
_debugLog("Loading page: ", url);
document.getElementById("previewFrame").src = url;
break;
default:
console.error("Unknown live preivew broadcast message received!: ", event);
}
}
livePreviewChannel.onmessage = (event) => {
_debugLog("Live Preview message channel: Browser received event from Phoenix: ", JSON.stringify(event.data));
if(event.data.pageLoaderID && event.data.pageLoaderID !== pageLoaderID){
// this message is not for this page loader window.
return;
}
// This is intended to the embedded live preview server frame which processes the request. just pass
livepreviewServerIframe.contentWindow.postMessage(event.data.data, '*');
}
// These messages are sent from either the live preview frame or the server frame.
window.addEventListener('message', function(event) {
// Security check: ensure the message is from the expected domain
if (!TRUSTED_ORIGINS[event.origin]) {
return;
}

// The live preview frame will send us its title and favicon for us to set the window
// title and favicon. IF its that message, then, set it up
if (event.data.title && event.data.faviconBase64) {
// Update the title of the parent page
document.title = event.data.title;

// Update the favicon
let link = document.querySelector("link[rel~='icon']");
if (!link) {
link = document.createElement('link');
link.rel = 'icon';
document.getElementsByTagName('head')[0].appendChild(link);
}
link.href = event.data.faviconBase64;
Fixed Show fixed Hide fixed
Fixed Show fixed Hide fixed
} else {
// this is for phoenix to process, pass it on
livePreviewChannel.postMessage({
pageLoaderID: pageLoaderID,
data: event.data
});
}
});

// todo how ot send the title and favicon below
// function convertImgToBase64(url, callback) {
// var canvas = document.createElement('CANVAS');
// var ctx = canvas.getContext('2d');
// var img = new Image();
// img.crossOrigin = 'Anonymous';
// img.onload = function() {
// canvas.height = img.height;
// canvas.width = img.width;
// ctx.drawImage(img, 0, 0);
// var dataURL = canvas.toDataURL();
// callback(dataURL);
// canvas = null;
// };
// img.src = url;
// }
//
// window.onload = function() {
// var faviconUrl = document.querySelector("link[rel~='icon']").href;
// convertImgToBase64(faviconUrl, function(base64) {
// window.parent.postMessage({
// title: document.title,
// faviconBase64: base64
// }, '*'); // Replace '*' with actual parent origin for security
// });
// };

}

function navigateToInitialURL() {
const queryParams = new URLSearchParams(window.location.search);
const initialURL = queryParams.get('initialURL');
const phoenixInstanceID = queryParams.get('phoenixInstanceID');
const virtualServerURL = queryParams.get('virtualServerURL');
if(!phoenixInstanceID || !initialURL || !virtualServerURL){
console.error("Expected required query strings: phoenixInstanceID, initialURL, virtualServerURL");
return;
}
let livepreviewServerIframe = document.getElementById("live-preview-server-iframe");
let url = `${virtualServerURL}?parentOrigin=${location.origin}`;
livepreviewServerIframe.addEventListener('load', function() {
document.getElementById('previewFrame').src = initialURL;
Fixed Show fixed Hide fixed
Fixed Show fixed Hide fixed
setupNavigationWatcher(phoenixInstanceID);
});
livepreviewServerIframe.setAttribute("src", url);
Fixed Show fixed Hide fixed
Fixed Show fixed Hide fixed
}

</script>
</head>
<body onload="navigateToInitialURL()">
<iframe id="live-preview-server-iframe"
title="live preview server"
src="about:blank"
style="width:100%;"
sandbox="allow-same-origin allow-scripts"
hidden>
</iframe>
<iframe id="previewFrame"
title="live preview"
src="about:blank"
sandbox="allow-same-origin allow-scripts allow-popups allow-forms allow-modals allow-pointer-lock">
</iframe>
</body>
</html>
4 changes: 4 additions & 0 deletions src/node-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,8 @@ function nodeLoader() {

window.__TAURI__.path.resolveResource("src-node/index.js")
.then(async nodeSrcPath=>{
// node is designed such that it is not required at boot time to lower startup time.
// Keep this so to increase boot speed.
const inspectPort = Phoenix.isTestWindow ? getRandomNumber(5000, 50000) : 9229;
const argsArray = isInspectEnabled() ? [`--inspect=${inspectPort}`, nodeSrcPath] : [nodeSrcPath, ''];
command = window.__TAURI__.shell.Command.sidecar('phnode', argsArray);
Expand Down Expand Up @@ -688,6 +690,8 @@ function nodeLoader() {
fs.forceUseNodeWSEndpoint(true);
setNodeWSEndpoint(message.phoenixNodeURL);
resolve(message);
// node is designed such that it is not required at boot time to lower startup time.
// Keep this so to increase boot speed.
window.PhNodeEngine._nodeLoadTime = Date.now() - nodeLoadstartTime;
});
execNode(NODE_COMMANDS.SET_DEBUG_MODE, window.debugMode);
Expand Down
2 changes: 1 addition & 1 deletion src/phoenix/shell.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ Phoenix.app = {
getUserProjectsDirectory: Phoenix.VFS.getUserProjectsDirectory,
getTempDirectory: Phoenix.VFS.getTempDir,
ERR_CODES: ERR_CODES,
getElapsedMilliseconds: function () {
getTimeSinceStartup: function () {
return Date.now() - Phoenix.startTime; // milliseconds elapsed since app start
},
language: navigator.language
Expand Down
3 changes: 2 additions & 1 deletion src/storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,8 @@
flushDB,
watchExternalChanges,
unwatchExternalChanges,
storageReadyPromise
storageReadyPromise,
_storageBootstrapTime: Date.now() - Phoenix.startTime
};
if(Phoenix.isTestWindow) {
PhStore._setTestKey = function (testKey) {
Expand Down
8 changes: 4 additions & 4 deletions src/utils/PerfUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ define(function (require, exports, module) {
* Flag to enable/disable performance data gathering. Default is true (enabled)
* @type {boolean} enabled
*/
var enabled = brackets && !!brackets.app.getElapsedMilliseconds;
var enabled = brackets && !!brackets.app.getTimeSinceStartup;

/**
* Performance data is stored in this hash object. The key is the name of the
Expand Down Expand Up @@ -161,7 +161,7 @@ define(function (require, exports, module) {
return;
}

var time = brackets.app.getElapsedMilliseconds();
var time = brackets.app.getTimeSinceStartup();
var id = _generatePerfMeasurements(name);
var i;

Expand Down Expand Up @@ -191,7 +191,7 @@ define(function (require, exports, module) {
id = new PerfMeasurement(id, id);
}

let elapsedTime = brackets.app.getElapsedMilliseconds();
let elapsedTime = brackets.app.getTimeSinceStartup();

if (activeTests[id.id]) {
elapsedTime -= activeTests[id.id].startTime;
Expand Down Expand Up @@ -241,7 +241,7 @@ define(function (require, exports, module) {
* @param {Object} id Timer id.
*/
function updateMeasurement(id) {
var elapsedTime = brackets.app.getElapsedMilliseconds();
var elapsedTime = brackets.app.getTimeSinceStartup();

if (updatableTests[id.id]) {
// update existing measurement
Expand Down
2 changes: 2 additions & 0 deletions test/SpecRunner.html
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@
const appLocalDirPromise = window.__TAURI__.path.appLocalDataDir();
const tempDirPromise = window.__TAURI__.os.tempdir();
window._tauriBootVars = {};
const tauriBootStartTime = Date.now();
window._tauriBootVarsPromise = Promise.all([appNamePromise, documentDirPromise,
appLocalDirPromise, tempDirPromise])
.then((results) => {
Expand All @@ -131,6 +132,7 @@
//Documents dir special case for tests
window._tauriBootVars.appLocalDir = results[2];
window._tauriBootVars.tempDir = results[3];
window._tauriBootVars.bootstrapTime = Date.now() - tauriBootStartTime;
});
}
setupTauriBootVars();
Expand Down
Loading