mirror of
https://github.com/nicoverbruggen/phpmon.git
synced 2025-08-08 04:20:07 +02:00
♻️ Reworked helper scripts
- Add 'Welcome Tour' to First Aid menu - Updated 'Welcome Tour' - Helpers are now always written to ~/.config/phpmon/bin - Updated helpers (now symlinked) - Updated checks for when to symlink helpers
This commit is contained in:
@ -1677,7 +1677,7 @@
|
|||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 920;
|
CURRENT_PROJECT_VERSION = 950;
|
||||||
DEBUG = YES;
|
DEBUG = YES;
|
||||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@ -1704,7 +1704,7 @@
|
|||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 920;
|
CURRENT_PROJECT_VERSION = 950;
|
||||||
DEBUG = NO;
|
DEBUG = NO;
|
||||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
|
@ -19,9 +19,12 @@ public class Paths {
|
|||||||
|
|
||||||
private var userName: String
|
private var userName: String
|
||||||
|
|
||||||
|
private var PATH: String
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
baseDir = App.architecture != "x86_64" ? .opt : .usr
|
baseDir = App.architecture != "x86_64" ? .opt : .usr
|
||||||
userName = String(Shell.pipe("whoami").split(separator: "\n")[0])
|
userName = String(Shell.pipe("whoami").split(separator: "\n")[0])
|
||||||
|
PATH = String(Shell.pipe("echo $PATH")).trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func detectBinaryPaths() {
|
public func detectBinaryPaths() {
|
||||||
@ -57,6 +60,10 @@ public class Paths {
|
|||||||
return shared.userName
|
return shared.userName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static var PATH: String {
|
||||||
|
return shared.PATH
|
||||||
|
}
|
||||||
|
|
||||||
public static var cellarPath: String {
|
public static var cellarPath: String {
|
||||||
return "\(shared.baseDir.rawValue)/Cellar"
|
return "\(shared.baseDir.rawValue)/Cellar"
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,15 @@ class Filesystem {
|
|||||||
return exists && !isDirectory.boolValue
|
return exists && !isDirectory.boolValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static func fileIsSymlink(_ path: String) -> Bool {
|
||||||
|
do {
|
||||||
|
let attribs = try FileManager.default.attributesOfItem(atPath: path)
|
||||||
|
return attribs[.type] as! FileAttributeType == FileAttributeType.typeSymbolicLink
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Checks if a directory exists at the provided path.
|
Checks if a directory exists at the provided path.
|
||||||
*/
|
*/
|
||||||
|
@ -16,8 +16,18 @@ class PhpHelper {
|
|||||||
// Take the PHP version (e.g. "7.2") and generate a dotless version
|
// Take the PHP version (e.g. "7.2") and generate a dotless version
|
||||||
let dotless = version.replacingOccurrences(of: ".", with: "")
|
let dotless = version.replacingOccurrences(of: ".", with: "")
|
||||||
|
|
||||||
|
// Determine the dotless name for this PHP version
|
||||||
|
let destination = "/Users/\(Paths.whoami)/.config/phpmon/bin/pm\(dotless)"
|
||||||
|
|
||||||
|
// Check if the ~/.config/phpmon/bin directory is in the PATH
|
||||||
|
let inPath = Paths.PATH.contains("/Users/\(Paths.whoami)/.config/phpmon/bin")
|
||||||
|
|
||||||
|
// Check if we can create symlinks (`/usr/local/bin` must be writable)
|
||||||
|
let canWriteSymlinks = FileManager.default.isWritableFile(atPath: "/usr/local/bin/")
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let destination = "/usr/local/bin/pm\(dotless)"
|
Shell.run("mkdir -p ~/.config/phpmon/bin")
|
||||||
|
|
||||||
if FileManager.default.fileExists(atPath: destination) {
|
if FileManager.default.fileExists(atPath: destination) {
|
||||||
let contents = try String(contentsOfFile: destination)
|
let contents = try String(contentsOfFile: destination)
|
||||||
if !contents.contains(keyPhrase) {
|
if !contents.contains(keyPhrase) {
|
||||||
@ -52,10 +62,40 @@ class PhpHelper {
|
|||||||
|
|
||||||
// Make sure the file is executable
|
// Make sure the file is executable
|
||||||
Shell.run("chmod +x \(destination)")
|
Shell.run("chmod +x \(destination)")
|
||||||
|
|
||||||
|
// Create a symlink if the folder is not in the PATH
|
||||||
|
if !inPath {
|
||||||
|
// First, check if we can create symlinks at all
|
||||||
|
if !canWriteSymlinks {
|
||||||
|
Log.err("PHP Monitor does not have permission to symlink `/usr/local/bin/\(dotless)`.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the symlink
|
||||||
|
self.createSymlink(dotless)
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
print(error)
|
print(error)
|
||||||
Log.err("Could not write PHP Monitor helper for PHP \(version) to /usr/local/bin/pm\(dotless)")
|
Log.err("Could not write PHP Monitor helper for PHP \(version) to \(destination))")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static func createSymlink(_ dotless: String) {
|
||||||
|
let source = "/Users/\(Paths.whoami)/.config/phpmon/bin/pm\(dotless)"
|
||||||
|
let destination = "/usr/local/bin/pm\(dotless)"
|
||||||
|
|
||||||
|
if !Filesystem.fileExists(destination) {
|
||||||
|
Log.info("Creating new symlink: \(destination)")
|
||||||
|
Shell.run("ln -s \(source) \(destination)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !Filesystem.fileIsSymlink(destination) {
|
||||||
|
Log.info("Overwriting existing file with new symlink: \(destination)")
|
||||||
|
Shell.run("ln -fs \(source) \(destination)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.info("Symlink in \(destination) already exists, OK.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,6 +57,9 @@ extension MainMenu {
|
|||||||
let installation = PhpEnv.phpInstall
|
let installation = PhpEnv.phpInstall
|
||||||
installation.notifyAboutBrokenPhpFpm()
|
installation.notifyAboutBrokenPhpFpm()
|
||||||
|
|
||||||
|
// Check for other problems
|
||||||
|
WarningManager.shared.evaluateWarnings()
|
||||||
|
|
||||||
// Set up the config watchers on launch (updated automatically when switching)
|
// Set up the config watchers on launch (updated automatically when switching)
|
||||||
Log.info("Setting up watchers...")
|
Log.info("Setting up watchers...")
|
||||||
App.shared.handlePhpConfigWatcher()
|
App.shared.handlePhpConfigWatcher()
|
||||||
@ -85,15 +88,11 @@ extension MainMenu {
|
|||||||
// Start the background refresh timer
|
// Start the background refresh timer
|
||||||
startSharedTimer()
|
startSharedTimer()
|
||||||
|
|
||||||
// Check warnings
|
|
||||||
WarningManager.shared.evaluateWarnings()
|
|
||||||
|
|
||||||
// Update the stats
|
// Update the stats
|
||||||
Stats.incrementSuccessfulLaunchCount()
|
Stats.incrementSuccessfulLaunchCount()
|
||||||
Stats.evaluateSponsorMessageShouldBeDisplayed()
|
Stats.evaluateSponsorMessageShouldBeDisplayed()
|
||||||
|
|
||||||
// Present first launch screen if needed
|
// Present first launch screen if needed
|
||||||
#warning("You should definitely tweak this view again")
|
|
||||||
if Stats.successfulLaunchCount == 0 && !isRunningSwiftUIPreview {
|
if Stats.successfulLaunchCount == 0 && !isRunningSwiftUIPreview {
|
||||||
Log.info("Should present the first launch screen!")
|
Log.info("Should present the first launch screen!")
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
|
@ -126,6 +126,16 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate
|
|||||||
ServicesManager.shared.loadData()
|
ServicesManager.shared.loadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Shows the Welcome Tour screen, again.
|
||||||
|
Did this need a comment? No, probably not.
|
||||||
|
*/
|
||||||
|
@objc func showWelcomeTour() {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
OnboardingWindowController.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Reloads the menu in the background, using `asyncExecution`. */
|
/** Reloads the menu in the background, using `asyncExecution`. */
|
||||||
@objc func reloadPhpMonitorMenuInBackground() {
|
@objc func reloadPhpMonitorMenuInBackground() {
|
||||||
asyncExecution({
|
asyncExecution({
|
||||||
|
@ -199,6 +199,10 @@ extension StatusMenu {
|
|||||||
let servicesMenu = NSMenu()
|
let servicesMenu = NSMenu()
|
||||||
|
|
||||||
servicesMenu.addItem(HeaderView.asMenuItem(text: "mi_first_aid".localized))
|
servicesMenu.addItem(HeaderView.asMenuItem(text: "mi_first_aid".localized))
|
||||||
|
|
||||||
|
servicesMenu.addItem(NSMenuItem(title: "mi_view_onboarding".localized,
|
||||||
|
action: #selector(MainMenu.showWelcomeTour), keyEquivalent: ""))
|
||||||
|
|
||||||
let fixMyValetMenuItem = NSMenuItem(
|
let fixMyValetMenuItem = NSMenuItem(
|
||||||
title: "mi_fix_my_valet".localized(PhpEnv.brewPhpVersion),
|
title: "mi_fix_my_valet".localized(PhpEnv.brewPhpVersion),
|
||||||
action: #selector(MainMenu.fixMyValet), keyEquivalent: ""
|
action: #selector(MainMenu.fixMyValet), keyEquivalent: ""
|
||||||
@ -216,22 +220,15 @@ extension StatusMenu {
|
|||||||
servicesMenu.addItem(NSMenuItem.separator())
|
servicesMenu.addItem(NSMenuItem.separator())
|
||||||
servicesMenu.addItem(HeaderView.asMenuItem(text: "mi_services".localized))
|
servicesMenu.addItem(HeaderView.asMenuItem(text: "mi_services".localized))
|
||||||
|
|
||||||
servicesMenu.addItem(
|
servicesMenu.addItem(NSMenuItem(title: "mi_restart_dnsmasq".localized,
|
||||||
NSMenuItem(title: "mi_restart_dnsmasq".localized,
|
action: #selector(MainMenu.restartDnsMasq), keyEquivalent: "d"))
|
||||||
action: #selector(MainMenu.restartDnsMasq), keyEquivalent: "d")
|
servicesMenu.addItem(NSMenuItem(title: "mi_restart_php_fpm".localized,
|
||||||
)
|
action: #selector(MainMenu.restartPhpFpm), keyEquivalent: "p"))
|
||||||
servicesMenu.addItem(
|
|
||||||
NSMenuItem(title: "mi_restart_php_fpm".localized,
|
servicesMenu.addItem(NSMenuItem(title: "mi_restart_nginx".localized,
|
||||||
action: #selector(MainMenu.restartPhpFpm), keyEquivalent: "p")
|
action: #selector(MainMenu.restartNginx), keyEquivalent: "n"))
|
||||||
)
|
servicesMenu.addItem(NSMenuItem(title: "mi_restart_valet_services".localized,
|
||||||
servicesMenu.addItem(
|
action: #selector(MainMenu.restartValetServices), keyEquivalent: "s"))
|
||||||
NSMenuItem(title: "mi_restart_nginx".localized,
|
|
||||||
action: #selector(MainMenu.restartNginx), keyEquivalent: "n")
|
|
||||||
)
|
|
||||||
servicesMenu.addItem(
|
|
||||||
NSMenuItem(title: "mi_restart_valet_services".localized,
|
|
||||||
action: #selector(MainMenu.restartValetServices), keyEquivalent: "s")
|
|
||||||
)
|
|
||||||
servicesMenu.addItem(
|
servicesMenu.addItem(
|
||||||
NSMenuItem(title: "mi_stop_valet_services".localized,
|
NSMenuItem(title: "mi_stop_valet_services".localized,
|
||||||
action: #selector(MainMenu.stopValetServices), keyEquivalent: "s"),
|
action: #selector(MainMenu.stopValetServices), keyEquivalent: "s"),
|
||||||
|
@ -25,18 +25,17 @@ struct OnboardingTextItem: View {
|
|||||||
Text(title.localizedForSwiftUI)
|
Text(title.localizedForSwiftUI)
|
||||||
.font(.system(size: 14))
|
.font(.system(size: 14))
|
||||||
.lineLimit(3)
|
.lineLimit(3)
|
||||||
HStack {
|
Text(description.localizedForSwiftUI)
|
||||||
Text(description.localizedForSwiftUI)
|
.foregroundColor(Color.secondary)
|
||||||
.foregroundColor(Color.secondary)
|
.font(.system(size: 13))
|
||||||
.font(.system(size: 13))
|
.lineLimit(6)
|
||||||
.lineLimit(3)
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
.fixedSize(horizontal: false, vertical: true)
|
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
|
||||||
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
.overlay(RoundedRectangle(cornerRadius: 5).stroke(Color.gray.opacity(0.3), lineWidth: 1))
|
.overlay(RoundedRectangle(cornerRadius: 5)
|
||||||
|
.stroke(Color.gray.opacity(0.3), lineWidth: 1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,9 +56,13 @@ struct OnboardingView: View {
|
|||||||
.padding(.bottom, 5)
|
.padding(.bottom, 5)
|
||||||
Text("onboarding.explore".localized)
|
Text("onboarding.explore".localized)
|
||||||
.padding(.bottom)
|
.padding(.bottom)
|
||||||
|
.padding(.trailing)
|
||||||
}
|
}
|
||||||
.padding(.top, 10)
|
.padding(.top, 10)
|
||||||
}
|
}
|
||||||
|
.padding(.leading)
|
||||||
|
.padding(.trailing)
|
||||||
|
|
||||||
VStack {
|
VStack {
|
||||||
VStack(alignment: .leading, spacing: 10) {
|
VStack(alignment: .leading, spacing: 10) {
|
||||||
OnboardingTextItem(
|
OnboardingTextItem(
|
||||||
@ -67,6 +70,11 @@ struct OnboardingView: View {
|
|||||||
title: "onboarding.tour.menu_bar.title",
|
title: "onboarding.tour.menu_bar.title",
|
||||||
description: "onboarding.tour.menu_bar"
|
description: "onboarding.tour.menu_bar"
|
||||||
)
|
)
|
||||||
|
OnboardingTextItem(
|
||||||
|
icon: "checkmark.circle.fill",
|
||||||
|
title: "onboarding.tour.services.title",
|
||||||
|
description: "onboarding.tour.services"
|
||||||
|
)
|
||||||
OnboardingTextItem(
|
OnboardingTextItem(
|
||||||
icon: "list.bullet.circle.fill",
|
icon: "list.bullet.circle.fill",
|
||||||
title: "onboarding.tour.domains.title",
|
title: "onboarding.tour.domains.title",
|
||||||
@ -79,6 +87,7 @@ struct OnboardingView: View {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}.padding()
|
}.padding()
|
||||||
|
|
||||||
VStack(spacing: 20) {
|
VStack(spacing: 20) {
|
||||||
HStack {
|
HStack {
|
||||||
Image(systemName: "questionmark.circle.fill")
|
Image(systemName: "questionmark.circle.fill")
|
||||||
|
@ -32,11 +32,16 @@ class WarningManager {
|
|||||||
),
|
),
|
||||||
Warning(
|
Warning(
|
||||||
command: {
|
command: {
|
||||||
|
!Paths.PATH.contains("/Users/\(Paths.whoami)/.config/phpmon/bin") &&
|
||||||
!FileManager.default.isWritableFile(atPath: "/usr/local/bin/")
|
!FileManager.default.isWritableFile(atPath: "/usr/local/bin/")
|
||||||
},
|
},
|
||||||
name: "`/usr/local/bin` not writable",
|
name: "Helpers cannot be symlinked and not in PATH",
|
||||||
title: "warnings.helper_permissions.title",
|
title: "warnings.helper_permissions.title",
|
||||||
paragraphs: ["warnings.helper_permissions.description", "warnings.helper_permissions.unavailable"],
|
paragraphs: [
|
||||||
|
"warnings.helper_permissions.description",
|
||||||
|
"warnings.helper_permissions.unavailable",
|
||||||
|
"warnings.helper_permissions.symlink"
|
||||||
|
],
|
||||||
url: "https://github.com/nicoverbruggen/phpmon/wiki/PHP-Monitor-helper-binaries"
|
url: "https://github.com/nicoverbruggen/phpmon/wiki/PHP-Monitor-helper-binaries"
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
@ -59,7 +64,7 @@ class WarningManager {
|
|||||||
|
|
||||||
for check in self.evaluations {
|
for check in self.evaluations {
|
||||||
if await check.applies() {
|
if await check.applies() {
|
||||||
Log.info("[WARNING] \(check.name)")
|
Log.info("[DOCTOR] \(check.name) (!)")
|
||||||
self.warnings.append(check)
|
self.warnings.append(check)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -74,6 +74,8 @@
|
|||||||
"mi_no_presets" = "No presets available.";
|
"mi_no_presets" = "No presets available.";
|
||||||
"mi_set_up_presets" = "Learn more about presets...";
|
"mi_set_up_presets" = "Learn more about presets...";
|
||||||
|
|
||||||
|
"mi_view_onboarding" = "Show Welcome Tour...";
|
||||||
|
|
||||||
"mi_xdebug_available_modes" = "Available Modes";
|
"mi_xdebug_available_modes" = "Available Modes";
|
||||||
"mi_xdebug_actions" = "Actions";
|
"mi_xdebug_actions" = "Actions";
|
||||||
"mi_xdebug_disable_all" = "Disable All Modes";
|
"mi_xdebug_disable_all" = "Disable All Modes";
|
||||||
@ -518,7 +520,8 @@ If you are seeing this message but are confused why this folder has gone missing
|
|||||||
|
|
||||||
"warnings.helper_permissions.title" = "PHP Monitor’s helpers are currently unavailable.";
|
"warnings.helper_permissions.title" = "PHP Monitor’s helpers are currently unavailable.";
|
||||||
"warnings.helper_permissions.description" = "PHP Monitor comes with various helper binaries. Using these binaries allows you to easily invoke a specific version of PHP without switching the linked PHP version.";
|
"warnings.helper_permissions.description" = "PHP Monitor comes with various helper binaries. Using these binaries allows you to easily invoke a specific version of PHP without switching the linked PHP version.";
|
||||||
"warnings.helper_permissions.unavailable" = "However, these helpers are currently *unavailable* because PHP Monitor could not create the required symlinks (alternatively, you could add PHP Monitor's helper directory to your `PATH` variable to make this warning go away as well).";
|
"warnings.helper_permissions.unavailable" = "However, these helpers are potentially *unavailable* because PHP Monitor cannot currently create or update the required symlinks.";
|
||||||
|
"warnings.helper_permissions.symlink" = "If you do not wish to make `/usr/local/bin` writable, you can add PHP Monitor's helper directory to your `PATH` variable to make this warning go away. (Click on ”Learn More” to find out how to fix this issue.)";
|
||||||
|
|
||||||
"warnings.arm_compatibility.title" = "You are running PHP Monitor using Rosetta on Apple Silicon, which means your PHP environment is also running via Rosetta.";
|
"warnings.arm_compatibility.title" = "You are running PHP Monitor using Rosetta on Apple Silicon, which means your PHP environment is also running via Rosetta.";
|
||||||
"warnings.arm_compatibility.description" = "You appear to be running an ARM-compatible version of macOS, but you are currently running PHP Monitor using Rosetta. While this will work correctly, it is recommended that you use the native version of Homebrew.";
|
"warnings.arm_compatibility.description" = "You appear to be running an ARM-compatible version of macOS, but you are currently running PHP Monitor using Rosetta. While this will work correctly, it is recommended that you use the native version of Homebrew.";
|
||||||
@ -527,13 +530,15 @@ If you are seeing this message but are confused why this folder has gone missing
|
|||||||
|
|
||||||
"onboarding.title" = "Welcome Tour";
|
"onboarding.title" = "Welcome Tour";
|
||||||
"onboarding.welcome" = "Welcome to PHP Monitor!";
|
"onboarding.welcome" = "Welcome to PHP Monitor!";
|
||||||
"onboarding.explore" = "Learn more about some of the features that PHP Monitor has to offer.";
|
"onboarding.explore" = "Learn more about some of the features that PHP Monitor has to offer. You can find a more comprehensive list of features on GitHub.";
|
||||||
"onboarding.tour.menu_bar.title" = "Get Started";
|
"onboarding.tour.menu_bar.title" = "Get Started";
|
||||||
"onboarding.tour.menu_bar" = "PHP Monitor lives in your menu bar. From here, you can switch the globally linked PHP version, start or stop services, locate config files, and more.";
|
"onboarding.tour.menu_bar" = "PHP Monitor lives in your menu bar. From here, you can switch the globally linked PHP version, start or stop services, locate config files, and more.";
|
||||||
"onboarding.tour.faq_hint" = "I recommend that you check out the [README](https://github.com/nicoverbruggen/phpmon/blob/main/README.md) on GitHub: it contains a comprehensive FAQ with various tips and common questions and answers.";
|
"onboarding.tour.faq_hint" = "I recommend that you check out the [README](https://github.com/nicoverbruggen/phpmon/blob/main/README.md) on GitHub: it contains a comprehensive FAQ with various tips and common questions and answers.";
|
||||||
"onboarding.tour.domains.title" = "Domains";
|
"onboarding.tour.services.title" = "Manage Services";
|
||||||
"onboarding.tour.domains" = "By opening the Domains window via the Menu Bar item, you can view which domains are linked and parked.";
|
"onboarding.tour.services" = "Once you click on the menu bar item, you can see at a glance based on the checkmarks or crosses if all of the Homebrew services are up and running. You can also click on a service to quickly toggle it. You can also add your own!";
|
||||||
"onboarding.tour.isolation.title" = "Isolation";
|
"onboarding.tour.domains.title" = "Manage Domains";
|
||||||
"onboarding.tour.isolation" = "If you have Valet 3 installed, you can even use domain isolation by right-clicking on a given domain in the Domains window. This allows you to pick a specific version of PHP to use for that domain!";
|
"onboarding.tour.domains" = "By opening the Domains window via the menu bar item, you can view which domains are linked and parked, as well as active nginx proxies.";
|
||||||
"onboarding.tour.once" = "You will only see the Welcome Tour once. You can re-open the Welcome Tour later via the menu bar icon.";
|
"onboarding.tour.isolation.title" = "Isolate Domains";
|
||||||
|
"onboarding.tour.isolation" = "If you have Valet 3 installed, you can even use domain isolation by right-clicking on a given domain in the Domains window. This allows you to pick a specific version of PHP to use for that domain, and that domain only!";
|
||||||
|
"onboarding.tour.once" = "You will only see the Welcome Tour once. You can re-open the Welcome Tour later via the menu bar icon (under First Aid & Services).";
|
||||||
"onboarding.tour.close" = "Close Tour";
|
"onboarding.tour.close" = "Close Tour";
|
||||||
|
Reference in New Issue
Block a user