Skip to content

Commit 7df8cb5

Browse files
committed
feat(live-preview): forward iframe keys to Phoenix only in design mode
Outside design mode the previewed page kept its own keyboard shortcuts captured by RemoteFunctions, which broke interactive previews. Now: - Push designMode into the iframe config (initialized at boot, updated via EVENT_WORKSPACE_DESIGN_MODE_CHANGE) so RemoteFunctions can gate. - Move the forwarder from document-bubble to window-bubble registered inside the load handler, so it runs as late as the DOM event flow allows. Previewed-page handlers get first crack and we honor defaultPrevented. - Skip forwarding entirely in popped-out (non-embedded) preview windows via __PHOENIX_EMBED_INFO; default to false until embed status is confirmed.
1 parent fde2ec8 commit 7df8cb5

2 files changed

Lines changed: 35 additions & 2 deletions

File tree

src/LiveDevelopment/BrowserScripts/RemoteFunctions.js

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1578,6 +1578,18 @@ function RemoteFunctions(config = {}) {
15781578
// working normally.
15791579
const _KEYS_NOT_FORWARDED = { c:1, v:1, x:1, a:1, z:1, y:1, C:1, V:1, X:1, A:1, Z:1, Y:1 };
15801580

1581+
// Forwarding only makes sense when the page runs as an embedded LP iframe
1582+
// inside Phoenix; if the user popped the preview out into a real browser
1583+
// tab, the synthetic events would go to a window with no Phoenix UI.
1584+
// Default false, flip to true via __PHOENIX_EMBED_INFO (per its contract:
1585+
// guaranteed to fire in embedded iframes, not guaranteed otherwise).
1586+
let _isPhoenixEmbeddedIframe = false;
1587+
if (window.__PHOENIX_EMBED_INFO && window.__PHOENIX_EMBED_INFO.onPhoenixEmbeddedInfoAvailable) {
1588+
window.__PHOENIX_EMBED_INFO.onPhoenixEmbeddedInfoAvailable(function (isEmbedded) {
1589+
_isPhoenixEmbeddedIframe = !!isEmbedded;
1590+
});
1591+
}
1592+
15811593
function _isFunctionKey(event) {
15821594
return event.key.length >= 2 && event.key[0] === 'F' && !isNaN(event.key.slice(1));
15831595
}
@@ -1600,9 +1612,19 @@ function RemoteFunctions(config = {}) {
16001612
if (config.mode === 'edit' && (event.key === 'Escape' || event.key === 'Esc')) {
16011613
event.preventDefault();
16021614
_handleEscapeKeyPress();
1615+
}
1616+
});
1617+
1618+
// Forwarder for design mode: runs as late as we can manage in the standard
1619+
// event flow — bubble phase on `window` (latest target after document),
1620+
// and the listener itself is registered on `load` so it goes last among
1621+
// same-target listeners. This gives the previewed page's own handlers the
1622+
// best chance to call preventDefault (which we then honor) before we
1623+
// forward to Phoenix.
1624+
function _designModeKeyForwarder(event) {
1625+
if (!_isPhoenixEmbeddedIframe || !config.designMode) {
16031626
return;
16041627
}
1605-
// Polite: if the previewed page handled the key, don't double-fire.
16061628
if (event.defaultPrevented) {
16071629
return;
16081630
}
@@ -1614,11 +1636,12 @@ function RemoteFunctions(config = {}) {
16141636
if (isMod && event.key && event.key.length === 1 && !_KEYS_NOT_FORWARDED[event.key]) {
16151637
_forwardKeyEventToPhoenix(event);
16161638
}
1617-
});
1639+
}
16181640

16191641
// we need to refresh the config once the load is completed
16201642
// this is important because messageBroker gets ready for use only when load fires
16211643
window.addEventListener('load', function() {
1644+
window.addEventListener('keydown', _designModeKeyForwarder);
16221645
MessageBroker.send({
16231646
requestConfigRefresh: true
16241647
});

src/LiveDevelopment/main.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ define(function main(require, exports, module) {
4343
Strings = require("strings"),
4444
ExtensionUtils = require("utils/ExtensionUtils"),
4545
StringUtils = require("utils/StringUtils"),
46+
WorkspaceManager = require("view/WorkspaceManager"),
4647
EventDispatcher = require("utils/EventDispatcher");
4748

4849
const LIVE_PREVIEW_MODE = CONSTANTS.LIVE_PREVIEW_MODE,
@@ -260,13 +261,22 @@ define(function main(require, exports, module) {
260261
return getCurrentMode() === LIVE_PREVIEW_MODE;
261262
}
262263

264+
function _designModeChanged() {
265+
const config = MultiBrowserLiveDev.getConfig();
266+
config.designMode = WorkspaceManager.isInDesignMode();
267+
MultiBrowserLiveDev.updateConfig(config);
268+
}
269+
263270
/** Initialize LiveDevelopment */
264271
AppInit.appReady(function () {
265272
params.parse();
266273
const config = Object.assign({}, defaultConfig, MultiBrowserLiveDev.getConfig());
267274
config.mode = getCurrentMode();
275+
config.designMode = WorkspaceManager.isInDesignMode();
268276
MultiBrowserLiveDev.init(config);
269277

278+
WorkspaceManager.on(WorkspaceManager.EVENT_WORKSPACE_DESIGN_MODE_CHANGE, _designModeChanged);
279+
270280
_loadStyles();
271281

272282
// update styles for UI status

0 commit comments

Comments
 (0)