Skip to content

Commit 85426b0

Browse files
committed
feat(php): find-bar-style install prompt with benefits tooltip
Replace the install prompt toast with a ModalBar banner across the top of the editor - the same surface as the find bar, impossible to miss on the very file that triggered it, yet passive (autoClose off: clicking back into the code doesn't dismiss it). - Terse copy: "Install advanced PHP code intelligence?" followed by an (i) icon whose hover shows a compact benefits card (the reusable rich tooltip): Completions / Docs on hover / Error checking / Navigation, one plain line each. Right side: "Powered by Intelephense" link, Not Now, and a primary Install button. - Reappears on every PHP file switch in the project until explicitly dismissed via Not Now (session-scoped per project); switching away just closes it for that moment. The Problems-panel row stays as the quiet persistent affordance, and php.codeIntelligence remains the durable off-switch. Install click closes the bar and hands off to the existing TaskManager install task + auto-start. Verified live: fresh consent -> bar -> Install -> server up; re-show on file switch; benefits tooltip; bar closes on non-php files.
1 parent b35fe7d commit 85426b0

3 files changed

Lines changed: 108 additions & 36 deletions

File tree

src/extensions/default/PHPSupport/ServerInstaller.js

Lines changed: 67 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ define(function (require, exports, module) {
4747
const NodeUtils = brackets.getModule("utils/NodeUtils"),
4848
ProjectManager = brackets.getModule("project/ProjectManager"),
4949
NativeApp = brackets.getModule("utils/NativeApp"),
50+
ModalBar = brackets.getModule("widgets/ModalBar").ModalBar,
5051
NotificationUI = brackets.getModule("widgets/NotificationUI"),
5152
TaskManager = brackets.getModule("features/TaskManager"),
5253
PreferencesManager = brackets.getModule("preferences/PreferencesManager"),
@@ -60,8 +61,12 @@ define(function (require, exports, module) {
6061

6162
let _onInstalled = null; // main.js callback: ({entryPath, upgraded}) => void
6263
let _inFlight = null; // single-flight install promise
63-
const _promptShownForProject = new Set(); // prompt toast: once per project per session
64-
let _promptToast = null;
64+
// projects where the user clicked "Not Now" - the bar stops reappearing there for the
65+
// session. Until then it returns on every php file switch (closing on switch-away is not a
66+
// dismissal - only the explicit button is).
67+
const _promptDismissedForProject = new Set();
68+
let _promptBar = null; // the ModalBar install prompt, when showing
69+
let _promptBarTip = null; // the benefits tooltip binding on the bar's info icon
6570
let _panelRowDismissed = false; // Problems-panel row dismissed this session
6671

6772
function _installDirVfs() {
@@ -202,49 +207,71 @@ define(function (require, exports, module) {
202207

203208
// ----- consent UI: prompt toast + Problems-panel row (mirrors the TS enable affordances) ------
204209

205-
function _dismissPromptToast() {
206-
if (_promptToast) {
207-
_promptToast.close();
208-
_promptToast = null;
210+
function _closePromptBar() {
211+
if (_promptBarTip) {
212+
_promptBarTip.detach();
213+
_promptBarTip = null;
214+
}
215+
if (_promptBar) {
216+
_promptBar.close();
217+
_promptBar = null;
209218
}
210219
}
211220

212-
function _showPromptToast() {
221+
// Compact benefits card for the bar's (i) icon - term -> what it means, one line each.
222+
function _benefitsTipHtml() {
223+
const $tip = $("<div>");
224+
$("<div class='ph-tip-title'>").text(Strings.PHP_INSTALL_TITLE).appendTo($tip);
225+
const $rows = $("<div class='ph-tip-rows'>").appendTo($tip);
226+
[
227+
[Strings.PHP_BENEFIT_COMPLETIONS, Strings.PHP_BENEFIT_COMPLETIONS_SUB],
228+
[Strings.PHP_BENEFIT_DOCS, Strings.PHP_BENEFIT_DOCS_SUB],
229+
[Strings.PHP_BENEFIT_ERRORS, Strings.PHP_BENEFIT_ERRORS_SUB],
230+
[Strings.PHP_BENEFIT_NAV, Strings.PHP_BENEFIT_NAV_SUB]
231+
].forEach(function (row) {
232+
$("<span class='ph-tip-term'>").text(row[0]).appendTo($rows);
233+
$("<span class='ph-tip-def'>").text(row[1]).appendTo($rows);
234+
});
235+
return $tip.html();
236+
}
237+
238+
// A find-bar-style banner across the top of the editor - impossible to miss on the file that
239+
// triggered it, but passive (autoClose false: clicking back into the code doesn't dismiss it).
240+
function _showPromptBar() {
213241
const root = ProjectManager.getProjectRoot();
214242
const rootPath = (root && root.fullPath) || "";
215-
if (_promptShownForProject.has(rootPath)) {
243+
if (_promptDismissedForProject.has(rootPath) || _promptBar) {
216244
return;
217245
}
218-
_promptShownForProject.add(rootPath);
219-
const $tpl = $("<div class='ts-code-intel-toast'>");
220-
$("<div class='ts-code-intel-msg'>").text(Strings.PHP_INSTALL_MESSAGE).appendTo($tpl);
246+
// built as detached DOM then serialized (ModalBar takes an HTML string); all click
247+
// handling is delegated on the live bar root below
248+
const $tpl = $("<div class='php-install-bar'>");
249+
$("<span class='php-install-bar-text'>").text(Strings.PHP_INSTALL_MESSAGE).appendTo($tpl);
250+
$("<i class='fa-solid fa-circle-info php-install-bar-info'>").appendTo($tpl);
221251
// credit where due (and where premium lives) - not license-required, just right
222-
$("<a class='php-intel-powered-by'>").text(Strings.PHP_POWERED_BY_INTELEPHENSE)
223-
.attr("href", "#")
224-
.on("click", function (e) {
225-
e.preventDefault();
226-
e.stopPropagation();
227-
NativeApp.openURLInDefaultBrowser(INTELEPHENSE_HOME_URL);
228-
})
252+
$("<a class='php-intel-powered-by' href='#'>").text(Strings.PHP_POWERED_BY_INTELEPHENSE)
229253
.appendTo($tpl);
230-
const $btns = $("<div class='ts-code-intel-buttons'>").appendTo($tpl);
231-
const $install = $("<button class='ts-code-intel-action'>")
232-
.text(Strings.PHP_INSTALL_ENABLE).appendTo($btns);
233-
const $later = $("<button class='ts-code-intel-action'>")
234-
.text(Strings.PHP_INSTALL_NOT_NOW).appendTo($btns);
235-
// SUBTLE like the TS enable toast - quiet, theme-matching surface where the link-style
236-
// action buttons read clearly. The Problems-panel row provides the persistent affordance
237-
// if the toast is missed.
238-
_promptToast = NotificationUI.createToastFromTemplate(Strings.PHP_INSTALL_TITLE, $tpl, {
239-
dismissOnClick: false, autoCloseTimeS: 45, instantOpen: true,
240-
toastStyle: NotificationUI.NOTIFICATION_STYLES_CSS_CLASS.SUBTLE
241-
});
242-
$install.on("click", function () {
243-
_dismissPromptToast();
254+
$("<button class='btn btn-mini php-install-bar-later'>")
255+
.text(Strings.PHP_INSTALL_NOT_NOW).appendTo($tpl);
256+
$("<button class='btn btn-mini primary php-install-bar-install'>")
257+
.text(Strings.PHP_INSTALL_ENABLE).appendTo($tpl);
258+
259+
_promptBar = new ModalBar($tpl[0].outerHTML, false);
260+
const $bar = _promptBar.getRoot();
261+
_promptBarTip = NotificationUI.attachRichTooltip(
262+
$bar.find(".php-install-bar-info"), _benefitsTipHtml(), { showDelayMs: 150 });
263+
$bar.on("click", ".php-install-bar-install", function () {
264+
_closePromptBar();
244265
installNow();
245266
});
246-
$later.on("click", function () {
247-
_dismissPromptToast();
267+
$bar.on("click", ".php-install-bar-later", function () {
268+
const projRoot = ProjectManager.getProjectRoot();
269+
_promptDismissedForProject.add((projRoot && projRoot.fullPath) || "");
270+
_closePromptBar();
271+
});
272+
$bar.on("click", ".php-intel-powered-by", function (e) {
273+
e.preventDefault();
274+
NativeApp.openURLInDefaultBrowser(INTELEPHENSE_HOME_URL);
248275
});
249276
}
250277

@@ -300,6 +327,9 @@ define(function (require, exports, module) {
300327
if (!$row) {
301328
return;
302329
}
330+
if (!phpDocumentActive) {
331+
_closePromptBar(); // the bar belongs to the php file that triggered it
332+
}
303333
if (!phpDocumentActive || _panelRowDismissed || _inFlight ||
304334
PreferencesManager.get(PREF_PHP_CODE_INTELLIGENCE) === false) {
305335
$row.hide();
@@ -323,7 +353,9 @@ define(function (require, exports, module) {
323353
if (typeof Phoenix !== "undefined" && Phoenix.isTestWindow) {
324354
return;
325355
}
326-
_showPromptToast();
356+
if (phpDocumentActive) {
357+
_showPromptBar();
358+
}
327359
updatePanelRow(phpDocumentActive);
328360
}
329361

src/nls/root/strings.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1802,10 +1802,18 @@ define({
18021802
"CODE_INTEL_PANEL_DISMISS": "Dismiss",
18031803
// PHP language support (PHPSupport)
18041804
"PHP_INSTALL_TITLE": "PHP Code Intelligence",
1805-
"PHP_INSTALL_MESSAGE": "Smart completions, docs on hover, error checking and go to definition for PHP. {APP_NAME} downloads the Intelephense language server (free third-party tool, ~9 MB) to your computer.",
1805+
"PHP_INSTALL_MESSAGE": "Install advanced PHP code intelligence?",
18061806
"PHP_INSTALL_ENABLE": "Install",
18071807
"PHP_INSTALL_NOT_NOW": "Not Now",
18081808
"PHP_POWERED_BY_INTELEPHENSE": "Powered by Intelephense",
1809+
"PHP_BENEFIT_COMPLETIONS": "Completions",
1810+
"PHP_BENEFIT_COMPLETIONS_SUB": "smart suggestions as you type",
1811+
"PHP_BENEFIT_DOCS": "Docs on hover",
1812+
"PHP_BENEFIT_DOCS_SUB": "function help right in the editor",
1813+
"PHP_BENEFIT_ERRORS": "Error checking",
1814+
"PHP_BENEFIT_ERRORS_SUB": "mistakes flagged as you code",
1815+
"PHP_BENEFIT_NAV": "Navigation",
1816+
"PHP_BENEFIT_NAV_SUB": "go to definition and find usages",
18091817
"PHP_PANEL_TEXT": "PHP code intelligence is off. Install the Intelephense language server (free, ~9 MB download) for completions, error checking, docs and go to definition.",
18101818
"PHP_INSTALLING": "Setting up PHP support — downloading the language server…",
18111819
"PHP_INSTALL_DONE": "PHP code intelligence is ready.",

src/styles/brackets.less

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2220,6 +2220,38 @@ a, img {
22202220
}
22212221
}
22222222

2223+
// PHP install prompt bar (PHPSupport) - find-bar-style banner across the editor top.
2224+
.modal-bar .php-install-bar {
2225+
display: flex;
2226+
align-items: center;
2227+
gap: 12px;
2228+
.php-install-bar-text {
2229+
flex: none; // hug the question so the (i) sits right after it
2230+
min-width: 0;
2231+
overflow: hidden;
2232+
text-overflow: ellipsis;
2233+
white-space: nowrap;
2234+
}
2235+
// sits right after the question; margin-right:auto pushes everything else to the right edge
2236+
.php-install-bar-info {
2237+
flex: none;
2238+
margin-left: -4px;
2239+
margin-right: auto;
2240+
opacity: 0.5;
2241+
cursor: default;
2242+
&:hover { opacity: 0.9; }
2243+
}
2244+
.php-intel-powered-by {
2245+
flex: none;
2246+
cursor: pointer;
2247+
white-space: nowrap;
2248+
}
2249+
button {
2250+
flex: none;
2251+
margin: 0;
2252+
}
2253+
}
2254+
22232255
// On bright colored toast surfaces (e.g. the PHP install prompt on INFO blue) the link-blue
22242256
// action text is invisible - use high-contrast white bold links there, both themes.
22252257
.notification-popup-container.style-info .ts-code-intel-action,

0 commit comments

Comments
 (0)