Skip to content

Commit 64a7633

Browse files
committed
deploy: 606b7d0
1 parent 563d7e3 commit 64a7633

File tree

89 files changed

+3856
-1632
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

89 files changed

+3856
-1632
lines changed

LiveDevelopment/BrowserScripts/RemoteFunctions.js

Lines changed: 653 additions & 338 deletions
Large diffs are not rendered by default.

LiveDevelopment/LiveDevMultiBrowser.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,7 @@ define(function (require, exports, module) {
435435
const urlString = `${url.origin}${url.pathname}`;
436436
if (_liveDocument && urlString === _resolveUrl(_liveDocument.doc.file.fullPath)) {
437437
_setStatus(STATUS_ACTIVE);
438+
resetLPEditState();
438439
}
439440
}
440441
Metrics.countEvent(Metrics.EVENT_TYPE.LIVE_PREVIEW, "connect",
@@ -733,7 +734,18 @@ define(function (require, exports, module) {
733734
*/
734735
function updateConfig(configJSON) {
735736
if (_protocol) {
736-
_protocol.evaluate("_LD.updateConfig('" + configJSON + "')");
737+
_protocol.evaluate("_LD.updateConfig(" + JSON.stringify(configJSON) + ")");
738+
}
739+
}
740+
741+
/**
742+
* this function is to completely reset the live preview edit
743+
* its done so that when live preview is opened/popped out, we can re-update the config so that
744+
* there are no stale markers and edit works perfectly
745+
*/
746+
function resetLPEditState() {
747+
if (_protocol) {
748+
_protocol.evaluate("_LD.resetState()");
737749
}
738750
}
739751

LiveDevelopment/LivePreviewEdit.js

Lines changed: 76 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
define(function (require, exports, module) {
2828
const HTMLInstrumentation = require("LiveDevelopment/MultiBrowserImpl/language/HTMLInstrumentation");
2929
const LiveDevMultiBrowser = require("LiveDevelopment/LiveDevMultiBrowser");
30+
const LiveDevelopment = require("LiveDevelopment/main");
3031
const CodeMirror = require("thirdparty/CodeMirror/lib/codemirror");
3132
const ProjectManager = require("project/ProjectManager");
3233
const FileSystem = require("filesystem/FileSystem");
@@ -41,6 +42,14 @@ define(function (require, exports, module) {
4142

4243
// state manager key, to save the download location of the image
4344
const IMAGE_DOWNLOAD_FOLDER_KEY = "imageGallery.downloadFolder";
45+
const IMAGE_DOWNLOAD_PERSIST_FOLDER_KEY = "imageGallery.persistFolder";
46+
47+
const DOWNLOAD_EVENTS = {
48+
STARTED: 'downloadStarted',
49+
COMPLETED: 'downloadCompleted',
50+
CANCELLED: 'downloadCancelled',
51+
ERROR: 'downloadError'
52+
};
4453

4554
const KernalModeTrust = window.KernalModeTrust;
4655
if(!KernalModeTrust){
@@ -685,6 +694,32 @@ define(function (require, exports, module) {
685694
}
686695
}
687696

697+
function _sendDownloadStatusToBrowser(eventType, data) {
698+
const currLiveDoc = LiveDevMultiBrowser.getCurrentLiveDoc();
699+
if (currLiveDoc && currLiveDoc.protocol && currLiveDoc.protocol.evaluate) {
700+
const dataJson = JSON.stringify(data || {});
701+
const evalString = `_LD.handleDownloadEvent('${eventType}', ${dataJson})`;
702+
currLiveDoc.protocol.evaluate(evalString);
703+
}
704+
}
705+
706+
function _handleDownloadError(error, downloadId) {
707+
console.error('something went wrong while download the image. error:', error);
708+
if (downloadId) {
709+
_sendDownloadStatusToBrowser(DOWNLOAD_EVENTS.ERROR, { downloadId: downloadId });
710+
}
711+
}
712+
713+
function _trackDownload(downloadLocation) {
714+
if (!downloadLocation) {
715+
return;
716+
}
717+
fetch(`https://images.phcode.dev/api/images/download?download_location=${encodeURIComponent(downloadLocation)}`)
718+
.catch(error => {
719+
console.error('download tracking failed:', error);
720+
});
721+
}
722+
688723
/**
689724
* Helper function to update image src attribute and dismiss ribbon gallery
690725
*
@@ -716,16 +751,18 @@ define(function (require, exports, module) {
716751
* @param {Directory} projectRoot - the project root in which the image is to be saved
717752
*/
718753
function _handleUseThisImageLocalFiles(message, filename, projectRoot) {
719-
const { tagId, imageData } = message;
754+
const { tagId, imageData, downloadLocation, downloadId } = message;
720755

721756
const uint8Array = new Uint8Array(imageData);
722757
const targetPath = projectRoot.fullPath + filename;
723758

724759
window.fs.writeFile(targetPath, window.Filer.Buffer.from(uint8Array),
725760
{ encoding: window.fs.BYTE_ARRAY_ENCODING }, (err) => {
726761
if (err) {
727-
console.error('Failed to save image:', err);
762+
_handleDownloadError(err, downloadId);
728763
} else {
764+
_trackDownload(downloadLocation);
765+
_sendDownloadStatusToBrowser(DOWNLOAD_EVENTS.COMPLETED, { downloadId });
729766
_updateImageAndDismissRibbon(tagId, targetPath, filename);
730767
}
731768
});
@@ -738,7 +775,7 @@ define(function (require, exports, module) {
738775
* @param {Directory} projectRoot - the project root in which the image is to be saved
739776
*/
740777
function _handleUseThisImageRemote(message, filename, projectRoot) {
741-
const { imageUrl, tagId } = message;
778+
const { imageUrl, tagId, downloadLocation, downloadId } = message;
742779

743780
fetch(imageUrl)
744781
.then(response => {
@@ -754,14 +791,16 @@ define(function (require, exports, module) {
754791
window.fs.writeFile(targetPath, window.Filer.Buffer.from(uint8Array),
755792
{ encoding: window.fs.BYTE_ARRAY_ENCODING }, (err) => {
756793
if (err) {
757-
console.error('Failed to save image:', err);
794+
_handleDownloadError(err, downloadId);
758795
} else {
796+
_trackDownload(downloadLocation);
797+
_sendDownloadStatusToBrowser(DOWNLOAD_EVENTS.COMPLETED, { downloadId });
759798
_updateImageAndDismissRibbon(tagId, targetPath, filename);
760799
}
761800
});
762801
})
763802
.catch(error => {
764-
console.error('Failed to fetch image:', error);
803+
_handleDownloadError(error, downloadId);
765804
});
766805
}
767806

@@ -778,6 +817,10 @@ define(function (require, exports, module) {
778817
return;
779818
}
780819

820+
if (message.downloadId) {
821+
_sendDownloadStatusToBrowser(DOWNLOAD_EVENTS.STARTED, { downloadId: message.downloadId });
822+
}
823+
781824
const filename = message.filename;
782825
const extnName = message.extnName || "jpg";
783826

@@ -792,11 +835,17 @@ define(function (require, exports, module) {
792835
// the directory name that user wrote, first check if it exists or not
793836
// if it doesn't exist we create it and then download the image inside it
794837
targetDir.exists((err, exists) => {
795-
if (err) { return; }
838+
if (err) {
839+
_handleDownloadError(err, message.downloadId);
840+
return;
841+
}
796842

797843
if (!exists) {
798844
targetDir.create((err) => {
799-
if (err) { return; }
845+
if (err) {
846+
_handleDownloadError(err, message.downloadId);
847+
return;
848+
}
800849
_downloadImageToDirectory(message, filename, extnName, targetDir);
801850
});
802851
} else {
@@ -1109,6 +1158,10 @@ define(function (require, exports, module) {
11091158
let rootFolders = [];
11101159
let stringMatcher = null;
11111160

1161+
const persistFolder = StateManager.get(IMAGE_DOWNLOAD_PERSIST_FOLDER_KEY, StateManager.PROJECT_CONTEXT);
1162+
const shouldBeChecked = persistFolder !== false;
1163+
$rememberCheckbox.prop('checked', shouldBeChecked);
1164+
11121165
_scanRootDirectoriesOnly(projectRoot, rootFolders).then(() => {
11131166
stringMatcher = new StringMatch.StringMatcher({ segmentedSearch: true });
11141167
_renderFolderSuggestions(rootFolders.slice(0, 15), $suggestions, $input);
@@ -1134,14 +1187,22 @@ define(function (require, exports, module) {
11341187
const folderPath = $input.val().trim();
11351188

11361189
// if the checkbox is checked, we save the folder preference for this project
1137-
if ($rememberCheckbox.is(':checked')) {
1190+
const isChecked = $rememberCheckbox.is(':checked');
1191+
StateManager.set(IMAGE_DOWNLOAD_PERSIST_FOLDER_KEY, isChecked, StateManager.PROJECT_CONTEXT);
1192+
if (isChecked) {
11381193
StateManager.set(IMAGE_DOWNLOAD_FOLDER_KEY, folderPath, StateManager.PROJECT_CONTEXT);
1194+
} else {
1195+
StateManager.set(IMAGE_DOWNLOAD_FOLDER_KEY, undefined, StateManager.PROJECT_CONTEXT);
11391196
}
11401197

11411198
// if message is provided, download the image
11421199
if (message) {
11431200
_downloadToFolder(message, folderPath);
11441201
}
1202+
} else {
1203+
if (message && message.downloadId) {
1204+
_sendDownloadStatusToBrowser(DOWNLOAD_EVENTS.CANCELLED, { downloadId: message.downloadId });
1205+
}
11451206
}
11461207
dialog.close();
11471208
});
@@ -1187,7 +1248,7 @@ define(function (require, exports, module) {
11871248
_handleUseThisImageRemote(message, uniqueFilename, targetDir);
11881249
}
11891250
}).catch(error => {
1190-
console.error('Something went wrong when trying to use this image', error);
1251+
_handleDownloadError(error, message.downloadId);
11911252
});
11921253
}
11931254

@@ -1234,6 +1295,12 @@ define(function (require, exports, module) {
12341295
return;
12351296
}
12361297

1298+
// handle image gallery state change message
1299+
if (message.type === "imageGalleryStateChange") {
1300+
LiveDevelopment.setImageGalleryState(message.selected);
1301+
return;
1302+
}
1303+
12371304
// handle move(drag & drop)
12381305
if (message.move && message.sourceId && message.targetId) {
12391306
_moveElementInSource(message.sourceId, message.targetId, message.insertAfter, message.insertInside);

LiveDevelopment/main.js

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ define(function main(require, exports, module) {
3838
LivePreviewTransport = require("LiveDevelopment/MultiBrowserImpl/transports/LivePreviewTransport"),
3939
CommandManager = require("command/CommandManager"),
4040
PreferencesManager = require("preferences/PreferencesManager"),
41+
StateManager = require("preferences/StateManager"),
4142
UrlParams = require("utils/UrlParams").UrlParams,
4243
Strings = require("strings"),
4344
ExtensionUtils = require("utils/ExtensionUtils"),
@@ -56,6 +57,33 @@ define(function main(require, exports, module) {
5657

5758
const EVENT_LIVE_HIGHLIGHT_PREF_CHANGED = "liveHighlightPrefChange";
5859

60+
// state manager key to track image gallery selected state, by default we keep this as selected
61+
// if this is true we show the image gallery when an image element is clicked
62+
const IMAGE_GALLERY_STATE = "livePreview.imageGallery.state";
63+
64+
/**
65+
* get the image gallery state from StateManager
66+
* @returns {boolean} true (default)
67+
*/
68+
function _getImageGalleryState() {
69+
const savedState = StateManager.get(IMAGE_GALLERY_STATE);
70+
return savedState !== undefined && savedState !== null ? savedState : true;
71+
}
72+
73+
/**
74+
* sets the image gallery state
75+
* @param {Boolean} the state that we need to set
76+
*/
77+
function setImageGalleryState(state) {
78+
StateManager.set(IMAGE_GALLERY_STATE, state);
79+
80+
// update the config with the new state
81+
config.imageGalleryState = state;
82+
if (MultiBrowserLiveDev && MultiBrowserLiveDev.status >= MultiBrowserLiveDev.STATUS_ACTIVE) {
83+
MultiBrowserLiveDev.updateConfig(JSON.stringify(config));
84+
}
85+
}
86+
5987
var params = new UrlParams();
6088
var config = {
6189
experimental: false, // enable experimental features
@@ -70,6 +98,7 @@ define(function main(require, exports, module) {
7098
},
7199
isProUser: isProUser,
72100
elemHighlights: "hover", // default value, this will get updated when the extension loads
101+
imageGalleryState: _getImageGalleryState(), // image gallery selected state
73102
// this strings are used in RemoteFunctions.js
74103
// we need to pass this through config as remoteFunctions runs in browser context and cannot
75104
// directly reference Strings file
@@ -89,7 +118,11 @@ define(function main(require, exports, module) {
89118
imageGalleryLoadingInitial: Strings.LIVE_DEV_IMAGE_GALLERY_LOADING_INITIAL,
90119
imageGalleryLoadingMore: Strings.LIVE_DEV_IMAGE_GALLERY_LOADING_MORE,
91120
imageGalleryNoImages: Strings.LIVE_DEV_IMAGE_GALLERY_NO_IMAGES,
92-
imageGalleryLoadError: Strings.LIVE_DEV_IMAGE_GALLERY_LOAD_ERROR
121+
imageGalleryLoadError: Strings.LIVE_DEV_IMAGE_GALLERY_LOAD_ERROR,
122+
imageGalleryClose: Strings.LIVE_DEV_IMAGE_GALLERY_CLOSE,
123+
imageGalleryUpload: Strings.LIVE_DEV_IMAGE_GALLERY_UPLOAD,
124+
toastNotEditable: Strings.LIVE_DEV_TOAST_NOT_EDITABLE,
125+
toastDontShowAgain: Strings.LIVE_DEV_TOAST_DONT_SHOW_AGAIN
93126
}
94127
};
95128
// Status labels/styles are ordered: error, not connected, progress1, progress2, connected.
@@ -402,6 +435,7 @@ define(function main(require, exports, module) {
402435
exports.setLivePreviewTransportBridge = setLivePreviewTransportBridge;
403436
exports.togglePreviewHighlight = togglePreviewHighlight;
404437
exports.setLivePreviewEditFeaturesActive = setLivePreviewEditFeaturesActive;
438+
exports.setImageGalleryState = setImageGalleryState;
405439
exports.updateElementHighlightConfig = updateElementHighlightConfig;
406440
exports.getConnectionIds = MultiBrowserLiveDev.getConnectionIds;
407441
exports.getLivePreviewDetails = MultiBrowserLiveDev.getLivePreviewDetails;

appConfig.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ window.AppConfig = {
3333
"app_update_url": "https://updates.phcode.io/tauri/update-latest-experimental-build.json",
3434
"extensionTakedownURL": "https://updates.phcode.io/extension_takedown.json",
3535
"linting.enabled_by_default": true,
36-
"build_timestamp": "2025-10-26T12:17:46.039Z",
36+
"build_timestamp": "2025-11-20T08:24:09.485Z",
3737
"googleAnalyticsID": "G-P4HJFPDB76",
3838
"googleAnalyticsIDDesktop": "G-VE5BXWJ0HF",
3939
"mixPanelID": "49c4d164b592be2350fc7af06a259bf3",
@@ -45,7 +45,7 @@ window.AppConfig = {
4545
"bugsnagEnv": "development"
4646
},
4747
"name": "Phoenix Code",
48-
"version": "4.1.2-21672",
48+
"version": "4.1.2-21709",
4949
"apiVersion": "4.1.2",
5050
"homepage": "https://core.ai",
5151
"issues": {

assets/default-project/en.zip

0 Bytes
Binary file not shown.

assets/sample-projects/HTML5.zip

0 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.

assets/sample-projects/explore.zip

0 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)