diff --git a/src/LiveDevelopment/BrowserScripts/LiveDevProtocolRemote.js b/src/LiveDevelopment/BrowserScripts/LiveDevProtocolRemote.js
index a959721cd4..72b95696fb 100644
--- a/src/LiveDevelopment/BrowserScripts/LiveDevProtocolRemote.js
+++ b/src/LiveDevelopment/BrowserScripts/LiveDevProtocolRemote.js
@@ -455,6 +455,37 @@
redoLivePreviewOperation: true
});
}
+
+ // for save
+ if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "s") {
+ e.preventDefault();
+
+ // to check if user was in between editing text
+ // in such cases we first finish the editing and then save
+ const activeElement = document.activeElement;
+ if (activeElement &&
+ activeElement.hasAttribute("contenteditable") &&
+ activeElement.hasAttribute("data-brackets-id") &&
+ window._LD &&
+ window._LD.finishEditing) {
+
+ window._LD.finishEditing(activeElement);
+ }
+
+ MessageBroker.send({
+ livePreviewEditEnabled: true,
+ saveCurrentDocument: true
+ });
+ }
+
+ // for preview button (play icon) toggle
+ if (e.key === 'F8') {
+ e.preventDefault();
+ MessageBroker.send({
+ livePreviewEditEnabled: true,
+ toggleLivePreviewMode: true
+ });
+ }
});
}(this));
diff --git a/src/LiveDevelopment/BrowserScripts/RemoteFunctions.js b/src/LiveDevelopment/BrowserScripts/RemoteFunctions.js
index 6c208d6c8b..0b7beceaed 100644
--- a/src/LiveDevelopment/BrowserScripts/RemoteFunctions.js
+++ b/src/LiveDevelopment/BrowserScripts/RemoteFunctions.js
@@ -33,10 +33,6 @@ function RemoteFunctions(config = {}) {
// we need this so that we can remove click styling from the previous element when a new element is clicked
let previouslyClickedElement = null;
- // this is needed so that when user starts typing we can dismiss all the boxes and highlights
- // now with this variable we check if its a first keystroke on an element or a subsequent keystroke
- let _uiHiddenDuringTyping = false;
-
var req, timeout;
var animateHighlight = function (time) {
if(req) {
@@ -133,6 +129,7 @@ function RemoteFunctions(config = {}) {
if(element && // element should exist
element.tagName.toLowerCase() !== "body" && // shouldn't be the body tag
element.tagName.toLowerCase() !== "html" && // shouldn't be the HTML tag
+ !element.closest("[data-phcode-internal-c15r5a9]") && // this attribute is used by phoenix internal elements
!_isInsideHeadTag(element)) { // shouldn't be inside the head tag like meta tags and all
return true;
}
@@ -1368,10 +1365,10 @@ function RemoteFunctions(config = {}) {
`,
selectImageFromComputer: `
-
-
-
- `,
+
+
+
+ `,
downloadImage: `
@@ -1465,6 +1462,7 @@ function RemoteFunctions(config = {}) {
_style: function() {
this.body = window.document.createElement("div");
+ this.body.setAttribute("data-phcode-internal-c15r5a9", "true");
// this is shadow DOM.
// we need it because if we add the box directly to the DOM then users style might override it.
@@ -1744,6 +1742,7 @@ function RemoteFunctions(config = {}) {
_style: function() {
this.body = window.document.createElement("div");
+ this.body.setAttribute("data-phcode-internal-c15r5a9", "true");
// this is shadow DOM.
// we need it because if we add the box directly to the DOM then users style might override it.
@@ -1928,6 +1927,7 @@ function RemoteFunctions(config = {}) {
_style: function() {
this.body = window.document.createElement("div");
+ this.body.setAttribute("data-phcode-internal-c15r5a9", "true");
// using shadow dom so that user styles doesn't override it
const shadow = this.body.attachShadow({ mode: "open" });
@@ -2297,7 +2297,8 @@ function RemoteFunctions(config = {}) {
ImageRibbonGallery.prototype = {
_style: function () {
this.body = window.document.createElement("div");
- this._shadow = this.body.attachShadow({mode: 'closed'});
+ this.body.setAttribute("data-phcode-internal-c15r5a9", "true");
+ this._shadow = this.body.attachShadow({ mode: 'open' });
this._shadow.innerHTML = `
${content}`;
window.document.body.appendChild(toast);
- // add click handler to "Don't show again" button
- const button = shadow.querySelector('.toast-button');
- button.addEventListener('click', () => {
- // save to localStorage to never show again and close toast rn
- localStorage.setItem('phoenix-hide-dynamic-toast', 'true');
- if (toast && toast.parentNode) {
- toast.remove();
- }
- if (_toastTimeout) {
- clearTimeout(_toastTimeout);
- _toastTimeout = null;
- }
- });
-
- // Auto-dismiss after 6 seconds
+ // Auto-dismiss after 3 seconds
_toastTimeout = setTimeout(() => {
if (toast && toast.parentNode) {
toast.remove();
}
_toastTimeout = null;
- }, 6000);
+ }, 3000);
+ }
+
+ /**
+ * this function is to dismiss the toast message
+ * and clear its timeout (if any)
+ */
+ function dismissToastMessage() {
+ const toastMessage = window.document.getElementById('phoenix-toast-notification');
+ if (toastMessage) {
+ toastMessage.remove();
+ }
+ if (_toastTimeout) {
+ clearTimeout(_toastTimeout);
+ }
+ _toastTimeout = null;
}
/**
diff --git a/src/LiveDevelopment/LivePreviewEdit.js b/src/LiveDevelopment/LivePreviewEdit.js
index e6575bdcfd..851e84d094 100644
--- a/src/LiveDevelopment/LivePreviewEdit.js
+++ b/src/LiveDevelopment/LivePreviewEdit.js
@@ -30,6 +30,8 @@ define(function (require, exports, module) {
const LiveDevelopment = require("LiveDevelopment/main");
const CodeMirror = require("thirdparty/CodeMirror/lib/codemirror");
const ProjectManager = require("project/ProjectManager");
+ const CommandManager = require("command/CommandManager");
+ const Commands = require("command/Commands");
const FileSystem = require("filesystem/FileSystem");
const PathUtils = require("thirdparty/path-utils/path-utils");
const StringMatch = require("utils/StringMatch");
@@ -1265,6 +1267,33 @@ define(function (require, exports, module) {
_showFolderSelectionDialog(null);
}
+ /**
+ * this function is responsible to save the active file (and previewed file, both might be same though)
+ * when ctrl/cmd + s is pressed in the live preview
+ */
+ function _handleLivePreviewSave() {
+ // this saves the active file
+ CommandManager.execute(Commands.FILE_SAVE);
+
+ // we also save the previewed file, (active file might be same as previewed or different)
+ const currLiveDoc = LiveDevMultiBrowser.getCurrentLiveDoc();
+ if (currLiveDoc && currLiveDoc.editor) {
+ const previewedDoc = currLiveDoc.editor.document;
+ CommandManager.execute(Commands.FILE_SAVE, { doc: previewedDoc });
+ }
+ }
+
+ /**
+ * This function is responsible to toggle the live preview Preview mode (play icon)
+ * this is done when user presses F8 key in the live preview
+ */
+ function _handlePreviewModeToggle() {
+ const $previewBtn = $("#previewModeLivePreviewButton");
+ if ($previewBtn.length > 0) {
+ $previewBtn.trigger("click");
+ }
+ }
+
/**
* This is the main function that is exported.
* it will be called by LiveDevProtocol when it receives a message from RemoteFunctions.js
@@ -1289,6 +1318,18 @@ define(function (require, exports, module) {
* these are the main properties that are passed through the message
*/
function handleLivePreviewEditOperation(message) {
+ // handle save current document in live preview (ctrl/cmd + s)
+ if (message.saveCurrentDocument) {
+ _handleLivePreviewSave();
+ return;
+ }
+
+ // toggle live preview mode using F8 key
+ if (message.toggleLivePreviewMode) {
+ _handlePreviewModeToggle();
+ return;
+ }
+
// handle reset image folder selection
if (message.resetImageFolderSelection) {
_handleResetImageFolderSelection();
diff --git a/src/LiveDevelopment/main.js b/src/LiveDevelopment/main.js
index 61ad9dba33..ad343d5880 100644
--- a/src/LiveDevelopment/main.js
+++ b/src/LiveDevelopment/main.js
@@ -111,7 +111,6 @@ define(function main(require, exports, module) {
imageGallery: Strings.LIVE_DEV_MORE_OPTIONS_IMAGE_GALLERY,
aiPromptPlaceholder: Strings.LIVE_DEV_AI_PROMPT_PLACEHOLDER,
imageGalleryUseImage: Strings.LIVE_DEV_IMAGE_GALLERY_USE_IMAGE,
- imageGallerySelectFromComputer: Strings.LIVE_DEV_IMAGE_GALLERY_SELECT_FROM_COMPUTER,
imageGallerySelectDownloadFolder: Strings.LIVE_DEV_IMAGE_GALLERY_SELECT_DOWNLOAD_FOLDER,
imageGallerySearchPlaceholder: Strings.LIVE_DEV_IMAGE_GALLERY_SEARCH_PLACEHOLDER,
imageGallerySearchButton: Strings.LIVE_DEV_IMAGE_GALLERY_SEARCH_BUTTON,
@@ -120,9 +119,9 @@ define(function main(require, exports, module) {
imageGalleryNoImages: Strings.LIVE_DEV_IMAGE_GALLERY_NO_IMAGES,
imageGalleryLoadError: Strings.LIVE_DEV_IMAGE_GALLERY_LOAD_ERROR,
imageGalleryClose: Strings.LIVE_DEV_IMAGE_GALLERY_CLOSE,
- imageGalleryUpload: Strings.LIVE_DEV_IMAGE_GALLERY_UPLOAD,
- toastNotEditable: Strings.LIVE_DEV_TOAST_NOT_EDITABLE,
- toastDontShowAgain: Strings.LIVE_DEV_TOAST_DONT_SHOW_AGAIN
+ imageGallerySelectFromComputer: Strings.LIVE_DEV_IMAGE_GALLERY_SELECT_FROM_COMPUTER,
+ imageGallerySelectFromComputerTooltip: Strings.LIVE_DEV_IMAGE_GALLERY_SELECT_FROM_COMPUTER_TOOLTIP,
+ toastNotEditable: Strings.LIVE_DEV_TOAST_NOT_EDITABLE
}
};
// Status labels/styles are ordered: error, not connected, progress1, progress2, connected.
@@ -465,7 +464,6 @@ define(function main(require, exports, module) {
config.highlight = PreferencesManager.getViewState("livedevHighlight");
function setLivePreviewEditFeaturesActive(enabled) {
- // TODO: @abose here add kernal mode trust check
isProUser = enabled;
config.isProUser = enabled;
if (MultiBrowserLiveDev && MultiBrowserLiveDev.status >= MultiBrowserLiveDev.STATUS_ACTIVE) {
diff --git a/src/base-config/keyboard.json b/src/base-config/keyboard.json
index 8674ac0619..6469aadec7 100644
--- a/src/base-config/keyboard.json
+++ b/src/base-config/keyboard.json
@@ -28,9 +28,6 @@
"key": "Ctrl-Shift-R"
}
],
- "file.previewHighlight": [
- "Ctrl-Shift-C"
- ],
"file.quit": [
"Ctrl-Q"
],
diff --git a/src/command/DefaultMenus.js b/src/command/DefaultMenus.js
index 8fec28a26e..31578dad57 100644
--- a/src/command/DefaultMenus.js
+++ b/src/command/DefaultMenus.js
@@ -230,8 +230,6 @@ define(function (require, exports, module) {
menu.addMenuItem(Commands.TOGGLE_WORD_WRAP);
menu.addMenuItem(Commands.TOGGLE_RULERS);
menu.addMenuDivider();
- menu.addMenuItem(Commands.FILE_LIVE_HIGHLIGHT);
- menu.addMenuDivider();
menu.addMenuItem(Commands.VIEW_TOGGLE_PROBLEMS);
menu.addMenuItem(Commands.VIEW_TOGGLE_INSPECTION);
diff --git a/src/extensionsIntegrated/Phoenix-live-preview/main.js b/src/extensionsIntegrated/Phoenix-live-preview/main.js
index 5e88c35cb7..547851b0ea 100644
--- a/src/extensionsIntegrated/Phoenix-live-preview/main.js
+++ b/src/extensionsIntegrated/Phoenix-live-preview/main.js
@@ -72,6 +72,7 @@ define(function (require, exports, module) {
panelHTML = require("text!./panel.html"),
Dialogs = require("widgets/Dialogs"),
DefaultDialogs = require("widgets/DefaultDialogs"),
+ ProDialogs = require("services/pro-dialogs"),
utils = require('./utils');
const StateManager = PreferencesManager.stateManager;
@@ -273,35 +274,6 @@ define(function (require, exports, module) {
}
}
- function _showProFeatureDialog() {
- const dialog = Dialogs.showModalDialog(
- DefaultDialogs.DIALOG_ID_INFO,
- Strings.LIVE_PREVIEW_PRO_FEATURE_TITLE,
- Strings.LIVE_PREVIEW_PRO_FEATURE_MESSAGE,
- [
- {
- className: Dialogs.DIALOG_BTN_CLASS_NORMAL,
- id: Dialogs.DIALOG_BTN_CANCEL,
- text: Strings.CANCEL
- },
- {
- className: Dialogs.DIALOG_BTN_CLASS_PRIMARY,
- id: "subscribe",
- text: Strings.LIVE_PREVIEW_PRO_SUBSCRIBE
- }
- ]
- );
-
- dialog.done(function (buttonId) {
- if (buttonId === "subscribe") {
- // TODO: write the implementation here...@abose
- console.log("the subscribe button got clicked");
- }
- });
-
- return dialog;
- }
-
// this function is to check if the live highlight feature is enabled or not
function _isLiveHighlightEnabled() {
return CommandManager.get(Commands.FILE_LIVE_HIGHLIGHT).getChecked();
@@ -438,7 +410,7 @@ define(function (require, exports, module) {
LiveDevelopment.setMode("highlight");
} else if (index === 2) {
if (!LiveDevelopment.setMode("edit")) {
- _showProFeatureDialog();
+ ProDialogs.showProUpsellDialog(ProDialogs.UPSELL_TYPE_LIVE_EDIT);
}
} else if (item === Strings.LIVE_PREVIEW_EDIT_HIGHLIGHT_ON) {
// Don't allow edit highlight toggle if edit features are not active
@@ -718,7 +690,7 @@ define(function (require, exports, module) {
Strings: Strings,
livePreview: Strings.LIVE_DEV_STATUS_TIP_OUT_OF_SYNC,
clickToReload: Strings.LIVE_DEV_CLICK_TO_RELOAD_PAGE,
- clickToPreview: Strings.LIVE_PREVIEW_MODE_PREVIEW,
+ clickToPreview: Strings.LIVE_PREVIEW_MODE_TOGGLE_PREVIEW,
livePreviewSettings: Strings.LIVE_DEV_SETTINGS,
livePreviewConfigureModes: Strings.LIVE_PREVIEW_CONFIGURE_MODES,
clickToPopout: Strings.LIVE_DEV_CLICK_POPOUT,
@@ -1174,6 +1146,13 @@ define(function (require, exports, module) {
$(document).on("click", "#livePreviewModeBtn", function (e) {
_handleLPModeBtnClick(e);
});
+
+ $(document).on("keydown", function (e) {
+ if (e.key === "F8") {
+ e.preventDefault();
+ _handlePreviewBtnClick();
+ }
+ });
}
AppInit.appReady(function () {
diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js
index ed03f8e14b..9db3935857 100644
--- a/src/nls/root/strings.js
+++ b/src/nls/root/strings.js
@@ -191,7 +191,6 @@ define({
"LIVE_DEV_MORE_OPTIONS_AI": "Edit with AI",
"LIVE_DEV_MORE_OPTIONS_IMAGE_GALLERY": "Image Gallery",
"LIVE_DEV_IMAGE_GALLERY_USE_IMAGE": "Use this image",
- "LIVE_DEV_IMAGE_GALLERY_SELECT_FROM_COMPUTER": "Upload image from computer",
"LIVE_DEV_IMAGE_GALLERY_SELECT_DOWNLOAD_FOLDER": "Choose image download folder",
"LIVE_DEV_IMAGE_GALLERY_SEARCH_PLACEHOLDER": "Search images\u2026",
"LIVE_DEV_IMAGE_GALLERY_SEARCH_BUTTON": "Search",
@@ -200,9 +199,9 @@ define({
"LIVE_DEV_IMAGE_GALLERY_NO_IMAGES": "No images found",
"LIVE_DEV_IMAGE_GALLERY_LOAD_ERROR": "Failed to load images",
"LIVE_DEV_IMAGE_GALLERY_CLOSE": "Close",
- "LIVE_DEV_IMAGE_GALLERY_UPLOAD": "Upload",
+ "LIVE_DEV_IMAGE_GALLERY_SELECT_FROM_COMPUTER_TOOLTIP": "Select an image from your device",
+ "LIVE_DEV_IMAGE_GALLERY_SELECT_FROM_COMPUTER": "Select from device",
"LIVE_DEV_TOAST_NOT_EDITABLE": "Element not editable - generated by script.",
- "LIVE_DEV_TOAST_DONT_SHOW_AGAIN": "Don't show again",
"LIVE_DEV_IMAGE_FOLDER_DIALOG_TITLE": "Select Folder to Save Image",
"LIVE_DEV_IMAGE_FOLDER_DIALOG_DESCRIPTION": "Choose where to download the image:",
"LIVE_DEV_IMAGE_FOLDER_DIALOG_PLACEHOLDER": "Type folder path (e.g., assets/images/)",
@@ -210,15 +209,13 @@ define({
"LIVE_DEV_IMAGE_FOLDER_DIALOG_REMEMBER": "Don't ask again for this project",
"LIVE_DEV_AI_PROMPT_PLACEHOLDER": "Ask Phoenix AI to modify this element...",
"LIVE_PREVIEW_CUSTOM_SERVER_BANNER": "Getting preview from your custom server {0}",
+ "LIVE_PREVIEW_MODE_TOGGLE_PREVIEW": "Toggle Preview Mode (F8)",
"LIVE_PREVIEW_MODE_PREVIEW": "Preview Mode",
"LIVE_PREVIEW_MODE_HIGHLIGHT": "Highlight Mode",
"LIVE_PREVIEW_MODE_EDIT": "Edit Mode",
"LIVE_PREVIEW_EDIT_HIGHLIGHT_ON": "Edit Highlights on Hover",
"LIVE_PREVIEW_MODE_PREFERENCE": "{0} shows only the webpage, {1} connects the webpage to your code - click on elements to jump to their code and vice versa, {2} provides highlighting along with advanced element manipulation",
"LIVE_PREVIEW_CONFIGURE_MODES": "Configure Live Preview Modes",
- "LIVE_PREVIEW_PRO_FEATURE_TITLE": "Pro Feature",
- "LIVE_PREVIEW_PRO_FEATURE_MESSAGE": "This is a Pro feature. Subscribe to Phoenix Pro to keep using this feature.",
- "LIVE_PREVIEW_PRO_SUBSCRIBE": "Subscribe",
"LIVE_DEV_DETACHED_REPLACED_WITH_DEVTOOLS": "Live Preview was canceled because the browser's developer tools were opened",
"LIVE_DEV_DETACHED_TARGET_CLOSED": "Live Preview was canceled because the page was closed in the browser",