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
4 changes: 2 additions & 2 deletions serve-proxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ const fs = require('fs');
const httpProxy = require('http-proxy');

// Account server configuration - switch between local and production
//const ACCOUNT_SERVER = 'https://account.phcode.dev'; // Production
const ACCOUNT_SERVER = 'http://localhost:5000'; // Local development
const ACCOUNT_SERVER = 'https://account.phcode.dev'; // Production
// const ACCOUNT_SERVER = 'http://localhost:5000'; // Local development

// Default configuration
let config = {
Expand Down
10 changes: 5 additions & 5 deletions src/assets/new-project/assets/css/responsive.css
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
width: 100%;
}

.editor-name h4 {
.editor-name h4, .editor-name h4 span {
font-size: 22px;
}

Expand Down Expand Up @@ -79,21 +79,21 @@
margin-left: 0px;
width: 48%;
}

.project-content-right {
width: 48%;

}

.website-detail input {
width: 242px;
height: 42px;
}

#noProjectIframe {
width: 267px;
}

}

@media (max-width: 650px) {
Expand Down
31 changes: 31 additions & 0 deletions src/assets/new-project/assets/js/code-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,36 @@ function _openURLInTauri(url) {
}
}

function _updateProBranding() {
try {
const $freeTitle = $('.phoenix-free-title');
const $proTitle = $('.phoenix-pro-title');
const $proTitleSpan = $('.pro-plan-name');

if (!$freeTitle.length || !$proTitle.length || !$proTitleSpan.length) {
console.warn('Pro branding elements not found');
return;
}

// Get plan info from window.top.Phoenix.pro.plan
const planInfo = window.top.Phoenix && window.top.Phoenix.pro && window.top.Phoenix.pro.plan;

if (planInfo && planInfo.paidSubscriber) {
// Hide free title, show pro title
$freeTitle.addClass('forced-hidden');
$proTitle.removeClass('forced-hidden');
// Update plan name
$proTitleSpan.text(planInfo.name || 'Phoenix Pro');
} else {
// Show free title, hide pro title
$freeTitle.removeClass('forced-hidden');
$proTitle.addClass('forced-hidden');
}
} catch (error) {
console.error('Error updating pro branding:', error);
}
}

function initCodeEditor() {
document.getElementById("openFolderBtn").onclick = function() {
Metrics.countEvent(Metrics.EVENT_TYPE.NEW_PROJECT, "main.Click", "open-folder");
Expand Down Expand Up @@ -252,4 +282,5 @@ function initCodeEditor() {
_showFirstTimeExperience();
$("body").append($(`<script async defer src="https://buttons.github.io/buttons.js"></script>`));
_attachSettingBtnEventListeners();
_updateProBranding();
}
19 changes: 18 additions & 1 deletion src/assets/new-project/code-editor.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,22 @@
<script src="../../thirdparty/floating-ui.core.umd.min.js"></script>
<script src="../../thirdparty/floating-ui.dom.umd.min.js"></script>
<script src="assets/js/notification-ui.js"></script>
<style>
.phoenix-pro-title {
background: linear-gradient(
45deg,
#ff8c42, /* deep orange */
#ffa500, /* bright orange */
#ffcc70, /* golden yellow */
#ffd700 /* rich gold */
);
background-clip: text;
-webkit-background-clip: text; /* Chrome, Safari */
color: transparent; /* works in Firefox */
-webkit-text-fill-color: transparent; /* Chrome, Safari */
display: inline-block;
}
</style>
</head>
<body id="top" onload="init(); initCodeEditor();">
<div class="code-editor-card">
Expand All @@ -24,7 +40,8 @@
<img src="images/logo.png" alt="image"/>
</div>
<div class="editor-name">
<h4 class="localize">{{APP_TITLE}}</h4>
<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>
<!-- 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
1 change: 1 addition & 0 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@
baseURL: _getBaseURL(),
isTestWindow: _isTestWindow(),
firstBoot: false, // will be set below
pro: {},
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
1 change: 0 additions & 1 deletion src/nls/root/strings.js
Original file line number Diff line number Diff line change
Expand Up @@ -1606,7 +1606,6 @@ define({
"CONTACT_SUPPORT": "Contact support",
"SIGN_OUT": "Sign out",
"ACCOUNT_DETAILS": "Account Details",
"AI_QUOTA_USED": "AI quota used",
"LOGIN_REFRESH": "Check Login Status",
"SIGN_IN_WAITING_TITLE": "Waiting for Sign In",
"SIGN_IN_WAITING_MESSAGE": "Please complete sign-in in the new tab, then return here.",
Expand Down
12 changes: 7 additions & 5 deletions src/services/html/profile-popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,20 @@
<div class="user-name"><secure-name></secure-name></div>
<div class="user-email"><secure-email></secure-email></div>
<iframe id="user-details-frame" class="user-details-iframe" style="display: none; padding: 0; border: none; background: transparent; width: 100%; height: auto; overflow: hidden;" scrolling="no"></iframe>
<div class="{{planClass}}">{{planName}}</div>
<div class="user-plan-name {{planClass}}">{{planName}}</div>
</div>
</div>
</div>
<div class="popup-body">
<div class="quota-section">
<div class="profile-section html-message forced-hidden">
</div>
<div class="profile-section quota-section forced-hidden">
<div class="quota-header">
<span>{{Strings.AI_QUOTA_USED}}</span>
<span>{{quotaUsed}} / {{quotaTotal}} {{quotaUnit}}</span>
<span class="titleText">{{titleText}}</span>
<span class="usageText">{{usageText}}</span>
</div>
<div class="progress-bar">
<div class="progress-fill" style="width: {{quotaPercent}}%;"></div>
<div class="progress-fill" style="width: {{usedPercent}}%;"></div>
</div>
</div>

Expand Down
3 changes: 3 additions & 0 deletions src/services/login-browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ define(function (require, exports, module) {
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 Down Expand Up @@ -418,6 +419,8 @@ define(function (require, exports, module) {
secureExports.getProfile = getProfile;
secureExports.verifyLoginStatus = () => _verifyBrowserLogin(false);
secureExports.getAccountBaseURL = _getAccountBaseURL;
secureExports.getEntitlements = LoginService.getEntitlements;
secureExports.EVENT_ENTITLEMENTS_CHANGED = LoginService.EVENT_ENTITLEMENTS_CHANGED;
}

// public exports
Expand Down
3 changes: 3 additions & 0 deletions src/services/login-desktop.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ 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 Down Expand Up @@ -417,6 +418,8 @@ define(function (require, exports, module) {
secureExports.getProfile = getProfile;
secureExports.verifyLoginStatus = () => _verifyLogin(false);
secureExports.getAccountBaseURL = getAccountBaseURL;
secureExports.getEntitlements = LoginService.getEntitlements;
secureExports.EVENT_ENTITLEMENTS_CHANGED = LoginService.EVENT_ENTITLEMENTS_CHANGED;
}

// public exports
Expand Down
125 changes: 125 additions & 0 deletions src/services/login-service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* GNU AGPL-3.0 License
*
* Copyright (c) 2021 - present core.ai . All rights reserved.
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://opensource.org/licenses/AGPL-3.0.
*
*/

/**
* Shared Login Service
*
* This module contains shared login service functionality used by both
* browser and desktop login implementations, including entitlements management.
*/

define(function (require, exports, module) {

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");
}

// Event constants
const EVENT_ENTITLEMENTS_CHANGED = "entitlements_changed";

// Cached entitlements data
let cachedEntitlements = null;

/**
* Get entitlements from API or cache
* Returns null if user is not logged in
*/
async function getEntitlements(forceRefresh = false) {
// Return null if not logged in
if (!KernalModeTrust.loginService.isLoggedIn()) {
return null;
}

// Return cached data if available and not forcing refresh
if (cachedEntitlements && !forceRefresh) {
return cachedEntitlements;
}

try {
const accountBaseURL = KernalModeTrust.loginService.getAccountBaseURL();
const language = Phoenix.app && Phoenix.app.language ? Phoenix.app.language : 'en';
let url = `${accountBaseURL}/getAppEntitlements?lang=${language}`;
let fetchOptions = {
method: 'GET',
headers: {
'Accept': 'application/json'
}
};

// Handle different authentication methods for browser vs desktop
if (Phoenix.isNativeApp) {
// Desktop app: use appSessionID and validationCode
const profile = KernalModeTrust.loginService.getProfile();
if (profile && profile.apiKey && profile.validationCode) {
url += `&appSessionID=${encodeURIComponent(profile.apiKey)}&validationCode=${encodeURIComponent(profile.validationCode)}`;
} else {
console.error('Missing appSessionID or validationCode for desktop app entitlements');
return null;
}
} else {
// Browser app: use session cookies
fetchOptions.credentials = 'include';
}

const response = await fetch(url, fetchOptions);

if (response.ok) {
const result = await response.json();
if (result.isSuccess) {
// Check if entitlements actually changed
const entitlementsChanged = JSON.stringify(cachedEntitlements) !== JSON.stringify(result);

cachedEntitlements = result;

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

return cachedEntitlements;
}
}
} catch (error) {
console.error('Failed to fetch entitlements:', error);
}

return null;
}

/**
* Clear cached entitlements and trigger change event
* Called when user logs out
*/
function clearEntitlements() {
if (cachedEntitlements) {
cachedEntitlements = null;

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

// Exports
exports.EVENT_ENTITLEMENTS_CHANGED = EVENT_ENTITLEMENTS_CHANGED;
exports.getEntitlements = getEntitlements;
exports.clearEntitlements = clearEntitlements;
});
Loading
Loading