WIP: preserve source badge beside action rail

This commit is contained in:
Akshay Kolli
2026-06-30 10:02:22 -07:00
parent 1e37169b3b
commit a1ed960d1c
3 changed files with 42 additions and 15 deletions

View File

@@ -54,7 +54,7 @@ Use this checklist before a release or after changes to panel, pasteboard, setti
21. Right-click a card, use Capture Rules to ignore its source app, copy from that app again, and confirm the new item is skipped.
22. Drag an unassigned card onto the renamed collection chip and confirm the chip count increases and the card appears when that collection is selected.
23. Resize or test on a narrow display and confirm the bottom shelf switches to compact cards that still show two recent clips cleanly.
24. Select a file, rich text, or URL card and confirm the selected-card rail exposes `Paste Plain Text`; on a narrow shelf, confirm secondary actions collapse behind `More` instead of overflowing the card.
24. Select a file, rich text, or URL card and confirm the selected-card rail exposes `Paste Plain Text`, the corner source/kind badge remains visible, and on a narrow shelf secondary actions collapse behind `More` instead of overflowing the card.
## Copy And Paste

View File

@@ -2320,7 +2320,8 @@ private final class AspectFillImageView: NSView {
private final class ClipboardItemCardView: NSView, NSDraggingSource {
private enum Metrics {
static let dragThreshold: CGFloat = 4
static let actionRailHorizontalMargin: CGFloat = 12
static let actionRailBadgeGap: CGFloat = 8
static let actionRailLeadingMargin: CGFloat = 10
}
private enum Palette {
static let border = NSColor.separatorColor.withAlphaComponent(0.20).cgColor
@@ -2942,7 +2943,7 @@ private final class ClipboardItemCardView: NSView, NSDraggingSource {
}
private func fittedActionRailButtonSpecs(from specs: [ActionRailButtonSpec]) -> [ActionRailButtonSpec] {
let maximumWidth = layout.width - (Metrics.actionRailHorizontalMargin * 2)
let maximumWidth = maximumVisibleActionRailWidth
guard actionRailWidth(for: specs) > maximumWidth else { return specs }
let moreSpec = ActionRailButtonSpec("ellipsis.circle", toolTip: "More", action: #selector(showMoreActionsFromActionRail(_:)))
@@ -2980,6 +2981,22 @@ private final class ClipboardItemCardView: NSView, NSDraggingSource {
+ actionRail.edgeInsets.right
}
private var maximumVisibleActionRailWidth: CGFloat {
layout.width
- Metrics.actionRailLeadingMargin
- layout.inset
- Metrics.actionRailBadgeGap
- headerBadgeSize
}
private var headerBadgeSize: CGFloat {
layout.isCompact ? 36 : 42
}
private var actionRailHeaderTopInset: CGFloat {
max(8, (layout.headerHeight - layout.actionRailHeight) / 2)
}
private func cardActionButton(
_ systemName: String,
toolTip: String,
@@ -3018,7 +3035,7 @@ private final class ClipboardItemCardView: NSView, NSDraggingSource {
private func updateActionRailVisibility() {
actionRail.isHidden = !isSelected
headerBadgeView?.isHidden = isSelected
headerBadgeView?.isHidden = false
headerPinView?.isHidden = isSelected
footerDetailLabel.isHidden = false
for button in actionRailButtons {
@@ -3199,9 +3216,18 @@ private final class ClipboardItemCardView: NSView, NSDraggingSource {
footer.widthAnchor.constraint(equalTo: stack.widthAnchor)
])
contentView.addSubview(actionRail)
let actionRailTrailingConstraint: NSLayoutConstraint
if let headerBadgeView {
actionRailTrailingConstraint = actionRail.trailingAnchor.constraint(
equalTo: headerBadgeView.leadingAnchor,
constant: -Metrics.actionRailBadgeGap
)
} else {
actionRailTrailingConstraint = actionRail.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -12)
}
NSLayoutConstraint.activate([
actionRail.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -12),
actionRail.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 11)
actionRailTrailingConstraint,
actionRail.topAnchor.constraint(equalTo: contentView.topAnchor, constant: actionRailHeaderTopInset)
])
setSelected(false)
}
@@ -3264,8 +3290,8 @@ private final class ClipboardItemCardView: NSView, NSDraggingSource {
labelStack.trailingAnchor.constraint(lessThanOrEqualTo: badge.leadingAnchor, constant: -12),
badge.trailingAnchor.constraint(equalTo: header.trailingAnchor, constant: -layout.inset),
badge.centerYAnchor.constraint(equalTo: header.centerYAnchor),
badge.widthAnchor.constraint(equalToConstant: layout.isCompact ? 36 : 42),
badge.heightAnchor.constraint(equalToConstant: layout.isCompact ? 36 : 42),
badge.widthAnchor.constraint(equalToConstant: headerBadgeSize),
badge.heightAnchor.constraint(equalToConstant: headerBadgeSize),
separator.leadingAnchor.constraint(equalTo: header.leadingAnchor, constant: layout.inset),
separator.trailingAnchor.constraint(equalTo: header.trailingAnchor, constant: -layout.inset),
separator.bottomAnchor.constraint(equalTo: header.bottomAnchor),

View File

@@ -366,17 +366,17 @@ final class ClipboardPanelViewTests: XCTestCase {
XCTAssertEqual(fixture.view.debugFirstCardVisibleActionLabels, ["Paste", "Copy", "Pin", "Collect", "Add to Stack", "Edit", "Preview", "Delete"])
XCTAssertEqual(fixture.view.debugFirstCardVisibleActionRailWidth, 238)
XCTAssertFalse(fixture.view.debugFirstCardFooterDetailIsHidden)
XCTAssertTrue(fixture.view.debugFirstCardHeaderBadgeIsHidden)
XCTAssertFalse(fixture.view.debugFirstCardHeaderBadgeIsHidden)
fixture.store.upsert(makeItem(kind: .file, text: "/tmp/report.txt", store: fixture.store))
drainMainQueue()
fixture.viewModel.selectFirstItem()
XCTAssertEqual(fixture.viewModel.visibleItems.first?.kind, .file)
XCTAssertEqual(fixture.view.debugFirstCardVisibleActionLabels, ["Paste", "Copy", "Paste Plain Text", "Pin", "Collect", "Add to Stack", "Preview", "Open", "Reveal", "More"])
XCTAssertEqual(fixture.view.debugFirstCardVisibleActionRailWidth, 294)
XCTAssertEqual(fixture.view.debugFirstCardVisibleActionLabels, ["Paste", "Copy", "Paste Plain Text", "Collect", "Add to Stack", "Preview", "Open", "More"])
XCTAssertEqual(fixture.view.debugFirstCardVisibleActionRailWidth, 238)
XCTAssertFalse(fixture.view.debugFirstCardFooterDetailIsHidden)
XCTAssertTrue(fixture.view.debugFirstCardHeaderBadgeIsHidden)
XCTAssertFalse(fixture.view.debugFirstCardHeaderBadgeIsHidden)
}
func testCompactFileCardActionsFitInsideShelfWithOverflowMenu() {
@@ -387,9 +387,10 @@ final class ClipboardPanelViewTests: XCTestCase {
fixture.window.contentView?.layoutSubtreeIfNeeded()
XCTAssertEqual(fixture.view.debugCardDensity, "compact")
XCTAssertEqual(fixture.view.debugFirstCardVisibleActionLabels, ["Paste", "Copy", "Paste Plain Text", "Collect", "Add to Stack", "Preview", "Open", "More"])
XCTAssertEqual(fixture.view.debugFirstCardVisibleActionRailWidth, 222)
XCTAssertLessThanOrEqual(fixture.view.debugFirstCardVisibleActionRailWidth, 240)
XCTAssertEqual(fixture.view.debugFirstCardVisibleActionLabels, ["Paste", "Copy", "Paste Plain Text", "Collect", "Add to Stack", "Preview", "More"])
XCTAssertEqual(fixture.view.debugFirstCardVisibleActionRailWidth, 196)
XCTAssertLessThanOrEqual(fixture.view.debugFirstCardVisibleActionRailWidth, 197)
XCTAssertFalse(fixture.view.debugFirstCardHeaderBadgeIsHidden)
XCTAssertEqual(
fixture.view.debugFirstCardMenuTitles,
["Paste", "Copy", "Paste Plain Text", "Copy Plain Text", "Rename...", "Add to Stack", "Quick Look", "Pin", "Add to Collection", "Capture Rules", "-", "Open", "Reveal in Finder", "-", "Delete"]