From 0e7a1ca1ab46c03ae943849d9b4bd45e4c6c19f3 Mon Sep 17 00:00:00 2001 From: abose Date: Mon, 27 Apr 2026 21:53:01 +0530 Subject: [PATCH 1/3] feat(debug): add Debug Overrides dialog under Diagnostic Tools MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dev-only command (debug.debugOverrides) that surfaces local-service override toggles without rebuilding. Values are persisted as a single JSON blob in localStorage under LOCAL_OVERIDES_FOR_PHOIENXI_DEBUG so any consumer can read flags with one getItem. First flag: AI_PANEL_LOCAL_OVERRIDE — when set, AIChatPanel switches the onboarding iframe from production to localhost:5555. Dialog shows one row per toggle with a checkbox, label, and a small fa-circle-info icon whose `title` carries the full explanation, so extra overrides can be added later without growing the dialog body. --- src/brackets.js | 1 + src/extensions/default/DebugCommands/main.js | 1 + .../debug-overrides-dialog.html | 20 ++++ src/phoenix-builder/debug-overrides.js | 97 +++++++++++++++++++ 4 files changed, 119 insertions(+) create mode 100644 src/phoenix-builder/debug-overrides-dialog.html create mode 100644 src/phoenix-builder/debug-overrides.js diff --git a/src/brackets.js b/src/brackets.js index 4c53bb9c83..3e427c081a 100644 --- a/src/brackets.js +++ b/src/brackets.js @@ -143,6 +143,7 @@ define(function (require, exports, module) { require("thirdparty/tinycolor"); require("utils/LocalizationUtils"); require("phoenix-builder/main"); + require("phoenix-builder/debug-overrides"); // DEPRECATED: In future we want to remove the global CodeMirror, but for now we // expose our required CodeMirror globally so as to avoid breaking extensions in the diff --git a/src/extensions/default/DebugCommands/main.js b/src/extensions/default/DebugCommands/main.js index 58d792abd8..61ea8d9349 100644 --- a/src/extensions/default/DebugCommands/main.js +++ b/src/extensions/default/DebugCommands/main.js @@ -830,6 +830,7 @@ define(function (require, exports, module) { diagnosticsSubmenu.addMenuItem(DEBUG_BUILD_TESTS); if (AppConfig.config.environment === "dev") { diagnosticsSubmenu.addMenuItem("debug.phoenixBuilderConnect"); + diagnosticsSubmenu.addMenuItem("debug.debugOverrides"); } diagnosticsSubmenu.addMenuDivider(); diagnosticsSubmenu.addMenuItem(DEBUG_ENABLE_LOGGING); diff --git a/src/phoenix-builder/debug-overrides-dialog.html b/src/phoenix-builder/debug-overrides-dialog.html new file mode 100644 index 0000000000..09705079d7 --- /dev/null +++ b/src/phoenix-builder/debug-overrides-dialog.html @@ -0,0 +1,20 @@ + diff --git a/src/phoenix-builder/debug-overrides.js b/src/phoenix-builder/debug-overrides.js new file mode 100644 index 0000000000..d4c98a5a4a --- /dev/null +++ b/src/phoenix-builder/debug-overrides.js @@ -0,0 +1,97 @@ +/* + * 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. + * + */ + +/*globals AppConfig*/ + +/** + * Debug → Diagnostic Tools → Debug Overrides + * + * A small dev-only dialog for toggling local-service overrides + * without rebuilding. Values are persisted as a single JSON blob in + * localStorage under LOCAL_OVERIDES_FOR_PHOIENXI_DEBUG so consumers + * can read overrides at startup with a single getItem. + * + * Read flags from anywhere via: + * const overrides = (function () { + * try { return JSON.parse(localStorage.getItem( + * "LOCAL_OVERIDES_FOR_PHOIENXI_DEBUG")) || {}; } + * catch (e) { return {}; } + * })(); + * if (overrides.AI_PANEL_LOCAL_OVERRIDE) { ... } + */ +define(function (require, exports, module) { + + if (!window.AppConfig || AppConfig.config.environment !== "dev") { + return; + } + + const CommandManager = require("command/CommandManager"), + Dialogs = require("widgets/Dialogs"), + Mustache = require("thirdparty/mustache/mustache"), + OverridesTpl = require("text!./debug-overrides-dialog.html"); + + const COMMAND_ID = "debug.debugOverrides"; + const STORAGE_KEY = "LOCAL_OVERIDES_FOR_PHOIENXI_DEBUG"; + + function _readOverrides() { + try { + const raw = localStorage.getItem(STORAGE_KEY); + if (!raw) { return {}; } + const parsed = JSON.parse(raw); + return (parsed && typeof parsed === "object") ? parsed : {}; + } catch (e) { + return {}; + } + } + + function _writeOverrides(obj) { + try { + localStorage.setItem(STORAGE_KEY, JSON.stringify(obj || {})); + } catch (e) { + console.warn("[Debug Overrides] Failed to persist:", e); + } + } + + function _handleDebugOverrides() { + const overrides = _readOverrides(); + let aiPanelLocalOverride = !!overrides.AI_PANEL_LOCAL_OVERRIDE; + + const html = Mustache.render(OverridesTpl, { + aiPanelLocalOverride: aiPanelLocalOverride + }); + + Dialogs.showModalDialogUsingTemplate(html).done(function (id) { + if (id !== Dialogs.DIALOG_BTN_OK) { return; } + const next = _readOverrides(); + next.AI_PANEL_LOCAL_OVERRIDE = aiPanelLocalOverride; + _writeOverrides(next); + }); + + const $dialog = $(".phoenix-debug-overrides.instance"); + $dialog.find(".ai-panel-local-override").on("change", function () { + aiPanelLocalOverride = $(this).is(":checked"); + }); + } + + CommandManager.register("Debug Overrides…", COMMAND_ID, _handleDebugOverrides); + + exports.COMMAND_ID = COMMAND_ID; + exports.STORAGE_KEY = STORAGE_KEY; +}); From 3b42f9d427819a162c4f5237758afa2abff625ee Mon Sep 17 00:00:00 2001 From: abose Date: Tue, 28 Apr 2026 11:50:18 +0530 Subject: [PATCH 2/3] feat(tour): introduce 3-step onboarding tour overlay MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A one-shot, app-lifetime onboarding tour that points new users at the design-mode toggle, the AI sidebar tab, and the New Project button. Step 1 auto-demos design mode (toggle on, hold 2s, toggle off) so the visible UI change does the explaining; the tooltip text stays stable. Subsequent steps are user-driven via the tooltip's Next/Dismiss buttons — clicking the actual targeted button never advances the tour, giving the user time to read each prompt. Gating: the tour waits for LoginService.proTrialStartDialogDismissed so its overlay never competes with the on-boot pro trial dialog. Falls back to a 60s timeout for runs where that dialog isn't shown. Persists completion via PhStore key "phoenixOnboardingTourState" (version field) so it never re-runs. Funnel metrics under (GUIDE, "tour", *): start, step1, step2, step3, dismiss. --- .../Phoenix/guided-tour.js | 2 + .../Phoenix/phoenix-tour.js | 374 ++++++++++++++++++ src/nls/root/strings.js | 7 + src/styles/Extn-PhoenixTour.less | 169 ++++++++ src/styles/brackets.less | 1 + src/utils/Metrics.js | 5 +- 6 files changed, 556 insertions(+), 2 deletions(-) create mode 100644 src/extensionsIntegrated/Phoenix/phoenix-tour.js create mode 100644 src/styles/Extn-PhoenixTour.less diff --git a/src/extensionsIntegrated/Phoenix/guided-tour.js b/src/extensionsIntegrated/Phoenix/guided-tour.js index d2ac70b8eb..03e81533d4 100644 --- a/src/extensionsIntegrated/Phoenix/guided-tour.js +++ b/src/extensionsIntegrated/Phoenix/guided-tour.js @@ -20,6 +20,7 @@ define(function (require, exports, module) { Dialogs = require("widgets/Dialogs"), Mustache = require("thirdparty/mustache/mustache"), SurveyTemplate = require("text!./html/survey-template.html"), + PhoenixTour = require("./phoenix-tour"), NOTIFICATION_BACKOFF = 10000, GUIDED_TOUR_LOCAL_STORAGE_KEY = "guidedTourActions"; @@ -279,5 +280,6 @@ define(function (require, exports, module) { tourStarted = true; _showBeautifyNotification(); _showSurveys(); + PhoenixTour.startTour(); }; }); diff --git a/src/extensionsIntegrated/Phoenix/phoenix-tour.js b/src/extensionsIntegrated/Phoenix/phoenix-tour.js new file mode 100644 index 0000000000..cbd137a563 --- /dev/null +++ b/src/extensionsIntegrated/Phoenix/phoenix-tour.js @@ -0,0 +1,374 @@ +/* + * 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. + * + */ + +/*global PhStore */ + +/** + * One-shot, app-lifetime onboarding tour that introduces the design-mode + * toggle and the AI sidebar tab. Distinct from the NotificationUI-based + * guided tour: it owns its own overlay (concentric pulse rings + tooltip) + * and drives a short demo of design mode before pointing the user at the + * AI tab. + */ +define(function (require, exports, module) { + + const Strings = require("strings"), + StringUtils = require("utils/StringUtils"), + Metrics = require("utils/Metrics"), + CentralControlBar = require("view/CentralControlBar"); + + // Capture the kernel trust ring at module-load time — it's deleted from + // `window` shortly after boot. Treated as optional: community-edition + // builds without the pro trial flow won't expose `loginService` and the + // tour will simply proceed without waiting. + const _LoginService = (window.KernalModeTrust && window.KernalModeTrust.loginService) || null; + + const TOUR_STORAGE_KEY = "phoenixOnboardingTourState"; + const CURRENT_TOUR_VERSION = 1; + + 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); + if (!raw) { + return { version: 0 }; + } + try { + return JSON.parse(raw); + } catch (e) { + return { version: 0 }; + } + } + + function _saveState(state) { + PhStore.setItem(TOUR_STORAGE_KEY, JSON.stringify(state)); + } + + let _state = _loadState(); + let _ranThisSession = false; + + let $overlay = null; + let _rafId = null; + let _timers = []; + + function _markComplete() { + _state.version = CURRENT_TOUR_VERSION; + _saveState(_state); + } + + function _clearTimers() { + for (let i = 0; i < _timers.length; i++) { + clearTimeout(_timers[i]); + } + _timers = []; + if (_rafId) { + cancelAnimationFrame(_rafId); + _rafId = null; + } + } + + function _teardown() { + _clearTimers(); + if ($overlay) { + $overlay.remove(); + $overlay = null; + } + } + + const TOTAL_STEPS = 3; + + function _ensureOverlay() { + if ($overlay) { + return; + } + $overlay = $( + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + ); + $overlay.appendTo(document.body); + } + + function _setText(text) { + if ($overlay) { + $overlay.find(".phoenix-tour-text").text(text); + } + } + + function _setStep(stepNum) { + if ($overlay) { + $overlay.find(".phoenix-tour-step") + .text(StringUtils.format(Strings.PHOENIX_TOUR_STEP_OF, stepNum, TOTAL_STEPS)); + } + } + + /** + * Replace tooltip action buttons. Pass an empty array to hide the row. + * @param {Array<{label: string, kind: string, onClick: Function}>} buttons + */ + function _setActions(buttons) { + if (!$overlay) { + return; + } + const $actions = $overlay.find(".phoenix-tour-actions").empty(); + if (!buttons || !buttons.length) { + $actions.removeClass("has-buttons"); + return; + } + $actions.addClass("has-buttons"); + buttons.forEach(function (b) { + const kind = b.kind || "primary"; + const $btn = $('') + .addClass("phoenix-tour-btn-" + kind) + .text(b.label); + $btn.on("click", function (e) { + e.preventDefault(); + e.stopPropagation(); + b.onClick(); + }); + $actions.append($btn); + }); + } + + function _trackTarget($target, placement) { + function update() { + if (!$overlay || !$target.length || !$target[0].isConnected) { + _rafId = null; + return; + } + const r = $target[0].getBoundingClientRect(); + if (r.width === 0 && r.height === 0) { + _rafId = requestAnimationFrame(update); + return; + } + const cx = r.left + r.width / 2; + const cy = r.top + r.height / 2; + const el = $overlay[0]; + el.style.left = cx + "px"; + el.style.top = cy + "px"; + _rafId = requestAnimationFrame(update); + } + if (_rafId) { + cancelAnimationFrame(_rafId); + } + $overlay.attr("data-tip-placement", placement || "right"); + update(); + } + + function _runStep1() { + const $btn = $("#ccbCollapseEditorBtn"); + if (!$btn.length) { + _markComplete(); + _teardown(); + return; + } + _ensureOverlay(); + _trackTarget($btn, "right"); + _setStep(1); + Metrics.countEvent(Metrics.EVENT_TYPE.GUIDE, "tour", "step1"); + // Single, stable message for the entire step. The visible toggle of + // design mode does the explaining; rotating text under a 2-second + // demo is too quick to read. + _setText(Strings.PHOENIX_TOUR_DESIGN_MODE); + _setActions([]); // hidden during the auto-demo + $overlay.addClass("phoenix-tour-visible"); + + // Auto-demo: enter design mode, hold, exit, then show "Next". + _timers.push(setTimeout(function () { + $btn.addClass("phoenix-tour-pressed"); + _timers.push(setTimeout(function () { + $btn.removeClass("phoenix-tour-pressed"); + }, 220)); + CentralControlBar.setEditorCollapsed(true); + + _timers.push(setTimeout(function () { + $btn.addClass("phoenix-tour-pressed"); + _timers.push(setTimeout(function () { + $btn.removeClass("phoenix-tour-pressed"); + }, 220)); + CentralControlBar.setEditorCollapsed(false); + _setActions([ + { + label: Strings.PHOENIX_TOUR_NEXT_BTN, + kind: "primary", + onClick: function () { + _runStep2(); + } + } + ]); + }, STEP1_DESIGN_MODE_HOLD_MS)); + }, STEP1_INVITE_MS)); + } + + function _runStep2() { + const $tab = $('.sidebar-tab[data-tab-id="ai"]'); + if (!$tab.length) { + // No AI tab in this build — skip ahead to the next step. + _runStep3(); + return; + } + _ensureOverlay(); + _trackTarget($tab, "right"); + _setStep(2); + Metrics.countEvent(Metrics.EVENT_TYPE.GUIDE, "tour", "step2"); + _setText(Strings.PHOENIX_TOUR_AI_PANEL); + _setActions([ + { + label: Strings.PHOENIX_TOUR_NEXT_BTN, + kind: "primary", + onClick: function () { + _runStep3(); + } + } + ]); + // Intentionally do NOT advance on a real click of the target — the + // user needs time to read the prompt; only the Next button advances. + } + + function _runStep3() { + const $newBtn = $("#newProject"); + if (!$newBtn.length) { + // No new-project button — tour is effectively done. + _markComplete(); + _teardown(); + return; + } + _ensureOverlay(); + _trackTarget($newBtn, "right"); + _setStep(3); + Metrics.countEvent(Metrics.EVENT_TYPE.GUIDE, "tour", "step3"); + _setText(Strings.PHOENIX_TOUR_NEW_PROJECT); + _setActions([ + { + label: Strings.PHOENIX_TOUR_DISMISS_BTN, + kind: "secondary", + onClick: function () { + Metrics.countEvent(Metrics.EVENT_TYPE.GUIDE, "tour", "dismiss"); + _markComplete(); + _teardown(); + } + } + ]); + // Intentionally do NOT end on a real click of the target — only the + // Dismiss button ends the tour. + } + + function _shouldRun() { + if (_ranThisSession) { + return false; + } + if (_state.version >= CURRENT_TOUR_VERSION) { + return false; + } + if (Phoenix.isTestWindow || Phoenix.isSpecRunnerWindow) { + return false; + } + if (CentralControlBar.isEditorCollapsed && CentralControlBar.isEditorCollapsed()) { + // User has already discovered design mode in some other way. + return false; + } + if (!$("#ccbCollapseEditorBtn").length) { + return false; + } + return true; + } + + /** + * 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. + */ + 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(); + }); + }); + } + + function startTour() { + if (!_shouldRun()) { + return; + } + _ranThisSession = true; + Metrics.countEvent(Metrics.EVENT_TYPE.GUIDE, "tour", "start"); + + _waitForTrialStartDialogDismissed().then(function () { + // Re-check primary preconditions after the wait — the user may + // have already discovered design mode while a trial dialog was + // up, or the button may have been torn down. + if (!$("#ccbCollapseEditorBtn").length) { + _markComplete(); + _teardown(); + return; + } + if (CentralControlBar.isEditorCollapsed && CentralControlBar.isEditorCollapsed()) { + _markComplete(); + _teardown(); + return; + } + _timers.push(setTimeout(function () { + if (!$("#ccbCollapseEditorBtn").length) { + _markComplete(); + _teardown(); + return; + } + _runStep1(); + }, STEP_START_DELAY_MS)); + }); + } + + exports.startTour = startTour; +}); diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js index 581c919fe9..0d95aa809b 100644 --- a/src/nls/root/strings.js +++ b/src/nls/root/strings.js @@ -1668,6 +1668,13 @@ define({ // Guided tour "GUIDED_LIVE_PREVIEW": "Make some code changes in the HTML file to see live preview.
ok", "GUIDED_LIVE_PREVIEW_POPOUT": "Click this button to popout live preview to a new tab.
ok", + // Phoenix onboarding tour (one-shot, app-lifetime) + "PHOENIX_TOUR_DESIGN_MODE": "Click here to enter Design Mode. Go full-screen and edit your page visually.", + "PHOENIX_TOUR_AI_PANEL": "Click here to open the AI panel", + "PHOENIX_TOUR_NEW_PROJECT": "Open or create a new project from here", + "PHOENIX_TOUR_STEP_OF": "{0} of {1}", + "PHOENIX_TOUR_NEXT_BTN": "Next", + "PHOENIX_TOUR_DISMISS_BTN": "Dismiss", "TEST_TRANSLATE": "use this to test translations", //beautify extension "BEAUTIFY_ERROR": "Could not beautify code. Check Syntax.", diff --git a/src/styles/Extn-PhoenixTour.less b/src/styles/Extn-PhoenixTour.less new file mode 100644 index 0000000000..79eecdbfa5 --- /dev/null +++ b/src/styles/Extn-PhoenixTour.less @@ -0,0 +1,169 @@ +/* + * 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. + * + */ + +/* Phoenix one-shot onboarding tour overlay (concentric pulse rings + tooltip). + The wrapper is centered on the target via inline left/top updated each frame + in JS — translate(-50%, -50%) handles the centering. The wrapper itself does + not capture pointer events; only the tooltip's action buttons do. */ + +.phoenix-tour-overlay { + position: fixed; + left: 0; + top: 0; + z-index: 100000; + pointer-events: none; + transform: translate(-50%, -50%); + opacity: 0; + transition: opacity 220ms ease-out; + + &.phoenix-tour-visible { + opacity: 1; + } +} + +.phoenix-tour-ring { + position: absolute; + left: 50%; + top: 50%; + width: 36px; + height: 36px; + margin: -18px 0 0 -18px; + border: 2px solid #4aa3ff; + border-radius: 50%; + box-shadow: 0 0 12px rgba(74, 163, 255, 0.45); + opacity: 0.9; + animation: phoenix-tour-pulse 1.6s ease-out infinite; +} + +.phoenix-tour-ring-2 { + animation-delay: 0.8s; +} + +@keyframes phoenix-tour-pulse { + 0% { + transform: scale(0.55); + opacity: 0.95; + } + 70% { + opacity: 0.35; + } + 100% { + transform: scale(2.6); + opacity: 0; + } +} + +/* Tooltip — sits below-right of the ring so it stays within the viewport + even when the target is near the top of the window (both tour targets, + the design-mode button and the AI sidebar tab, sit at the top). Solid + blue surface so the panel reads clearly against either light or dark + theme; theme-agnostic on purpose. The arrow points up-left at the ring. */ +@phoenix-tour-bg: #1e63d8; +@phoenix-tour-bg-end: #2c7bff; + +.phoenix-tour-tooltip { + position: absolute; + left: 18px; + top: 24px; + background: linear-gradient(135deg, @phoenix-tour-bg 0%, @phoenix-tour-bg-end 100%); + color: #ffffff; + padding: 10px 14px; + border-radius: 8px; + font-size: 13px; + line-height: 1.35; + max-width: 280px; + min-width: 180px; + pointer-events: auto; + border: 1px solid rgba(255, 255, 255, 0.25); + box-shadow: 0 10px 28px rgba(0, 0, 0, 0.55), + 0 0 0 4px rgba(30, 99, 216, 0.18); + + &::before { + content: ""; + position: absolute; + left: 14px; + top: -7px; + border: 7px solid transparent; + border-bottom-color: @phoenix-tour-bg; + } +} + +.phoenix-tour-step { + display: block; + font-size: 11px; + font-weight: 600; + letter-spacing: 0.04em; + text-transform: uppercase; + color: rgba(255, 255, 255, 0.7); + margin-bottom: 4px; +} + +.phoenix-tour-text { + display: block; +} + +.phoenix-tour-actions { + display: none; + margin-top: 10px; + justify-content: flex-end; + gap: 8px; + + &.has-buttons { + display: flex; + } +} + +.phoenix-tour-btn { + appearance: none; + border: 0; + border-radius: 5px; + padding: 5px 12px; + font-size: 12px; + font-weight: 600; + cursor: pointer; + line-height: 1.2; + transition: background-color 0.12s ease, color 0.12s ease; + + &.phoenix-tour-btn-primary { + background: #ffffff; + color: #1e63d8; + + &:hover { + background: #f1f6ff; + } + } + + &.phoenix-tour-btn-secondary { + background: rgba(255, 255, 255, 0.18); + color: #ffffff; + border: 1px solid rgba(255, 255, 255, 0.4); + + &:hover { + background: rgba(255, 255, 255, 0.28); + } + } +} + +/* Brief press effect applied to the demoed button by the tour driver so + the auto-toggled design-mode click feels visually anchored. */ +.phoenix-tour-pressed { + transform: scale(0.92); + transition: transform 120ms ease-out; +} diff --git a/src/styles/brackets.less b/src/styles/brackets.less index 7dc6d979bc..c96be89166 100644 --- a/src/styles/brackets.less +++ b/src/styles/brackets.less @@ -50,6 +50,7 @@ @import "Extn-BottomPanelTabs.less"; @import "Extn-AIChatPanel.less"; @import "CentralControlBar.less"; +@import "Extn-PhoenixTour.less"; @import "Extn-Terminal.less"; @import "UserProfile.less"; @import "phoenix-pro.less"; diff --git a/src/utils/Metrics.js b/src/utils/Metrics.js index d34d48e277..9d096e5722 100644 --- a/src/utils/Metrics.js +++ b/src/utils/Metrics.js @@ -94,7 +94,7 @@ define(function (require, exports, module) { * ### Properties * `PLATFORM`, `PROJECT`, `THEMES`, `EXTENSIONS`, `EXTENSIONS`, `UI`, `UI_DIALOG`, `UI_BOTTOM_PANEL`, * `UI_SIDE_PANEL`, `LIVE_PREVIEW`, `CODE_HINTS`, `EDITOR`, `SEARCH`, `SHARING`, `PERFORMANCE`, `NEW_PROJECT` - * `ERROR`, `USER`, `NODEJS`, `LINT`, `GIT` + * `ERROR`, `USER`, `NODEJS`, `LINT`, `GIT`, `AUTH`, `PRO`, `GUIDE` * * @typedef EVENT_TYPE * @type {Object} @@ -128,7 +128,8 @@ define(function (require, exports, module) { LINT: "lint", GIT: "git", AUTH: "auth", - PRO: "pro" + PRO: "pro", + GUIDE: "guide" }; /** From 1f494050c9dbbfe132d365569fbb4ff75c3d73d3 Mon Sep 17 00:00:00 2001 From: abose Date: Tue, 28 Apr 2026 12:13:00 +0530 Subject: [PATCH 3/3] build: 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 ebb6ad1b3b..d9af866619 100644 --- a/tracking-repos.json +++ b/tracking-repos.json @@ -1,5 +1,5 @@ { "phoenixPro": { - "commitID": "05c8420787657d64785b753c80b901e27ae74270" + "commitID": "35a5652662376d7149a1a9b7cf6d4f5ad9cf3cd5" } }