From d864054e822ca44f145a420a88627821c8302b45 Mon Sep 17 00:00:00 2001
From: abose
Date: Wed, 29 Apr 2026 14:01:21 +0530
Subject: [PATCH 01/19] feat(ai-chat): expand system prompt with explicit live
preview inspection tooling
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Tells Claude that the live preview supports both HTML/CSS/JS/SVG and
Markdown, frames the preview tools as active-iteration debugging
(takeScreenshot, execJsInLivePreview, resizeLivePreview, controlEditor,
getEditorState), and notes that the preview normally follows the active
file — only check livePreviewFile if a screenshot mismatches the recent
edit, to avoid defensive round trips for the rare pinned case.
---
src-node/claude-code-agent.js | 28 ++++++++++++++++++++--------
1 file changed, 20 insertions(+), 8 deletions(-)
diff --git a/src-node/claude-code-agent.js b/src-node/claude-code-agent.js
index 3380e2e778..4bd039ae1b 100644
--- a/src-node/claude-code-agent.js
+++ b/src-node/claude-code-agent.js
@@ -663,15 +663,27 @@ async function _runQuery(requestId, prompt, projectPath, model, signal, locale,
"controlEditor). Never use relative paths." +
"\n\nWhen a tool response mentions the user has typed a clarification, immediately " +
"call getUserClarification to read it and incorporate the user's feedback into your current work." +
- "\n\nYou are running inside Phoenix Code, a web-focused code editor with a built-in " +
- "live preview for HTML/CSS/JS. When the user asks to create mockups, prototypes, " +
- "or web pages, prefer vanilla HTML/CSS/JS so the live preview can render and " +
- "edit them — unless the user specifically requests a framework. " +
+ "\n\nYou are running inside Phoenix Code, a web-focused code editor with built-in " +
+ "live preview for both HTML/CSS/JS/SVG and Markdown. When the user asks to create " +
+ "mockups, prototypes, or web pages, prefer vanilla HTML/CSS/JS so the live preview " +
+ "can render and edit them — unless the user specifically requests a framework. " +
"Build responsive layouts by default for web content." +
- "\n\nWhen planning, consider if verification is needed. takeScreenshot can " +
- "capture the full editor, specific panels, the code area, or the live preview. " +
- "For HTML/CSS/JS with live preview, execJsInLivePreview can run JS in the " +
- "browser to confirm behavior." +
+ "\n\nYou can debug and inspect the live preview directly — these tools are for " +
+ "active iteration, not just final verification:" +
+ "\n- takeScreenshot: see the rendered HTML preview, the rendered Markdown preview, " +
+ "the editor, or any panel. Use it to confirm visual output, diagnose layout/styling " +
+ "bugs, or check that HTML or Markdown rendered as expected." +
+ "\n- execJsInLivePreview: run JS inside the HTML preview iframe to read the DOM, " +
+ "query computed styles, click elements, or capture console output. Use it to debug " +
+ "behavior, not just to verify." +
+ "\n- resizeLivePreview: change the preview viewport width to test responsive " +
+ "breakpoints." +
+ "\n- controlEditor: open files, move the cursor, or change selection from your side." +
+ "\n- getEditorState: report active file, working set, cursor/selection, and the " +
+ "livePreviewFile. The live preview normally follows the active editor file, so " +
+ "assume that. Rarely the user pins the preview to a specific file — if a " +
+ "screenshot doesn't match the file you just edited, check " +
+ "getEditorState.livePreviewFile to rule that out." +
"\n\nUse your best judgement for when to enter plan mode. Use it when the task " +
"involves creating new applications, extensive modifications, or architectural " +
"changes — propose a plan for user approval before writing code." +
From 44cb65ce594c44ab951b04e42f2cef7d4056ddff Mon Sep 17 00:00:00 2001
From: abose
Date: Wed, 29 Apr 2026 16:49:42 +0530
Subject: [PATCH 02/19] feat(ai-chat): add live-preview reload op + screenshot
reload arg
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Expose reload to the AI in two complementary forms:
- controlEditor gains a reloadLivePreview operation that runs
CMD_RELOAD_LIVE_PREVIEW (also broadcasts to popped-out tabs).
- takeScreenshot gains a reload boolean — reloads then captures in one
tool call, saving a round-trip when the AI is about to screenshot
anyway. The controlEditor op description hints at this combined call.
Also adds the AI_CHAT_SURPRISE_ME_USER_MSG string used by the pro
Surprise Me onboarding flow.
---
src-node/claude-code-agent.js | 8 ++++++--
src-node/mcp-editor-tools.js | 14 +++++++++++---
src/nls/root/strings.js | 1 +
3 files changed, 18 insertions(+), 5 deletions(-)
diff --git a/src-node/claude-code-agent.js b/src-node/claude-code-agent.js
index 4bd039ae1b..4a507cf566 100644
--- a/src-node/claude-code-agent.js
+++ b/src-node/claude-code-agent.js
@@ -672,13 +672,17 @@ async function _runQuery(requestId, prompt, projectPath, model, signal, locale,
"active iteration, not just final verification:" +
"\n- takeScreenshot: see the rendered HTML preview, the rendered Markdown preview, " +
"the editor, or any panel. Use it to confirm visual output, diagnose layout/styling " +
- "bugs, or check that HTML or Markdown rendered as expected." +
+ "bugs, or check that HTML or Markdown rendered as expected. Pass reload=true to " +
+ "force-reload the preview before capturing (useful after JS edits) — saves a tool " +
+ "call vs. reloading separately." +
"\n- execJsInLivePreview: run JS inside the HTML preview iframe to read the DOM, " +
"query computed styles, click elements, or capture console output. Use it to debug " +
"behavior, not just to verify." +
"\n- resizeLivePreview: change the preview viewport width to test responsive " +
"breakpoints." +
- "\n- controlEditor: open files, move the cursor, or change selection from your side." +
+ "\n- controlEditor: open files, move the cursor, change selection, toggle the live " +
+ "preview panel, or reload it (reloadLivePreview operation — use after JS edits if " +
+ "you're not also taking a screenshot)." +
"\n- getEditorState: report active file, working set, cursor/selection, and the " +
"livePreviewFile. The live preview normally follows the active editor file, so " +
"assume that. Rarely the user pins the preview to a specific file — if a " +
diff --git a/src-node/mcp-editor-tools.js b/src-node/mcp-editor-tools.js
index 4896a598f1..269eb605ac 100644
--- a/src-node/mcp-editor-tools.js
+++ b/src-node/mcp-editor-tools.js
@@ -128,10 +128,13 @@ function createEditorMcpServer(sdkModule, nodeConnector, clarificationAccessors)
"or '.editor-holder' to capture only the code editor area. " +
"Only omit the selector when you need to see the full editor application layout. " +
"Note: live preview screenshots may include Phoenix toolbox overlays on selected elements. " +
- "Use purePreview=true to temporarily hide these overlays and render the page as it would appear in a real browser.",
+ "Use purePreview=true to temporarily hide these overlays and render the page as it would appear in a real browser. " +
+ "Use reload=true to force-reload the live preview before capturing — useful after editing JS, " +
+ "and saves a tool call vs. calling controlEditor.reloadLivePreview separately.",
{
selector: z.string().optional().describe("CSS selector to capture a specific element. Use '#panel-live-preview-frame' for the preview panel (HTML live preview or markdown preview), '.editor-holder' for the code editor."),
purePreview: z.boolean().optional().describe("When true, temporarily switches to preview mode to hide element highlight overlays and toolboxes before capturing, then restores the previous mode."),
+ reload: z.boolean().optional().describe("When true, force-reloads the live preview before capturing. Use this instead of a separate reloadLivePreview call when you're about to screenshot anyway."),
filePath: z.string().optional().describe("Absolute path to save the screenshot as a PNG file. If specified, returns the file path instead of inline image data.")
},
async function (args) {
@@ -140,6 +143,7 @@ function createEditorMcpServer(sdkModule, nodeConnector, clarificationAccessors)
const result = await _execPeerWithTimeout(nodeConnector, "takeScreenshot", {
selector: args.selector || undefined,
purePreview: args.purePreview || false,
+ reload: args.reload || false,
filePath: args.filePath || undefined
}, "takeScreenshot");
if (result.filePath) {
@@ -212,10 +216,14 @@ function createEditorMcpServer(sdkModule, nodeConnector, clarificationAccessors)
"- openInWorkingSet: Open a file and pin it to the working set. Params: filePath\n" +
"- setSelection: Open a file and select a range. Params: filePath, startLine, startCh, endLine, endCh\n" +
"- setCursorPos: Open a file and set cursor position. Params: filePath, line, ch\n" +
- "- toggleLivePreview: Show or hide the live preview panel. Params: show (boolean)",
+ "- toggleLivePreview: Show or hide the live preview panel. Params: show (boolean)\n" +
+ "- reloadLivePreview: Force-reload the live preview iframe (and any popped-out preview tabs). " +
+ "Use after editing JS that doesn't appear to have hot-reloaded. Note: if you're about to call " +
+ "takeScreenshot anyway, prefer takeScreenshot({ reload: true }) — it reloads and captures in " +
+ "one step. No params.",
{
operations: z.array(z.object({
- operation: z.enum(["open", "close", "openInWorkingSet", "setSelection", "setCursorPos", "toggleLivePreview"]),
+ operation: z.enum(["open", "close", "openInWorkingSet", "setSelection", "setCursorPos", "toggleLivePreview", "reloadLivePreview"]),
filePath: z.string().optional().describe("Absolute path to the file (not required for toggleLivePreview)"),
startLine: z.number().optional().describe("Start line (1-based) for setSelection"),
startCh: z.number().optional().describe("Start column (1-based) for setSelection"),
diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js
index bd448c75b2..d4cae0302d 100644
--- a/src/nls/root/strings.js
+++ b/src/nls/root/strings.js
@@ -2152,6 +2152,7 @@ define({
// AI CHAT PANEL
"AI_CHAT_TITLE_NO_AI": "Phoenix Code AI",
"AI_CHAT_TITLE": "Claude Code",
+ "AI_CHAT_SURPRISE_ME_USER_MSG": "Surprise me!",
"AI_CHAT_NEW_SESSION_TITLE": "Start a new conversation",
"AI_CHAT_NEW_BTN": "New",
"AI_CHAT_THINKING": "Thinking...",
From 1bc7a0edea64f17ca500929123d62a9e6ad631d8 Mon Sep 17 00:00:00 2001
From: abose
Date: Wed, 29 Apr 2026 17:32:49 +0530
Subject: [PATCH 03/19] feat(ai-chat): prefer img tags over div
background-image
Tells the AI to default to real
tags so the user can swap,
inspect, and resize images in the editor, only falling back to
background-image when an effect genuinely requires it.
---
src-node/claude-code-agent.js | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src-node/claude-code-agent.js b/src-node/claude-code-agent.js
index 4a507cf566..05475298c8 100644
--- a/src-node/claude-code-agent.js
+++ b/src-node/claude-code-agent.js
@@ -667,7 +667,10 @@ async function _runQuery(requestId, prompt, projectPath, model, signal, locale,
"live preview for both HTML/CSS/JS/SVG and Markdown. When the user asks to create " +
"mockups, prototypes, or web pages, prefer vanilla HTML/CSS/JS so the live preview " +
"can render and edit them — unless the user specifically requests a framework. " +
- "Build responsive layouts by default for web content." +
+ "Build responsive layouts by default for web content. For images, prefer real " +
+ "
tags over div background-image so the user can swap, inspect, and resize " +
+ "them in the editor — only fall back to background-image when an effect (parallax, " +
+ "cover-with-overlay, repeating tile) genuinely requires it." +
"\n\nYou can debug and inspect the live preview directly — these tools are for " +
"active iteration, not just final verification:" +
"\n- takeScreenshot: see the rendered HTML preview, the rendered Markdown preview, " +
From b94ddacd2a7696315cd9758755fa6e8dcc988aaa Mon Sep 17 00:00:00 2001
From: abose
Date: Wed, 29 Apr 2026 18:07:41 +0530
Subject: [PATCH 04/19] fix(phoenix-tour): expand sidebar before each step
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Upgrade flows can boot Phoenix with the sidebar hidden (restored from a
previous session), which hides the AI tab (step 2) and the new-project
button (step 3) the tour points at. Because both are visibility:hidden
rather than display:none, getBoundingClientRect still returns positive
dimensions, so the existing rect-zero gate in _trackTarget didn't catch
this — the overlay would silently point at invisible UI.
Verified the failure mode and the fix in a connected electron instance:
the AI tab and new-project button switch from visibility:hidden to
visibility:visible after SidebarView.show().
---
.../Phoenix/phoenix-tour.js | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/src/extensionsIntegrated/Phoenix/phoenix-tour.js b/src/extensionsIntegrated/Phoenix/phoenix-tour.js
index 2a0f156960..b61768d704 100644
--- a/src/extensionsIntegrated/Phoenix/phoenix-tour.js
+++ b/src/extensionsIntegrated/Phoenix/phoenix-tour.js
@@ -32,6 +32,7 @@ define(function (require, exports, module) {
const Strings = require("strings"),
StringUtils = require("utils/StringUtils"),
Metrics = require("utils/Metrics"),
+ SidebarView = require("project/SidebarView"),
CentralControlBar = require("view/CentralControlBar");
// Capture the kernel trust ring at module-load time — it's deleted from
@@ -189,7 +190,21 @@ define(function (require, exports, module) {
update();
}
+ /**
+ * Make sure the sidebar is showing before each step. Upgrade flows can
+ * boot Phoenix with the sidebar hidden (the user's last-session state),
+ * which would hide the AI tab and the new-project button this tour
+ * points at. Cheap to call when already visible — SidebarView.show()
+ * is a no-op then.
+ */
+ function _ensureSidebarVisible() {
+ if (SidebarView && SidebarView.isVisible && !SidebarView.isVisible()) {
+ SidebarView.show();
+ }
+ }
+
function _runStep1() {
+ _ensureSidebarVisible();
const $btn = $("#ccbCollapseEditorBtn");
if (!$btn.length) {
_markComplete();
@@ -235,6 +250,7 @@ define(function (require, exports, module) {
}
function _runStep2() {
+ _ensureSidebarVisible();
const $tab = $('.sidebar-tab[data-tab-id="ai"]');
if (!$tab.length) {
// No AI tab in this build — skip ahead to the next step.
@@ -260,6 +276,7 @@ define(function (require, exports, module) {
}
function _runStep3() {
+ _ensureSidebarVisible();
const $newBtn = $("#newProject");
if (!$newBtn.length) {
// No new-project button — tour is effectively done.
From fb96ea0666dc1194fce041992181f525d3052c87 Mon Sep 17 00:00:00 2001
From: abose
Date: Wed, 29 Apr 2026 18:16:00 +0530
Subject: [PATCH 05/19] feat(phoenix-tour): add step 4 introducing live-preview
Edit Mode
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The fourth step points at the LP panel's Edit Mode button. Before it
runs, the tour ensures the welcome project is open, the active editor
shows phoenix-pro.html (preferred) or index.html, and the LP panel is
visible — each branch a no-op when already in that state. Falls back
to completing the tour if the button still can't be located (custom
server, unsupported file).
Step 3's Dismiss button becomes Next, advancing to step 4 where the
new Dismiss lives. CURRENT_TOUR_VERSION stays at 1 — only users who
haven't completed the tour will see the new step.
---
.../Phoenix/phoenix-tour.js | 104 +++++++++++++++++-
src/nls/root/strings.js | 1 +
2 files changed, 99 insertions(+), 6 deletions(-)
diff --git a/src/extensionsIntegrated/Phoenix/phoenix-tour.js b/src/extensionsIntegrated/Phoenix/phoenix-tour.js
index b61768d704..5979be67b1 100644
--- a/src/extensionsIntegrated/Phoenix/phoenix-tour.js
+++ b/src/extensionsIntegrated/Phoenix/phoenix-tour.js
@@ -33,6 +33,11 @@ define(function (require, exports, module) {
StringUtils = require("utils/StringUtils"),
Metrics = require("utils/Metrics"),
SidebarView = require("project/SidebarView"),
+ ProjectManager = require("project/ProjectManager"),
+ EditorManager = require("editor/EditorManager"),
+ CommandManager = require("command/CommandManager"),
+ Commands = require("command/Commands"),
+ WorkspaceManager = require("view/WorkspaceManager"),
CentralControlBar = require("view/CentralControlBar");
// Capture the kernel trust ring at module-load time — it's deleted from
@@ -102,7 +107,7 @@ define(function (require, exports, module) {
}
}
- const TOTAL_STEPS = 3;
+ const TOTAL_STEPS = 4;
function _ensureOverlay() {
if ($overlay) {
@@ -279,9 +284,9 @@ define(function (require, exports, module) {
_ensureSidebarVisible();
const $newBtn = $("#newProject");
if (!$newBtn.length) {
- // No new-project button — tour is effectively done.
- _markComplete();
- _teardown();
+ // No new-project button — skip to the live-preview step instead
+ // of giving up on the tour entirely.
+ _runStep4();
return;
}
_ensureOverlay();
@@ -289,6 +294,95 @@ define(function (require, exports, module) {
_setStep(3);
Metrics.countEvent(Metrics.EVENT_TYPE.GUIDE, "tour", "step3");
_setText(Strings.PHOENIX_TOUR_NEW_PROJECT);
+ _setActions([
+ {
+ label: Strings.PHOENIX_TOUR_NEXT_BTN,
+ kind: "primary",
+ onClick: function () {
+ _runStep4();
+ }
+ }
+ ]);
+ // Intentionally do NOT advance on a real click of the target — only
+ // the Next button advances.
+ }
+
+ /**
+ * Bring the workspace to a state where the live-preview Edit Mode button
+ * is visible and meaningful: the welcome project must be open, the
+ * active editor file must be one the preview can render (the welcome
+ * project's index.html or phoenix-pro.html), and the LP panel must be
+ * showing. Each branch is a no-op when already true.
+ */
+ async function _ensureLivePreviewReady() {
+ const welcomeRoot = ProjectManager.getWelcomeProjectPath();
+
+ // 1. Switch to the welcome project if we're not already there.
+ const currentRoot = ProjectManager.getProjectRoot();
+ if (!currentRoot || currentRoot.fullPath !== welcomeRoot) {
+ await new Promise(function (resolve) {
+ ProjectManager.openProject(welcomeRoot)
+ .done(resolve).fail(resolve);
+ });
+ }
+
+ // 2. Make sure the active file is one the LP can render. Prefer
+ // phoenix-pro.html (Pro flow) and fall back to index.html.
+ const proPath = welcomeRoot + "phoenix-pro.html";
+ const indexPath = welcomeRoot + "index.html";
+ const editor = EditorManager.getActiveEditor();
+ const currentFile = editor && editor.document && editor.document.file
+ ? editor.document.file.fullPath : null;
+ if (currentFile !== proPath && currentFile !== indexPath) {
+ let target = null;
+ try {
+ if (await Phoenix.VFS.existsAsync(proPath)) {
+ target = proPath;
+ } else if (await Phoenix.VFS.existsAsync(indexPath)) {
+ target = indexPath;
+ }
+ } catch (e) { /* fall through with target=null */ }
+ if (target) {
+ await new Promise(function (resolve) {
+ CommandManager.execute(Commands.FILE_OPEN, { fullPath: target })
+ .done(resolve).fail(resolve);
+ });
+ }
+ }
+
+ // 3. Open the live-preview panel if it isn't visible.
+ const lpPanel = WorkspaceManager.getPanelForID("live-preview-panel");
+ if (!lpPanel || !lpPanel.isVisible()) {
+ await new Promise(function (resolve) {
+ CommandManager.execute(Commands.FILE_LIVE_FILE_PREVIEW)
+ .done(resolve).fail(resolve);
+ });
+ }
+ }
+
+ async function _runStep4() {
+ _ensureSidebarVisible();
+ try {
+ await _ensureLivePreviewReady();
+ } catch (e) { /* best-effort prep — proceed and let the rect-zero
+ RAF gate sort out a missing target */ }
+
+ const $btn = $("#previewModeLivePreviewButton");
+ if (!$btn.length) {
+ // LP panel never came up (custom server, unsupported file, etc.)
+ // — finalize the tour rather than stalling on a missing target.
+ _markComplete();
+ _teardown();
+ return;
+ }
+ _ensureOverlay();
+ // LP panel sits on the right edge; keep the default tooltip
+ // placement (lower-right of the ring) so the tooltip extends back
+ // into the panel area rather than off the right edge of the viewport.
+ _trackTarget($btn, "left");
+ _setStep(4);
+ Metrics.countEvent(Metrics.EVENT_TYPE.GUIDE, "tour", "step4");
+ _setText(Strings.PHOENIX_TOUR_EDIT_MODE);
_setActions([
{
label: Strings.PHOENIX_TOUR_DISMISS_BTN,
@@ -300,8 +394,6 @@ define(function (require, exports, module) {
}
}
]);
- // Intentionally do NOT end on a real click of the target — only the
- // Dismiss button ends the tour.
}
function _shouldRun() {
diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js
index d4cae0302d..63a312fde1 100644
--- a/src/nls/root/strings.js
+++ b/src/nls/root/strings.js
@@ -1672,6 +1672,7 @@ define({
"PHOENIX_TOUR_DESIGN_MODE": "Click here to enter Design Mode. Go full-screen and edit your page visually.",
"PHOENIX_TOUR_AI_PANEL": "Design layouts, fix bugs, and build faster with AI. Click here to open the AI panel",
"PHOENIX_TOUR_NEW_PROJECT": "Open or create a new project from here",
+ "PHOENIX_TOUR_EDIT_MODE": "Edit Mode lets you tweak your page directly in the live preview — click elements to inspect and edit them inline.",
"PHOENIX_TOUR_STEP_OF": "{0} of {1}",
"PHOENIX_TOUR_NEXT_BTN": "Next",
"PHOENIX_TOUR_DISMISS_BTN": "Dismiss",
From f34accf7028d86792cb52431333a563dd684fc07 Mon Sep 17 00:00:00 2001
From: abose
Date: Wed, 29 Apr 2026 18:27:50 +0530
Subject: [PATCH 06/19] fix: large brackets min dev build
---
gulpfile.js/validate-build.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/gulpfile.js/validate-build.js b/gulpfile.js/validate-build.js
index 1f7bad73ea..95417a7adf 100644
--- a/gulpfile.js/validate-build.js
+++ b/gulpfile.js/validate-build.js
@@ -29,7 +29,7 @@ const DEV_MAX_TOTAL_SIZE_MB = 100;
// Custom size limits for known large files (size in MB) For development builds
const LARGE_FILE_LIST_DEV = {
'dist/thirdparty/no-minify/language-worker.js.map': 10,
- 'dist/brackets-min.js': 15
+ 'dist/brackets-min.js': 20
};
// Size limits for production/staging builds (in MB)
From faa275152dc7173be1629ae786d7b7e86eb0c75a Mon Sep 17 00:00:00 2001
From: abose
Date: Wed, 29 Apr 2026 20:31:51 +0530
Subject: [PATCH 07/19] feat(ai-chat): redesign browser-build placeholder per
new spec
Replaces the centered icon/title/message layout with a left-aligned
header (Design, fix and build faster), the existing video card,
three info cards (Generate Layout / Fix Bugs / Suggest improvements
with FA icons table-cells-large, bug-slash, wand-sparkles), the
desktop-only body copy, and a full-width CTA.
Strings: adds AI_CHAT_PLACEHOLDER_* keys (title, subtitle, three
card title/desc pairs); reuses AI_CHAT_DESKTOP_ONLY for the body to
avoid duplicating what the cards already say. Drops the now-unused
AI_CHAT_TITLE_NO_AI from the root locale (other locale files will
re-sync via GitHub Actions).
CSS: new .ai-placeholder column styles with min-width:0 and explicit
white-space:normal so text wraps when the sidebar is narrow (the
panel inherits white-space:nowrap from chat-message defaults). The
CTA gets a 14px-padded, 8px-radius treatment that matches the cards
and beats the shared .btn.btn-primary override; the shared override
itself is widened to also re-apply the standard primary look inside
.ai-placeholder.
---
src/nls/root/strings.js | 9 +-
src/styles/Extn-AIChatPanel.less | 156 ++++++++++++++++++++++++++++++-
2 files changed, 162 insertions(+), 3 deletions(-)
diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js
index 63a312fde1..0ac351d533 100644
--- a/src/nls/root/strings.js
+++ b/src/nls/root/strings.js
@@ -2151,7 +2151,6 @@ define({
"AI_UPSELL_DIALOG_MESSAGE": "You’ve discovered {0}. To proceed, you’ll need an AI subscription or credits.",
// AI CHAT PANEL
- "AI_CHAT_TITLE_NO_AI": "Phoenix Code AI",
"AI_CHAT_TITLE": "Claude Code",
"AI_CHAT_SURPRISE_ME_USER_MSG": "Surprise me!",
"AI_CHAT_NEW_SESSION_TITLE": "Start a new conversation",
@@ -2173,6 +2172,14 @@ define({
"AI_CHAT_RETRY": "Retry",
"AI_CHAT_DESKTOP_ONLY": "AI features require the Phoenix desktop app. Download it to get started.",
"AI_CHAT_DOWNLOAD_BTN": "Download Desktop App",
+ "AI_CHAT_PLACEHOLDER_TITLE": "Design, fix and build faster",
+ "AI_CHAT_PLACEHOLDER_SUBTITLE": "AI that works along your preview",
+ "AI_CHAT_PLACEHOLDER_CARD_LAYOUT_TITLE": "Generate Layout",
+ "AI_CHAT_PLACEHOLDER_CARD_LAYOUT_DESC": "Describe a section",
+ "AI_CHAT_PLACEHOLDER_CARD_BUGS_TITLE": "Fix Bugs",
+ "AI_CHAT_PLACEHOLDER_CARD_BUGS_DESC": "Scan for issues",
+ "AI_CHAT_PLACEHOLDER_CARD_IMPROVE_TITLE": "Suggest improvements",
+ "AI_CHAT_PLACEHOLDER_CARD_IMPROVE_DESC": "UX & performance tips",
"AI_CHAT_LOGIN_TITLE": "Sign In to Use AI",
"AI_CHAT_LOGIN_MESSAGE": "Sign in to your {APP_NAME} account to access AI features.",
"AI_CHAT_LOGIN_BTN": "Sign In",
diff --git a/src/styles/Extn-AIChatPanel.less b/src/styles/Extn-AIChatPanel.less
index b1c26c4716..650ffec634 100644
--- a/src/styles/Extn-AIChatPanel.less
+++ b/src/styles/Extn-AIChatPanel.less
@@ -2840,6 +2840,156 @@
}
}
+/* Browser-build placeholder layout (designed by the team — left-aligned
+ header, video, three info cards, body copy, full-width CTA). Used only
+ when the browser app can't run AI features, so the panel doubles as a
+ marketing surface for the desktop app. */
+
+.ai-placeholder {
+ display: flex;
+ flex-direction: column;
+ gap: 1.1rem;
+ padding: 1.25rem 1.25rem 1.5rem;
+ box-sizing: border-box;
+ /* Allow the column to shrink below its intrinsic content size when the
+ sidebar is narrow, otherwise long words like "improvements" force a
+ horizontal scroll. min-width:0 alone isn't enough on a flex column —
+ the inner cards' default min-width:auto prevents shrink. */
+ min-width: 0;
+ width: 100%;
+
+ /* The chat panel sets `white-space: nowrap` higher up so streaming
+ chat lines don't reflow mid-token. Reset it here so all text in the
+ placeholder wraps normally when the sidebar is narrow. */
+ &, * {
+ white-space: normal;
+ }
+}
+
+.ai-placeholder-header {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+
+.ai-placeholder-title {
+ font-size: 22px;
+ font-weight: 700;
+ line-height: 1.2;
+ margin: 0;
+ color: @project-panel-text-1;
+ letter-spacing: -0.01em;
+}
+
+.ai-placeholder-subtitle {
+ font-size: 13px;
+ color: @project-panel-text-2;
+ opacity: 0.85;
+ line-height: 1.4;
+}
+
+/* Video slot in the placeholder: full-width, no max cap (the surrounding
+ `.ai-placeholder` panel padding controls effective width). The shared
+ `.ai-intro-video-slot` rule above is centered and capped at 320px,
+ which would look orphaned in the new left-aligned layout. */
+.ai-placeholder .ai-intro-video-slot {
+ max-width: none;
+ margin: 0;
+ text-align: left;
+ gap: 0;
+}
+
+.ai-placeholder-cards {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.ai-placeholder-card {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 10px 12px;
+ border: 1px solid rgba(255, 255, 255, 0.06);
+ border-radius: 8px;
+ background: rgba(255, 255, 255, 0.025);
+ /* Allow the card to shrink at narrow sidebar widths instead of forcing
+ a horizontal scroll. */
+ min-width: 0;
+}
+
+.ai-placeholder-card-icon {
+ flex: 0 0 auto;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 32px;
+ height: 32px;
+ border-radius: 6px;
+ background: rgba(255, 255, 255, 0.04);
+ color: @project-panel-text-1;
+ font-size: 14px;
+}
+
+.ai-placeholder-card-text {
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+ min-width: 0;
+ flex: 1 1 auto;
+}
+
+.ai-placeholder-card-title,
+.ai-placeholder-card-desc {
+ /* Wrap long localized strings instead of overflowing the card. */
+ overflow-wrap: anywhere;
+ word-break: break-word;
+}
+
+.ai-placeholder-card-title {
+ font-size: 13.5px;
+ font-weight: 600;
+ color: @project-panel-text-1;
+ line-height: 1.25;
+}
+
+.ai-placeholder-card-desc {
+ font-size: 12px;
+ color: @project-panel-text-2;
+ opacity: 0.8;
+ line-height: 1.3;
+}
+
+.ai-placeholder-body {
+ font-size: 12.5px;
+ color: @project-panel-text-2;
+ opacity: 0.75;
+ line-height: 1.5;
+ margin: 4px 0 0;
+}
+
+/* The shared `.btn.btn-primary` override above sets padding: 6px 18px
+ for the older Sign-In CTA. `!important` here locks in the designer's
+ taller spec for this placeholder CTA without having to thread a more
+ specific selector through every inherited declaration. */
+.ai-chat-panel .ai-placeholder .ai-placeholder-cta {
+ width: 100%;
+ padding: 14px 18px !important;
+ font-size: 14px;
+ font-weight: 600;
+ line-height: 1.3;
+ /* Match the 8px corner radius of the cards above so the column reads
+ as one coherent stack. The shared .btn.btn-primary rule sets 4px,
+ hence !important. */
+ border-radius: 8px !important;
+ /* Reset horizontal margins — a higher-up `.btn` rule adds
+ `margin-left: 3px` which would shift the CTA right of the cards. */
+ margin: 4px 0 0;
+}
+
/* Re-apply Phoenix's standard `.btn .btn-primary` look inside the
.ai-unavailable intro states. The blanket reset below (`.ai-chat-panel
.btn-primary`) blanks bootstrap's button styling so chat-area buttons
@@ -2847,7 +2997,8 @@
button so the Sign In CTA reads as the standard Phoenix action. The
selector is more specific than the reset, and `!important` belt-and-
braces against any later reset rule that might still apply. */
-.ai-chat-panel .ai-unavailable .btn.btn-primary {
+.ai-chat-panel .ai-unavailable .btn.btn-primary,
+.ai-chat-panel .ai-placeholder .btn.btn-primary {
display: inline-block;
background-color: @bc-primary-btn-bg !important;
background-image: none !important;
@@ -2876,7 +3027,8 @@
}
}
-.dark .ai-chat-panel .ai-unavailable .btn.btn-primary {
+.dark .ai-chat-panel .ai-unavailable .btn.btn-primary,
+.dark .ai-chat-panel .ai-placeholder .btn.btn-primary {
background-color: @dark-bc-primary-btn-bg !important;
border-color: darken(@dark-bc-primary-btn-bg, 8%) !important;
From 8f302a587dc6e8106bb46f6e8ed9917e142538c0 Mon Sep 17 00:00:00 2001
From: abose
Date: Wed, 29 Apr 2026 20:35:00 +0530
Subject: [PATCH 08/19] chore: update pro deps
---
tracking-repos.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tracking-repos.json b/tracking-repos.json
index 64b439c59e..9043633945 100644
--- a/tracking-repos.json
+++ b/tracking-repos.json
@@ -1,5 +1,5 @@
{
"phoenixPro": {
- "commitID": "85b05bfa13b79712d6854be27d2b02b9d1fa065f"
+ "commitID": "9ebac475c9df434d92584e9ad6eceb9bccb7d0f6"
}
}
From 674cbe7dd247d7c7352d76fe8c5241183601f337 Mon Sep 17 00:00:00 2001
From: abose
Date: Wed, 29 Apr 2026 20:51:00 +0530
Subject: [PATCH 09/19] refactor(phoenix-tour): drop trial-dialog wait timeout
proTrialStartDialogDismissed is guaranteed to resolve on every boot
path (including builds where the dialog isn't shown), so the 60s
fallback was dead code. Just await the deferred.
---
.../Phoenix/phoenix-tour.js | 46 +++++--------------
1 file changed, 11 insertions(+), 35 deletions(-)
diff --git a/src/extensionsIntegrated/Phoenix/phoenix-tour.js b/src/extensionsIntegrated/Phoenix/phoenix-tour.js
index 5979be67b1..7eee1aadd8 100644
--- a/src/extensionsIntegrated/Phoenix/phoenix-tour.js
+++ b/src/extensionsIntegrated/Phoenix/phoenix-tour.js
@@ -52,13 +52,6 @@ define(function (require, exports, module) {
const STEP_START_DELAY_MS = 2500;
const STEP1_INVITE_MS = 1800;
const STEP1_DESIGN_MODE_HOLD_MS = 2000;
- // Hard cap on how long we'll wait for the pro trial start dialog to be
- // dismissed before starting the tour. The dialog is shown on every fresh
- // first-run boot (where this tour also runs), so under normal conditions
- // the wait is bounded by the user dismissing it. The cap protects edge
- // cases where the dialog isn't shown at all (e.g. user already has a
- // subscription / a prior expired trial).
- const TRIAL_DIALOG_WAIT_TIMEOUT_MS = 60000;
function _loadState() {
const raw = PhStore.getItem(TOUR_STORAGE_KEY);
@@ -417,36 +410,19 @@ define(function (require, exports, module) {
}
/**
- * Resolves once the pro trial start dialog has been dismissed, or after
- * TRIAL_DIALOG_WAIT_TIMEOUT_MS as a fallback for builds/runs where the
- * dialog isn't shown.
+ * Resolves once the pro trial start dialog has been dismissed. The
+ * dialog is guaranteed to fire `proTrialStartDialogDismissed` on every
+ * boot path (including builds where the dialog isn't shown), so we
+ * just await it without a timeout fallback.
*/
function _waitForTrialStartDialogDismissed() {
- return new Promise(function (resolve) {
- const dismissed = _LoginService && _LoginService.proTrialStartDialogDismissed;
- if (!dismissed) {
- // No pro trial flow exposed — proceed immediately.
- resolve();
- return;
- }
- let settled = false;
- const fallback = setTimeout(function () {
- if (settled) {
- return;
- }
- settled = true;
- resolve();
- }, TRIAL_DIALOG_WAIT_TIMEOUT_MS);
- // jQuery deferred or native promise — both implement .then
- Promise.resolve(dismissed).then(function () {
- if (settled) {
- return;
- }
- settled = true;
- clearTimeout(fallback);
- resolve();
- });
- });
+ const dismissed = _LoginService && _LoginService.proTrialStartDialogDismissed;
+ // Community-edition builds expose no login service at all — skip
+ // the wait so the tour still works there.
+ if (!dismissed) {
+ return Promise.resolve();
+ }
+ return Promise.resolve(dismissed);
}
function startTour() {
From 5fb6d61269272574182a209aeb83224aa1ba8c75 Mon Sep 17 00:00:00 2001
From: abose
Date: Wed, 29 Apr 2026 21:11:28 +0530
Subject: [PATCH 10/19] feat(pro-dialogs): slide-over animation between
carousel slides
Replaces the cross-fade-only transition between feature carousel slides
with a slide-and-fade. Each slide lives at one of three transform
positions (translateX(100%) default, 0 when active, -100% when .is-left)
so the outgoing slide can animate left while the incoming one comes in
from the right (and vice versa for backward navigation). The .is-active
rule is defined after .is-left so a slide flipping from parked-left to
active animates directly from -100% to 0, no intermediate jump.
Same treatment is applied to .feature-info-slide so the text block
below the video animates in lockstep with the stage.
---
src/styles/phoenix-pro.less | 27 +++++++++++++++++++++++++--
1 file changed, 25 insertions(+), 2 deletions(-)
diff --git a/src/styles/phoenix-pro.less b/src/styles/phoenix-pro.less
index 4bc90dcf68..b29661f786 100644
--- a/src/styles/phoenix-pro.less
+++ b/src/styles/phoenix-pro.less
@@ -85,17 +85,31 @@
background: #1b1b1b;
}
+ /* Slide-over animation: every slide is positioned absolute and lives
+ at one of three transforms — off-screen right (default), active (0),
+ or off-screen left (.is-left). JS toggles is-active / is-left so the
+ transitioning slides cross-fade and translate at the same time, in
+ the direction matching the user's nav. */
.feature-slide {
position: absolute;
inset: 0;
opacity: 0;
pointer-events: none;
- transition: opacity 0.35s ease;
+ transform: translateX(100%);
+ transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.35s ease;
+ will-change: transform, opacity;
}
+ .feature-slide.is-left {
+ transform: translateX(-100%);
+ }
+
+ /* Defined after .is-left so a slide flipping from is-left to is-active
+ animates straight from -100% to 0, no intermediate jump to +100%. */
.feature-slide.is-active {
opacity: 1;
pointer-events: auto;
+ transform: translateX(0);
}
.feature-thumb {
@@ -228,21 +242,30 @@
}
}
+ /* Mirror the .feature-slide transform/cross-fade so the text below the
+ video animates in the same direction as the video stage. */
.feature-info-slide {
position: absolute;
inset: 0;
padding: 10px 28px;
opacity: 0;
pointer-events: none;
- transition: opacity 0.35s ease;
+ transform: translateX(100%);
+ transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.35s ease;
+ will-change: transform, opacity;
font-feature-settings: "kern" 1, "liga" 1, "calt" 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
+ .feature-info-slide.is-left {
+ transform: translateX(-100%);
+ }
+
.feature-info-slide.is-active {
opacity: 1;
pointer-events: auto;
+ transform: translateX(0);
}
.feature-info-slide h2 {
From b2d035d7bfe4fcbf0b9a5394400efd0523e20d92 Mon Sep 17 00:00:00 2001
From: abose
Date: Wed, 29 Apr 2026 21:15:53 +0530
Subject: [PATCH 11/19] chore: update about box
---
src/htmlContent/about-dialog.html | 1 +
tracking-repos.json | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/htmlContent/about-dialog.html b/src/htmlContent/about-dialog.html
index 021c3ade52..2a98fd61a4 100644
--- a/src/htmlContent/about-dialog.html
+++ b/src/htmlContent/about-dialog.html
@@ -19,6 +19,7 @@ {{APP_NAME_ABOUT_BOX}}
Arun Bose,
Charly P Abraham,
Devansh Agarwal,
+ Ansu Ann Koshy,
Krrish Parmar
diff --git a/tracking-repos.json b/tracking-repos.json
index 9043633945..c47905b399 100644
--- a/tracking-repos.json
+++ b/tracking-repos.json
@@ -1,5 +1,5 @@
{
"phoenixPro": {
- "commitID": "9ebac475c9df434d92584e9ad6eceb9bccb7d0f6"
+ "commitID": "6a33b8b613ca09335a24a96e370262f11824e4b0"
}
}
From 236f8b3f19c4731c641217c9c89e4bfc45431555 Mon Sep 17 00:00:00 2001
From: abose
Date: Wed, 29 Apr 2026 22:28:50 +0530
Subject: [PATCH 12/19] chore: rephrase strings for pro upgrade dialog
---
src/nls/root/strings.js | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js
index 0ac351d533..ab12c51617 100644
--- a/src/nls/root/strings.js
+++ b/src/nls/root/strings.js
@@ -2084,12 +2084,12 @@ define({
"PROMO_UPGRADE_MESSAGE": "Enjoy free access to these premium features for the next {0} days:",
"PROMO_CARD_1": "Edit In Live Preview",
"PROMO_CARD_1_MESSAGE": "Edit text, update images, change links, drag elements, and more. Your code updates as you go.",
- "PROMO_CARD_2": "AI in your editor",
- "PROMO_CARD_2_MESSAGE": "Ask the AI to write code, fix bugs, or refactor your project. It does the work for you.",
- "PROMO_CARD_3": "Preview Any Device",
- "PROMO_CARD_3_MESSAGE": "Resize the live preview to any phone, tablet, or custom viewport - catch responsive issues before they ship.",
- "PROMO_CARD_4": "Rich Markdown, Live",
- "PROMO_CARD_4_MESSAGE": "A polished side-by-side markdown preview with diagrams, tables, and code blocks - rendered as you type.",
+ "PROMO_CARD_2": "AI that sees your UI",
+ "PROMO_CARD_2_MESSAGE": "Let AI fix layout issues, tweak styles, and edit your page based on what you see — so you can focus on the creative parts.",
+ "PROMO_CARD_3": "See your site on any device",
+ "PROMO_CARD_3_MESSAGE": "Switch instantly between phone, tablet, and desktop views to catch responsive issues early.",
+ "PROMO_CARD_4": "Your Markdown, as a real document",
+ "PROMO_CARD_4_MESSAGE": "Edit tables, images, and formatting visually — an easier way to work with Markdown.",
"PROMO_LEARN_MORE": "Learn More\u2026",
"PROMO_OPT_OUT_LINK": "Opt out?",
"PROMO_OPT_OUT_NOTE": "You can cancel your trial anytime by selecting `Help > Cancel Phoenix Pro Trial`.",
From a8daccd63ce46d319dabfc53e9b7f6af0c36294b Mon Sep 17 00:00:00 2001
From: abose
Date: Wed, 29 Apr 2026 22:51:04 +0530
Subject: [PATCH 13/19] feat(pro-dialogs): refine PROMO_CARD_2 / PROMO_CARD_3
copy
PROMO_CARD_2 reframes the AI value-prop as 'sees what you see' rather
than 'sees your UI', emphasizing exploration/build/fix tasks. PROMO_CARD_3
swaps 'site' for 'page' to match the term used elsewhere in the dialog.
---
src/nls/root/strings.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js
index ab12c51617..02f657cd2f 100644
--- a/src/nls/root/strings.js
+++ b/src/nls/root/strings.js
@@ -2084,9 +2084,9 @@ define({
"PROMO_UPGRADE_MESSAGE": "Enjoy free access to these premium features for the next {0} days:",
"PROMO_CARD_1": "Edit In Live Preview",
"PROMO_CARD_1_MESSAGE": "Edit text, update images, change links, drag elements, and more. Your code updates as you go.",
- "PROMO_CARD_2": "AI that sees your UI",
- "PROMO_CARD_2_MESSAGE": "Let AI fix layout issues, tweak styles, and edit your page based on what you see — so you can focus on the creative parts.",
- "PROMO_CARD_3": "See your site on any device",
+ "PROMO_CARD_2": "AI that sees what you see",
+ "PROMO_CARD_2_MESSAGE": "Explore UI ideas, build apps, and fix issues with AI — it sees your page the same way you do, not just code.",
+ "PROMO_CARD_3": "See your page on any device",
"PROMO_CARD_3_MESSAGE": "Switch instantly between phone, tablet, and desktop views to catch responsive issues early.",
"PROMO_CARD_4": "Your Markdown, as a real document",
"PROMO_CARD_4_MESSAGE": "Edit tables, images, and formatting visually — an easier way to work with Markdown.",
From a1ffabd0203f40b308187bdfa265f52b587032fd Mon Sep 17 00:00:00 2001
From: abose
Date: Wed, 29 Apr 2026 23:24:23 +0530
Subject: [PATCH 14/19] feat(ai-chat): rephrase login title to action-led copy
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
AI_CHAT_LOGIN_TITLE: 'Sign In to Use AI' → 'Sign in to get started with AI'.
Used by the new login UI which reuses the placeholder card layout, so
the title pairs with the placeholder header to read as an inviting
call-to-action rather than a gating message.
---
src/nls/root/strings.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js
index 02f657cd2f..6b118bab27 100644
--- a/src/nls/root/strings.js
+++ b/src/nls/root/strings.js
@@ -2180,7 +2180,7 @@ define({
"AI_CHAT_PLACEHOLDER_CARD_BUGS_DESC": "Scan for issues",
"AI_CHAT_PLACEHOLDER_CARD_IMPROVE_TITLE": "Suggest improvements",
"AI_CHAT_PLACEHOLDER_CARD_IMPROVE_DESC": "UX & performance tips",
- "AI_CHAT_LOGIN_TITLE": "Sign In to Use AI",
+ "AI_CHAT_LOGIN_TITLE": "Sign in to get started with AI",
"AI_CHAT_LOGIN_MESSAGE": "Sign in to your {APP_NAME} account to access AI features.",
"AI_CHAT_LOGIN_BTN": "Sign In",
"AI_CHAT_INTRO_GUEST_TITLE": "Design layouts, fix bugs, and build faster with AI",
From cb8bd594a74d0152e0f4e2495be397eb0ff8b550 Mon Sep 17 00:00:00 2001
From: abose
Date: Wed, 29 Apr 2026 23:25:08 +0530
Subject: [PATCH 15/19] chore: update pro deps
---
tracking-repos.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tracking-repos.json b/tracking-repos.json
index c47905b399..b03dfb5576 100644
--- a/tracking-repos.json
+++ b/tracking-repos.json
@@ -1,5 +1,5 @@
{
"phoenixPro": {
- "commitID": "6a33b8b613ca09335a24a96e370262f11824e4b0"
+ "commitID": "5da333cf26e33351b7d31376b016a3d990cdc340"
}
}
From 54ace7110135922550722ff3122061df13f50153 Mon Sep 17 00:00:00 2001
From: abose
Date: Wed, 29 Apr 2026 23:33:11 +0530
Subject: [PATCH 16/19] chore: update phrasing
---
src/nls/root/strings.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js
index 6b118bab27..804bf732c8 100644
--- a/src/nls/root/strings.js
+++ b/src/nls/root/strings.js
@@ -2172,7 +2172,7 @@ define({
"AI_CHAT_RETRY": "Retry",
"AI_CHAT_DESKTOP_ONLY": "AI features require the Phoenix desktop app. Download it to get started.",
"AI_CHAT_DOWNLOAD_BTN": "Download Desktop App",
- "AI_CHAT_PLACEHOLDER_TITLE": "Design, fix and build faster",
+ "AI_CHAT_PLACEHOLDER_TITLE": "Design, build, and fix faster with AI",
"AI_CHAT_PLACEHOLDER_SUBTITLE": "AI that works along your preview",
"AI_CHAT_PLACEHOLDER_CARD_LAYOUT_TITLE": "Generate Layout",
"AI_CHAT_PLACEHOLDER_CARD_LAYOUT_DESC": "Describe a section",
From 2fccbc573c98c315a92589c0bb5179800ac00bf1 Mon Sep 17 00:00:00 2001
From: abose
Date: Wed, 29 Apr 2026 23:36:39 +0530
Subject: [PATCH 17/19] chore: update phrasing
---
src/nls/root/strings.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js
index 804bf732c8..bfaa2d7a31 100644
--- a/src/nls/root/strings.js
+++ b/src/nls/root/strings.js
@@ -2170,10 +2170,10 @@ define({
"AI_CHAT_CLAUDE_LOGIN_BTN": "Setup Claude Code",
"AI_CHAT_ADD_PROVIDER_BTN": "Add Custom Provider",
"AI_CHAT_RETRY": "Retry",
- "AI_CHAT_DESKTOP_ONLY": "AI features require the Phoenix desktop app. Download it to get started.",
+ "AI_CHAT_DESKTOP_ONLY": "AI features require the {APP_NAME} desktop app. Download it to get started.",
"AI_CHAT_DOWNLOAD_BTN": "Download Desktop App",
"AI_CHAT_PLACEHOLDER_TITLE": "Design, build, and fix faster with AI",
- "AI_CHAT_PLACEHOLDER_SUBTITLE": "AI that works along your preview",
+ "AI_CHAT_PLACEHOLDER_SUBTITLE": "AI that works with your preview",
"AI_CHAT_PLACEHOLDER_CARD_LAYOUT_TITLE": "Generate Layout",
"AI_CHAT_PLACEHOLDER_CARD_LAYOUT_DESC": "Describe a section",
"AI_CHAT_PLACEHOLDER_CARD_BUGS_TITLE": "Fix Bugs",
From 1e1fe7d808981b9d0eead64b7cae4746d6dc8388 Mon Sep 17 00:00:00 2001
From: abose
Date: Thu, 30 Apr 2026 00:18:59 +0530
Subject: [PATCH 18/19] feat(ai-chat): redesign install + setup screens to
match login layout
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The CLI-not-installed and Claude-needs-setup screens now share the
.ai-placeholder layout with the login/placeholder screens — left-
aligned header, intro video, body copy, full-width CTA, footer help
link — for one consistent visual language across the four 'AI gated'
surfaces.
Per designer spec, the primary CTAs on these setup-on-dark screens
flip from blue to a white pill (kept blue on the login/placeholder
to keep the desktop-app upsell action distinct). Selector chains
.btn.btn-primary.ai-placeholder-cta-on-dark to beat the dark-mode
.dark .btn.btn-primary !important rule that would otherwise force
blue.
Adds a .ai-placeholder-cta-secondary outlined style for the
'Add Custom Provider' option on the setup screen, plus a centered
'Need Help? Learn More' footer link.
Strings: drops the inline learn-more anchors in CLI_INSTALL_MSG /
CLAUDE_LOGIN_MSG (footer link replaces them), updates
INTRO_CONFIGURE_DESC to match the new subtitle copy, adds
SETUP_NEED_HELP and SETUP_LEARN_MORE.
---
src/nls/root/strings.js | 8 ++--
src/styles/Extn-AIChatPanel.less | 65 ++++++++++++++++++++++++++++++++
2 files changed, 70 insertions(+), 3 deletions(-)
diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js
index bfaa2d7a31..86ae96e612 100644
--- a/src/nls/root/strings.js
+++ b/src/nls/root/strings.js
@@ -2160,15 +2160,17 @@ define({
"AI_CHAT_SEND_TITLE": "Send message",
"AI_CHAT_STOP_TITLE": "Stop generation (Esc)",
"AI_CHAT_CLI_NOT_FOUND": "Claude Code Not Installed",
- "AI_CHAT_CLI_INSTALL_MSG": "Claude Code CLI must be installed on your system to use AI features in {APP_NAME}. Learn more",
+ "AI_CHAT_CLI_INSTALL_MSG": "Claude Code CLI must be installed on your system to use AI features in {APP_NAME}.",
"AI_CHAT_CLI_INSTALL_BTN": "Install Claude Code",
"AI_CHAT_CLI_INSTALLING": "Installing…",
"AI_CHAT_CLI_INSTALLING_MSG": "Installing Claude Code, please wait. This may take a while...",
"AI_CHAT_CLI_RESTART_NOTE": "Restart {APP_NAME} after installation completes.",
"AI_CHAT_CLAUDE_LOGIN_TITLE": "Setup Claude Code",
- "AI_CHAT_CLAUDE_LOGIN_MSG": "Claude Code is installed but needs to be configured. Learn more",
+ "AI_CHAT_CLAUDE_LOGIN_MSG": "Claude Code is installed but it needs configuration.",
"AI_CHAT_CLAUDE_LOGIN_BTN": "Setup Claude Code",
"AI_CHAT_ADD_PROVIDER_BTN": "Add Custom Provider",
+ "AI_CHAT_SETUP_NEED_HELP": "Need Help?",
+ "AI_CHAT_SETUP_LEARN_MORE": "Learn More",
"AI_CHAT_RETRY": "Retry",
"AI_CHAT_DESKTOP_ONLY": "AI features require the {APP_NAME} desktop app. Download it to get started.",
"AI_CHAT_DOWNLOAD_BTN": "Download Desktop App",
@@ -2185,7 +2187,7 @@ define({
"AI_CHAT_LOGIN_BTN": "Sign In",
"AI_CHAT_INTRO_GUEST_TITLE": "Design layouts, fix bugs, and build faster with AI",
"AI_CHAT_INTRO_CONFIGURE_TITLE": "Getting started with Claude Code",
- "AI_CHAT_INTRO_CONFIGURE_DESC": "See this short video on how to set up AI",
+ "AI_CHAT_INTRO_CONFIGURE_DESC": "Watch this short video to set up AI in {APP_NAME}",
"AI_CHAT_UPSELL_TITLE": "Phoenix Pro + AI",
"AI_CHAT_UPSELL_MESSAGE": "AI features are available with Phoenix Pro.",
"AI_CHAT_UPSELL_BTN": "Get Phoenix Pro",
diff --git a/src/styles/Extn-AIChatPanel.less b/src/styles/Extn-AIChatPanel.less
index 650ffec634..af7b30112f 100644
--- a/src/styles/Extn-AIChatPanel.less
+++ b/src/styles/Extn-AIChatPanel.less
@@ -2990,6 +2990,71 @@
margin: 4px 0 0;
}
+/* Setup variant of the primary CTA: white pill on the dark panel, used
+ on the "Install Claude Code" / "Setup Claude Code" screens. The
+ placeholder/login CTA stays blue — only these setup-on-dark surfaces
+ flip to white per the designer spec. The selector chains both .btn
+ and .btn-primary classes (specificity 0,6,0) to beat the
+ `.dark .ai-chat-panel .ai-placeholder .btn.btn-primary` rule that
+ would otherwise force blue in the default dark theme. */
+.ai-chat-panel .ai-placeholder .btn.btn-primary.ai-placeholder-cta.ai-placeholder-cta-on-dark {
+ background-color: #ffffff !important;
+ background-image: none !important;
+ color: #1a1a1a !important;
+ border: 1px solid rgba(0, 0, 0, 0.08) !important;
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25) !important;
+
+ &:hover {
+ background-color: #f4f4f4 !important;
+ }
+
+ &:active {
+ background-color: #e8e8e8 !important;
+ }
+}
+
+/* Outlined / muted secondary CTA — sits directly under the white
+ primary on the setup screen for the "Add Custom Provider" alternative. */
+.ai-chat-panel .ai-placeholder .ai-placeholder-cta-secondary {
+ width: 100%;
+ padding: 12px 18px;
+ font-size: 13.5px;
+ font-weight: 500;
+ line-height: 1.3;
+ border-radius: 8px;
+ margin: 8px 0 0;
+ background: transparent;
+ border: 1px solid rgba(255, 255, 255, 0.12);
+ color: @project-panel-text-1;
+ cursor: pointer;
+ transition: background-color 0.15s ease, border-color 0.15s ease;
+
+ &:hover {
+ background-color: rgba(255, 255, 255, 0.04);
+ border-color: rgba(255, 255, 255, 0.2);
+ }
+}
+
+/* Centered "Need Help? Learn More" footer below the setup CTAs. */
+.ai-chat-panel .ai-placeholder-help {
+ margin-top: 14px;
+ text-align: center;
+ font-size: 12.5px;
+ color: @project-panel-text-2;
+ opacity: 0.75;
+}
+
+.ai-chat-panel .ai-placeholder-help .ai-learn-more-link {
+ color: @bc-primary-btn-bg;
+ text-decoration: none;
+ margin-left: 4px;
+ cursor: pointer;
+
+ &:hover {
+ text-decoration: underline;
+ }
+}
+
/* Re-apply Phoenix's standard `.btn .btn-primary` look inside the
.ai-unavailable intro states. The blanket reset below (`.ai-chat-panel
.btn-primary`) blanks bootstrap's button styling so chat-area buttons
From 2a02dcae17dd71afa2b83e8d9ea7bca2e7b445e3 Mon Sep 17 00:00:00 2001
From: abose
Date: Thu, 30 Apr 2026 00:21:23 +0530
Subject: [PATCH 19/19] chore: update pro deps
---
tracking-repos.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tracking-repos.json b/tracking-repos.json
index b03dfb5576..ef2c9b6463 100644
--- a/tracking-repos.json
+++ b/tracking-repos.json
@@ -1,5 +1,5 @@
{
"phoenixPro": {
- "commitID": "5da333cf26e33351b7d31376b016a3d990cdc340"
+ "commitID": "f1efe02545e90721e29b7c3acd409c5e7cce8df1"
}
}