mirror of
https://github.com/Homebrew/BrewUI.git
synced 2026-10-02 08:34:36 +08:00
Merge pull request #211 from Homebrew/pkg_hierarchy_fix
Drop the leading icon from package list rows
This commit is contained in:
@@ -660,6 +660,14 @@
|
||||
- **The handoff refuses while a *mutating* brew command is in flight.** `NSApplication.terminate` would kill the subprocess the command center is streaming and the helper would start a second brew against the same Homebrew; `HelperSelfUpgradeHandoff` asks `commandCenter.runningPhases()` first and throws, which surfaces on the banner. `BrewOperationKind.isMutating` is the filter — `doctorRead` is the one scheduled kind that changes nothing, and it runs long enough that counting it would block every upgrade attempted while the Doctor tab is loading.
|
||||
- **The runner takes an injected `sleep`, like `SelfUpgradeHelperRun`.** Wall-clock timeouts of 0.5–3s pass alone and fail under the parallel suite, because the fake `brew` has not spawned before the deadline — which made the tests that matter most (a descendant surviving the timeout) pass vacuously. Tests drive the timeout off a readiness file the fake `brew` touches.
|
||||
|
||||
## 2026-09-10 — Package list rows drop the leading icon
|
||||
|
||||
- **The icon carried no information, so it was noise on a scanning surface.** Every installed row drew the same `cube.box.fill` and every Discover row the same `shippingbox.fill`, inside a 36pt stroked circle tinted by kind. The glyph never varied, and the tint duplicated the FORMULA/CASK pill sitting a few points to its right — so the only thing the leading column did was push the name off the list's left edge and give the row a second alignment axis to parse. Removing it makes the name, description and version line share one axis.
|
||||
- **The name is now `Font.brewBodyEmphasized`** (a new token: `.body.weight(.semibold)`, same 13pt size). With the icon gone the row needs its rank stated typographically — semibold name, `brewCallout` secondary description, `brewCaption` tertiary metadata is a three-step hierarchy that reads without any chrome.
|
||||
- **Row titles are `.lineLimit(1)`.** Without it SwiftUI truncated one long name and wrapped the next depending on the pills beside it, so the kind pill floated against the second line on some rows and not others. Truncating keeps the name column straight at the 300pt list minimum; the full name stays on the row's accessibility label and in the detail pane.
|
||||
- **The kind pill's chrome now carries `brewHiddenWhenRedacted()`**, which the icon's circle used to be the only caller of. `.redacted(reason: .placeholder)` greys text and symbols but leaves `Shape` alone, so the pill's fill and 1px stroke stayed crisp around a redacted label on every skeleton row.
|
||||
- **Considered and not done:** moving the kind pill down to the metadata line, and dropping the green up-to-date checkmark (a constant on every current package, so it carries nothing while scanning). Both were offered and declined — the checkmark change alters what the row communicates, not just how it looks.
|
||||
|
||||
## 2026-09-13 — Sidebar icons use SF Symbols
|
||||
|
||||
- Sidebar rows render `Image(systemName:)` glyphs, not emoji (issue #165). macOS convention; symbols tint with selection state and align via `BrewLayout.sidebarIconWidth`.
|
||||
|
||||
@@ -45,19 +45,17 @@ struct DiscoverListRowView: View {
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
HStack(alignment: .center, spacing: BrewSpacing.md) {
|
||||
iconBadge
|
||||
VStack(alignment: .leading, spacing: BrewSpacing.xs) {
|
||||
titleRow
|
||||
if viewModel.hasDescription {
|
||||
Text(viewModel.descriptionText)
|
||||
.font(.brewCallout)
|
||||
.foregroundStyle(Color.brewTextSecondary)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
metadataRow
|
||||
VStack(alignment: .leading, spacing: BrewSpacing.xs) {
|
||||
titleRow
|
||||
if viewModel.hasDescription {
|
||||
Text(viewModel.descriptionText)
|
||||
.font(.brewCallout)
|
||||
.foregroundStyle(Color.brewTextSecondary)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
metadataRow
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(.vertical, BrewSpacing.sm)
|
||||
.task(id: discoveryPackage.id) {
|
||||
await viewModel.observeRowUpdates()
|
||||
@@ -69,24 +67,12 @@ struct DiscoverListRowView: View {
|
||||
.accessibilityLabel(viewModel.rowAccessibilityLabel)
|
||||
}
|
||||
|
||||
private var iconBadge: some View {
|
||||
ZStack {
|
||||
Circle()
|
||||
.strokeBorder(accentColor(viewModel.packageKindChrome.accent), lineWidth: 1)
|
||||
.frame(width: 36, height: 36)
|
||||
.brewHiddenWhenRedacted()
|
||||
Image(systemName: "shippingbox.fill")
|
||||
.font(.body)
|
||||
.foregroundStyle(accentColor(viewModel.packageKindChrome.accent))
|
||||
}
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
|
||||
private var titleRow: some View {
|
||||
HStack(spacing: BrewSpacing.sm) {
|
||||
Text(viewModel.name)
|
||||
.font(.brewBody)
|
||||
.font(.brewBodyEmphasized)
|
||||
.foregroundStyle(Color.brewTextPrimary)
|
||||
.lineLimit(1)
|
||||
|
||||
if viewModel.showsInstallBusy {
|
||||
ProgressView()
|
||||
@@ -113,10 +99,11 @@ struct DiscoverListRowView: View {
|
||||
.background {
|
||||
Capsule()
|
||||
.fill(Color.brewSurfaceElevated)
|
||||
}
|
||||
.overlay {
|
||||
Capsule()
|
||||
.strokeBorder(Color.brewBorderDefault, lineWidth: 1)
|
||||
.overlay {
|
||||
Capsule()
|
||||
.strokeBorder(Color.brewBorderDefault, lineWidth: 1)
|
||||
}
|
||||
.brewHiddenWhenRedacted()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,42 +50,28 @@ struct InstalledListRowView: View {
|
||||
}
|
||||
|
||||
private func rowContent(viewModel: InstalledListRowViewModel) -> some View {
|
||||
HStack(alignment: .center, spacing: BrewSpacing.md) {
|
||||
iconBadge(viewModel: viewModel)
|
||||
VStack(alignment: .leading, spacing: BrewSpacing.xs) {
|
||||
titleRow(viewModel: viewModel)
|
||||
if viewModel.hasDescription {
|
||||
Text(viewModel.descriptionText)
|
||||
.font(.brewCallout)
|
||||
.foregroundStyle(Color.brewTextSecondary)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
versionLine(viewModel: viewModel)
|
||||
VStack(alignment: .leading, spacing: BrewSpacing.xs) {
|
||||
titleRow(viewModel: viewModel)
|
||||
if viewModel.hasDescription {
|
||||
Text(viewModel.descriptionText)
|
||||
.font(.brewCallout)
|
||||
.foregroundStyle(Color.brewTextSecondary)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
versionLine(viewModel: viewModel)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(.vertical, BrewSpacing.sm)
|
||||
.accessibilityElement(children: .combine)
|
||||
.accessibilityLabel(viewModel.rowAccessibilityLabel)
|
||||
}
|
||||
|
||||
private func iconBadge(viewModel: InstalledListRowViewModel) -> some View {
|
||||
ZStack {
|
||||
Circle()
|
||||
.strokeBorder(accentColor(viewModel.kind.chrome.accent), lineWidth: 1)
|
||||
.frame(width: 36, height: 36)
|
||||
.brewHiddenWhenRedacted()
|
||||
Image(systemName: "cube.box.fill")
|
||||
.font(.body)
|
||||
.foregroundStyle(accentColor(viewModel.kind.chrome.accent))
|
||||
}
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
|
||||
private func titleRow(viewModel: InstalledListRowViewModel) -> some View {
|
||||
HStack(spacing: BrewSpacing.sm) {
|
||||
Text(viewModel.name)
|
||||
.font(.brewBody)
|
||||
.font(.brewBodyEmphasized)
|
||||
.foregroundStyle(Color.brewTextPrimary)
|
||||
.lineLimit(1)
|
||||
|
||||
if viewModel.showsOperationBusy {
|
||||
ProgressView()
|
||||
@@ -101,10 +87,11 @@ struct InstalledListRowView: View {
|
||||
.background {
|
||||
Capsule()
|
||||
.fill(Color.brewSurfaceElevated)
|
||||
}
|
||||
.overlay {
|
||||
Capsule()
|
||||
.strokeBorder(Color.brewBorderDefault, lineWidth: 1)
|
||||
.overlay {
|
||||
Capsule()
|
||||
.strokeBorder(Color.brewBorderDefault, lineWidth: 1)
|
||||
}
|
||||
.brewHiddenWhenRedacted()
|
||||
}
|
||||
|
||||
statusBadge(viewModel: viewModel)
|
||||
|
||||
@@ -25,6 +25,9 @@ public extension Font {
|
||||
/// Standard body text — 13pt SF Pro Text.
|
||||
static let brewBody: Font = .body
|
||||
|
||||
/// List row titles — 13pt SF Pro Text Semibold.
|
||||
static let brewBodyEmphasized: Font = .body.weight(.semibold)
|
||||
|
||||
/// Secondary info rows — 12pt SF Pro Text.
|
||||
static let brewCallout: Font = .callout
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ The brew.sh site uses a clean sans-serif for prose and a monospaced font for all
|
||||
| **Display** | SF Pro Display | `.title` / `.largeTitle` | Used for app name, empty states |
|
||||
| **Heading** | SF Pro Display Semibold | `.title2`, `.title3` | Section headers, panel titles |
|
||||
| **Body** | SF Pro Text Regular | `.body` | Standard readable text |
|
||||
| **Body Emphasized** | SF Pro Text Semibold | `.body.weight(.semibold)` | List row titles — ranks the row's name above its secondary and metadata lines |
|
||||
| **Label** | SF Pro Text Medium | `.callout`, `.subheadline` | List row labels, form labels |
|
||||
| **Caption** | SF Pro Text Regular | `.caption`, `.caption2` | Metadata, timestamps, version strings |
|
||||
| **Code / Command** | SF Mono Regular | `.body` with `.monospaced()` | Command output, brew commands |
|
||||
@@ -79,6 +80,7 @@ The brew.sh site uses a clean sans-serif for prose and a monospaced font for all
|
||||
| `fontSize.title2` | 17 | 22 | Panel header |
|
||||
| `fontSize.title3` | 15 | 20 | Sub-section header |
|
||||
| `fontSize.body` | 13 | 18 | Standard body text (macOS default) |
|
||||
| `fontSize.bodyEmphasized` | 13 | 18 | List row titles — same size as body, semibold weight |
|
||||
| `fontSize.callout` | 12 | 16 | Secondary info rows |
|
||||
| `fontSize.caption` | 11 | 14 | Metadata, badges |
|
||||
| `fontSize.code` | 12 | 18 | Terminal / command output (SF Mono) |
|
||||
|
||||
Reference in New Issue
Block a user