Show profile paths, report the build version, and colour each profile
- Selecting a row in the Profiles tab shows its Claude Desktop and Claude Code dirs inline and selectable, instead of requiring a right-click "Reveal in Finder" to find out where a profile actually lives. - build.sh stamps Contents/Resources/COMMIT with the short HEAD SHA, left empty when HEAD sits exactly on a tag (how a real release is built), since the version number alone is unambiguous there. About appends it when present, so two dev builds off the same VERSION are distinguishable. The update checker still compares plain semver. - Profile dot colours are picked per NSApp.effectiveAppearance at render time: same hue either way, but full saturation with brightness split per background (1.0 dark, 0.55 light), so they read as bold hues rather than washing out on white or muddying against a dark menu. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -58,6 +58,9 @@ private final class ProfilesViewController: NSViewController, NSTableViewDataSou
|
||||
private var infos: [ProfileInfo] = []
|
||||
private let tableView = NSTableView()
|
||||
private let removeButton = NSButton(title: "\u{2212}", target: nil, action: nil)
|
||||
private let detailStack = NSStackView()
|
||||
private let detailAppValue = NSTextField(labelWithString: "")
|
||||
private let detailCodeValue = NSTextField(labelWithString: "")
|
||||
private let addForm = NSStackView()
|
||||
private let nameField = NSTextField()
|
||||
private let codeField = NSTextField()
|
||||
@@ -91,11 +94,15 @@ private final class ProfilesViewController: NSViewController, NSTableViewDataSou
|
||||
buttonRow.spacing = 4
|
||||
buttonRow.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
buildDetailStack()
|
||||
detailStack.isHidden = true
|
||||
detailStack.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
buildAddForm()
|
||||
addForm.isHidden = true
|
||||
addForm.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
let stack = NSStackView(views: [scroll, buttonRow, addForm])
|
||||
let stack = NSStackView(views: [scroll, detailStack, buttonRow, addForm])
|
||||
stack.orientation = .vertical
|
||||
stack.spacing = 8
|
||||
stack.edgeInsets = NSEdgeInsets(top: 12, left: 12, bottom: 12, right: 12)
|
||||
@@ -108,9 +115,36 @@ private final class ProfilesViewController: NSViewController, NSTableViewDataSou
|
||||
stack.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
stack.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||
scroll.heightAnchor.constraint(greaterThanOrEqualToConstant: 160),
|
||||
detailStack.widthAnchor.constraint(equalTo: stack.widthAnchor),
|
||||
])
|
||||
}
|
||||
|
||||
// Shows the selected profile's two dirs — the thing you'd otherwise
|
||||
// have to right-click "Reveal" to find out.
|
||||
private func buildDetailStack() {
|
||||
for value in [detailAppValue, detailCodeValue] {
|
||||
value.isSelectable = true
|
||||
value.textColor = .secondaryLabelColor
|
||||
value.font = .systemFont(ofSize: NSFont.smallSystemFontSize)
|
||||
value.lineBreakMode = .byTruncatingMiddle
|
||||
value.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
||||
}
|
||||
|
||||
let appRow = NSStackView(views: [labeled("Claude Desktop:"), detailAppValue])
|
||||
appRow.orientation = .horizontal
|
||||
appRow.spacing = 6
|
||||
let codeRow = NSStackView(views: [labeled("Claude Code:"), detailCodeValue])
|
||||
codeRow.orientation = .horizontal
|
||||
codeRow.spacing = 6
|
||||
|
||||
detailStack.orientation = .vertical
|
||||
detailStack.alignment = .leading
|
||||
detailStack.spacing = 2
|
||||
[appRow, codeRow].forEach(detailStack.addArrangedSubview)
|
||||
appRow.widthAnchor.constraint(equalTo: detailStack.widthAnchor).isActive = true
|
||||
codeRow.widthAnchor.constraint(equalTo: detailStack.widthAnchor).isActive = true
|
||||
}
|
||||
|
||||
private func buildAddForm() {
|
||||
nameField.placeholderString = "Profile name"
|
||||
nameField.delegate = self
|
||||
@@ -156,9 +190,21 @@ private final class ProfilesViewController: NSViewController, NSTableViewDataSou
|
||||
func update(_ infos: [ProfileInfo]) {
|
||||
self.infos = infos
|
||||
tableView.reloadData()
|
||||
removeButton.isEnabled = tableView.selectedRow >= 0
|
||||
&& infos.indices.contains(tableView.selectedRow)
|
||||
&& infos[tableView.selectedRow].profile.name != "default"
|
||||
refreshForSelection()
|
||||
}
|
||||
|
||||
private func refreshForSelection() {
|
||||
let row = tableView.selectedRow
|
||||
guard infos.indices.contains(row) else {
|
||||
removeButton.isEnabled = false
|
||||
detailStack.isHidden = true
|
||||
return
|
||||
}
|
||||
let profile = infos[row].profile
|
||||
removeButton.isEnabled = profile.name != "default"
|
||||
detailAppValue.stringValue = profile.appDir
|
||||
detailCodeValue.stringValue = profile.codeDir
|
||||
detailStack.isHidden = false
|
||||
}
|
||||
|
||||
// MARK: NSTableViewDataSource / Delegate
|
||||
@@ -198,9 +244,7 @@ private final class ProfilesViewController: NSViewController, NSTableViewDataSou
|
||||
}
|
||||
|
||||
func tableViewSelectionDidChange(_ notification: Notification) {
|
||||
removeButton.isEnabled = tableView.selectedRow >= 0
|
||||
&& infos.indices.contains(tableView.selectedRow)
|
||||
&& infos[tableView.selectedRow].profile.name != "default"
|
||||
refreshForSelection()
|
||||
}
|
||||
|
||||
// MARK: NSTextFieldDelegate — live default-path preview as the name is typed
|
||||
|
||||
Reference in New Issue
Block a user