Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 5 additions & 1 deletion src/assets/new-project/assets/js/code-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ function _openURLInTauri(url) {
function _updateProBranding() {
try {
const $freeTitle = $('.phoenix-free-title');
const $proTitle = $('.phoenix-pro-title');
const $proTitle = $('#phoenix-pro-title');
const $proTitleSpan = $('.pro-plan-name');

if (!$freeTitle.length || !$proTitle.length || !$proTitleSpan.length) {
Expand Down Expand Up @@ -222,6 +222,10 @@ function initCodeEditor() {
Metrics.countEvent(Metrics.EVENT_TYPE.NEW_PROJECT, "main.Click", "viewMore");
window.location.href = 'new-project-more.html';
};
document.getElementById("phoenix-pro-title").onclick = function() {
Metrics.countEvent(Metrics.EVENT_TYPE.NEW_PROJECT, "main.Click", "proTitle");
_openURLInTauri("https://account.phcode.dev");
};
document.getElementById("githubStarsButton").onclick = function() {
Metrics.countEvent(Metrics.EVENT_TYPE.NEW_PROJECT, "main.Click", "githubStars");
_openURLInTauri("https://github.com/phcode-dev/phoenix");
Expand Down
4 changes: 2 additions & 2 deletions src/assets/new-project/code-editor.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<script src="../../thirdparty/floating-ui.dom.umd.min.js"></script>
<script src="assets/js/notification-ui.js"></script>
<style>
.phoenix-pro-title {
#phoenix-pro-title {
background: linear-gradient(
45deg,
#ff8c42, /* deep orange */
Expand All @@ -41,7 +41,7 @@
</div>
<div class="editor-name">
<h4 class="localize phoenix-free-title">{{APP_TITLE}}</h4>
<h4 class="phoenix-pro-title forced-hidden"><a href="https://account.phcode.dev" target="_blank" rel="noopener" class="phoenix-pro"><span class="pro-plan-name">Phoenix Pro</span><i class="fa-solid fa-feather orange-gold" style="margin-left: 4px;"></i></a></h4>
<h4 id="phoenix-pro-title" class="forced-hidden"><a href="https://account.phcode.dev" target="_blank" rel="noopener" class="phoenix-pro"><span class="pro-plan-name">Phoenix Pro</span><i class="fa-solid fa-feather orange-gold" style="margin-left: 4px;"></i></a></h4>
<!-- Place this tag where you want the button to render. -->
<div style="display: flex; justify-content: space-between;">
<span class="localize">{{BUILD_THE_WEB}}</span>
Expand Down
2 changes: 1 addition & 1 deletion src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@
baseURL: _getBaseURL(),
isTestWindow: _isTestWindow(),
firstBoot: false, // will be set below
pro: {},
pro: {}, // this is only for display purposes and should not be used to gate features. use kernal mode for that
startTime: Date.now(),
TRUSTED_ORIGINS: {
// if modifying this list, make sure to update in https://github.com/phcode-dev/phcode.live/blob/main/docs/trustedOrigins.js
Expand Down
15 changes: 14 additions & 1 deletion src/nls/root/strings.js
Original file line number Diff line number Diff line change
Expand Up @@ -1661,5 +1661,18 @@ define({
"CUSTOM_SNIPPETS_HEADER_ABBREVIATION": "Abbreviation",
"CUSTOM_SNIPPETS_HEADER_TEMPLATE": "Template Text",
"CUSTOM_SNIPPETS_HEADER_DESCRIPTION": "Description",
"CUSTOM_SNIPPETS_HEADER_FILE_EXTENSION": "File Extension"
"CUSTOM_SNIPPETS_HEADER_FILE_EXTENSION": "File Extension",

// promos
"PROMO_UPGRADE_TITLE": "You’ve been upgraded to {0}",
"PROMO_UPGRADE_MESSAGE": "Enjoy full access to all premium features for the next {0} days:",
"PROMO_CARD_1": "Drag & Drop Elements",
"PROMO_CARD_1_MESSAGE": "Rearrange sections visually — Phoenix updates the HTML & CSS for you.",
"PROMO_CARD_2": "Image Replacement",
"PROMO_CARD_2_MESSAGE": "Click any image to replace it instantly and preview changes in real time.",
"PROMO_CARD_3": "Element Duplication and Deletion",
"PROMO_CARD_3_MESSAGE": "Duplicate and delete elements with a single click.",
"PROMO_CARD_4": "Editing Text In Preview",
"PROMO_CARD_4_MESSAGE": "Edit headings, buttons, and copy directly in the preview.",
"PROMO_LEARN_MORE": "Learn More\u2026"
});
2 changes: 2 additions & 0 deletions src/phoenix/trust_ring.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,13 +179,15 @@ function _selectKeys() {
}

const CRED_KEY_API = "API_KEY";
const CRED_KEY_ENTITLEMENTS = "ENTITLEMENTS_GRANT_KEY";
const { key, iv } = _selectKeys();
// this key is set at boot time as a truct base for all the core components before any extensions are loaded.
// just before extensions are loaded, this key is blanked. This can be used by core modules to talk with other
// core modules securely without worrying about interception by extensions.
// KernalModeTrust should only be available within all code that loads before the first default/any extension.
window.KernalModeTrust = {
CRED_KEY_API,
CRED_KEY_ENTITLEMENTS,
aesKeys: { key, iv },
setCredential,
getCredential,
Expand Down
56 changes: 56 additions & 0 deletions src/services/html/pro-upgrade.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<div class="browser-login-waiting-dialog modal">
<div class="modal-header">
<h1 class="dialog-title">{{{title}}}</h1>
</div>

<div class="modal-body" style="max-height: 550px;">
<div class="waiting-content-container">
<p style="margin-bottom: 20px; font-size: 14px;">
{{message}}
</p>

<div class="features-grid">
<!-- Card 1 -->
<div class="feature-card">
<img src="https://docs-images.phcode.dev/phcode-sdk/quick-view-image.png" alt="{{Strings.PROMO_CARD_1}}" class="feature-thumb">
<div class="feature-body">
<h2>{{Strings.PROMO_CARD_1}}</h2>
<p>{{Strings.PROMO_CARD_1_MESSAGE}}</p>
</div>
</div>

<!-- Card 2 -->
<div class="feature-card">
<img src="https://docs-images.phcode.dev/phcode-sdk/quick-view-image.png" alt="{{Strings.PROMO_CARD_2}}" class="feature-thumb">
<div class="feature-body">
<h2>{{Strings.PROMO_CARD_2}}</h2>
<p>{{Strings.PROMO_CARD_2_MESSAGE}}</p>
</div>
</div>

<!-- Card 3 -->
<div class="feature-card">
<img src="https://docs-images.phcode.dev/phcode-sdk/quick-view-image.png" alt="{{Strings.PROMO_CARD_3}}" class="feature-thumb">
<div class="feature-body">
<h2>{{Strings.PROMO_CARD_3}}</h2>
<p>{{Strings.PROMO_CARD_3_MESSAGE}}</p>
</div>
</div>

<!-- Card 4 -->
<div class="feature-card">
<img src="https://docs-images.phcode.dev/phcode-sdk/quick-view-image.png" alt="{{Strings.PROMO_CARD_4}}" class="feature-thumb">
<div class="feature-body">
<h2>{{Strings.PROMO_CARD_4}}</h2>
<p>{{Strings.PROMO_CARD_4_MESSAGE}}</p>
</div>
</div>
</div>
</div>
</div>

<div class="modal-footer">
<button class="dialog-button btn" data-button-id="learn_more">{{Strings.PROMO_LEARN_MORE}}</button>
<button class="dialog-button btn primary" data-button-id="ok">{{Strings.OK}}</button>
</div>
</div>
39 changes: 10 additions & 29 deletions src/services/login-browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,14 @@
*/

define(function (require, exports, module) {
const EventDispatcher = require("utils/EventDispatcher"),
PreferencesManager = require("preferences/PreferencesManager"),
require("./login-service"); // after this, loginService will be in KernalModeTrust
const PreferencesManager = require("preferences/PreferencesManager"),
Metrics = require("utils/Metrics"),
Dialogs = require("widgets/Dialogs"),
DefaultDialogs = require("widgets/DefaultDialogs"),
Strings = require("strings"),
StringUtils = require("utils/StringUtils"),
ProfileMenu = require("./profile-menu"),
LoginService = require("./login-service"),
Mustache = require("thirdparty/mustache/mustache"),
browserLoginWaitingTemplate = require("text!./html/browser-login-waiting-dialog.html");

Expand All @@ -60,11 +59,7 @@ define(function (require, exports, module) {
// integrated extensions will have access to kernal mode, but not external extensions
throw new Error("Browser Login service should have access to KernalModeTrust. Cannot boot without trust ring");
}
const secureExports = {};
// Only set loginService for browser apps to avoid conflict with desktop login
if (!Phoenix.isNativeApp) {
KernalModeTrust.loginService = secureExports;
}
const LoginService = KernalModeTrust.loginService;

// user profile structure: "customerID": "uuid...", "firstName":"Aa","lastName":"bb",
// "email":"aaaa@sss.com", "loginTime":1750074393853, "isSuccess": true,
Expand All @@ -75,14 +70,6 @@ define(function (require, exports, module) {
// just used as trigger to notify different windows about user profile changes
const PREF_USER_PROFILE_VERSION = "userProfileVersion";

EventDispatcher.makeEventDispatcher(exports);
EventDispatcher.makeEventDispatcher(secureExports);

const _EVT_PAGE_FOCUSED = "page_focused";
$(window).focus(function () {
exports.trigger(_EVT_PAGE_FOCUSED);
});

function isLoggedIn() {
return isLoggedInUser;
}
Expand Down Expand Up @@ -199,7 +186,6 @@ define(function (require, exports, module) {
}

let loginWaitingDialog = null;
let focusCheckInterval = null;

/**
* Show waiting dialog with auto-detection and manual check options
Expand Down Expand Up @@ -288,10 +274,6 @@ define(function (require, exports, module) {
loginWaitingDialog.close();
loginWaitingDialog = null;
}
if (focusCheckInterval) {
clearInterval(focusCheckInterval);
focusCheckInterval = null;
}
$(window).off('focus.loginWaiting');
}

Expand Down Expand Up @@ -413,14 +395,13 @@ define(function (require, exports, module) {
if (!Phoenix.isNativeApp) {
init();
// kernal exports
secureExports.isLoggedIn = isLoggedIn;
secureExports.signInToAccount = signInToBrowser;
secureExports.signOutAccount = signOutBrowser;
secureExports.getProfile = getProfile;
secureExports.verifyLoginStatus = () => _verifyBrowserLogin(false);
secureExports.getAccountBaseURL = _getAccountBaseURL;
secureExports.getEntitlements = LoginService.getEntitlements;
secureExports.EVENT_ENTITLEMENTS_CHANGED = LoginService.EVENT_ENTITLEMENTS_CHANGED;
// Add to existing KernalModeTrust.loginService from login-service.js
LoginService.isLoggedIn = isLoggedIn;
LoginService.signInToAccount = signInToBrowser;
LoginService.signOutAccount = signOutBrowser;
LoginService.getProfile = getProfile;
LoginService.verifyLoginStatus = () => _verifyBrowserLogin(false);
LoginService.getAccountBaseURL = _getAccountBaseURL;
}

// public exports
Expand Down
37 changes: 16 additions & 21 deletions src/services/login-desktop.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
/*global logger*/

define(function (require, exports, module) {
require("./login-service"); // after this, loginService will be in KernalModeTrust

const EventDispatcher = require("utils/EventDispatcher"),
PreferencesManager = require("preferences/PreferencesManager"),
Metrics = require("utils/Metrics"),
Expand All @@ -27,7 +29,6 @@ define(function (require, exports, module) {
Strings = require("strings"),
NativeApp = require("utils/NativeApp"),
ProfileMenu = require("./profile-menu"),
LoginService = require("./login-service"),
Mustache = require("thirdparty/mustache/mustache"),
NodeConnector = require("NodeConnector"),
otpDialogTemplate = require("text!./html/otp-dialog.html");
Expand All @@ -37,11 +38,8 @@ define(function (require, exports, module) {
// integrated extensions will have access to kernal mode, but not external extensions
throw new Error("Login service should have access to KernalModeTrust. Cannot boot without trust ring");
}
const secureExports = {};
// Only set loginService for native apps to avoid conflict with browser login
if (Phoenix.isNativeApp) {
KernalModeTrust.loginService = secureExports;
}
const LoginService = KernalModeTrust.loginService;

// user profile is something like "apiKey": "uuid...", validationCode: "dfdf", "firstName":"Aa","lastName":"bb",
// "email":"aaaa@sss.com", "customerID":"uuid...","loginTime":1750074393853,
// "profileIcon":{"color":"#14b8a6","initials":"AB"}
Expand All @@ -51,12 +49,11 @@ define(function (require, exports, module) {
// just used as trigger to notify different windows about user profile changes
const PREF_USER_PROFILE_VERSION = "userProfileVersion";

EventDispatcher.makeEventDispatcher(exports);
EventDispatcher.makeEventDispatcher(secureExports);

const _EVT_PAGE_FOCUSED = "page_focused";
const focusWatcher = {};
EventDispatcher.makeEventDispatcher(focusWatcher);
$(window).focus(function () {
exports.trigger(_EVT_PAGE_FOCUSED);
focusWatcher.trigger(_EVT_PAGE_FOCUSED);
});

const AUTH_CONNECTOR_ID = "ph_auth";
Expand Down Expand Up @@ -320,7 +317,7 @@ define(function (require, exports, module) {
}
}
let isAutoSignedIn = false;
exports.on(_EVT_PAGE_FOCUSED, checkLoginStatus);
focusWatcher.on(_EVT_PAGE_FOCUSED, checkLoginStatus);
async function _AutoSignedIn() {
isAutoSignedIn = true;
await checkLoginStatus();
Expand All @@ -329,7 +326,7 @@ define(function (require, exports, module) {

// Clean up when dialog is closed
dialog.done(function() {
exports.off(_EVT_PAGE_FOCUSED, checkLoginStatus);
focusWatcher.off(_EVT_PAGE_FOCUSED, checkLoginStatus);
authNodeConnector.off(EVENT_CONNECTED, _AutoSignedIn);
clearTimeout(closeTimeout);
Metrics.countEvent(Metrics.EVENT_TYPE.AUTH,
Expand Down Expand Up @@ -411,15 +408,13 @@ define(function (require, exports, module) {
// Only set exports for native apps to avoid conflict with browser login
if (Phoenix.isNativeApp) {
init();
// kernal exports
secureExports.isLoggedIn = isLoggedIn;
secureExports.signInToAccount = signInToAccount;
secureExports.signOutAccount = signOutAccount;
secureExports.getProfile = getProfile;
secureExports.verifyLoginStatus = () => _verifyLogin(false);
secureExports.getAccountBaseURL = getAccountBaseURL;
secureExports.getEntitlements = LoginService.getEntitlements;
secureExports.EVENT_ENTITLEMENTS_CHANGED = LoginService.EVENT_ENTITLEMENTS_CHANGED;
// kernal exports - add to existing KernalModeTrust.loginService from login-service.js
LoginService.isLoggedIn = isLoggedIn;
LoginService.signInToAccount = signInToAccount;
LoginService.signOutAccount = signOutAccount;
LoginService.getProfile = getProfile;
LoginService.verifyLoginStatus = () => _verifyLogin(false);
LoginService.getAccountBaseURL = getAccountBaseURL;
}

// public exports
Expand Down
24 changes: 14 additions & 10 deletions src/services/login-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,17 @@
*/

define(function (require, exports, module) {
require("./setup-login-service"); // this adds loginService to KernalModeTrust
require("./promotions");

const KernalModeTrust = window.KernalModeTrust;
if(!KernalModeTrust){
// integrated extensions will have access to kernal mode, but not external extensions
throw new Error("Login service should have access to KernalModeTrust. Cannot boot without trust ring");
}

const LoginService = KernalModeTrust.loginService;

// Event constants
const EVENT_ENTITLEMENTS_CHANGED = "entitlements_changed";

Expand All @@ -43,7 +47,7 @@ define(function (require, exports, module) {
*/
async function getEntitlements(forceRefresh = false) {
// Return null if not logged in
if (!KernalModeTrust.loginService.isLoggedIn()) {
if (!LoginService.isLoggedIn()) {
return null;
}

Expand All @@ -53,7 +57,7 @@ define(function (require, exports, module) {
}

try {
const accountBaseURL = KernalModeTrust.loginService.getAccountBaseURL();
const accountBaseURL = LoginService.getAccountBaseURL();
const language = Phoenix.app && Phoenix.app.language ? Phoenix.app.language : 'en';
let url = `${accountBaseURL}/getAppEntitlements?lang=${language}`;
let fetchOptions = {
Expand All @@ -66,7 +70,7 @@ define(function (require, exports, module) {
// Handle different authentication methods for browser vs desktop
if (Phoenix.isNativeApp) {
// Desktop app: use appSessionID and validationCode
const profile = KernalModeTrust.loginService.getProfile();
const profile = LoginService.getProfile();
if (profile && profile.apiKey && profile.validationCode) {
url += `&appSessionID=${encodeURIComponent(profile.apiKey)}&validationCode=${encodeURIComponent(profile.validationCode)}`;
} else {
Expand All @@ -90,7 +94,7 @@ define(function (require, exports, module) {

// Trigger event if entitlements changed
if (entitlementsChanged) {
KernalModeTrust.loginService.trigger(EVENT_ENTITLEMENTS_CHANGED, result);
LoginService.trigger(EVENT_ENTITLEMENTS_CHANGED, result);
}

return cachedEntitlements;
Expand All @@ -112,14 +116,14 @@ define(function (require, exports, module) {
cachedEntitlements = null;

// Trigger event when entitlements are cleared
if (KernalModeTrust.loginService.trigger) {
KernalModeTrust.loginService.trigger(EVENT_ENTITLEMENTS_CHANGED, null);
if (LoginService.trigger) {
LoginService.trigger(EVENT_ENTITLEMENTS_CHANGED, null);
}
}
}

// Exports
exports.EVENT_ENTITLEMENTS_CHANGED = EVENT_ENTITLEMENTS_CHANGED;
exports.getEntitlements = getEntitlements;
exports.clearEntitlements = clearEntitlements;
// Add functions to secure exports
LoginService.getEntitlements = getEntitlements;
LoginService.clearEntitlements = clearEntitlements;
LoginService.EVENT_ENTITLEMENTS_CHANGED = EVENT_ENTITLEMENTS_CHANGED;
});
Loading
Loading