Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import AppKit
import CmuxNextDesign
import CmuxNextFloating
import CmuxNextSettings
import UniformTypeIdentifiers

/// The Chief pet's nine characters side by side in the Chief Settings popover (Leo 2026-10-21:
/// The Chief pet's characters side by side in the Chief Settings popover (Leo 2026-10-21:
/// "the nine avatars, we need to be able to just swap them out, just how ChatGPT does"): each
/// character's picture, its name as the tooltip and VoiceOver label, the current one checked.
/// One click picks a character; `onPick` keeps it as the popover's Chief's. After the nine comes the
/// One click picks a character; `onPick` keeps it as the popover's Chief's. After them comes the
/// person's own upload (``uploadCell``, a "+" until there is one; Leo 2026-10-26: "upload their own"),
/// and a picture or GIF dropped anywhere on the grid is uploaded (`onDrop`).
@MainActor
Expand All @@ -19,7 +20,7 @@ final class ChiefPetMascotGrid: NSStackView {
/// A picture file dropped on the grid.
var onDrop: ((URL) -> Void)?
private(set) var cells: [ChiefPetMascotCell] = []
/// The tenth cell: the upload's picture, or "+" (shown only in the tile style while there is none).
/// The last cell: the upload's picture, or "+" (shown only in the tile style while there is none).
let uploadCell = ChiefPetMascotCell(id: ChiefPetMascotGrid.uploadID, title: ChiefAvatarUploads.chooseTitle)

init(items: [(id: String, title: String)]) {
Expand Down Expand Up @@ -53,9 +54,10 @@ final class ChiefPetMascotGrid: NSStackView {
@available(*, unavailable)
required init?(coder: NSCoder) { nil }

/// Checks the character `id` (an unknown id: the first, as the Settings page shows it).
/// Checks the character `id` (an unknown id: the default, orb, by id and not by its place in the
/// grid, as the Settings page shows it), so reordering the characters never moves the check.
func select(_ id: String, enabled: Bool) {
let shown = cells.contains { $0.id == id } ? id : cells.first?.id
let shown = cells.contains { $0.id == id } ? id : HomeChiefPetSetting.defaultMascot
for cell in cells {
cell.isSelected = cell.id == shown
cell.isEnabled = enabled
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ public final class ChiefPetSettingsRows: NSObject {
uploads?.worn(of: ChiefAvatarUploads.key(chief: chief ?? picks.defaultChief), over: picks.mascot(of: chief, in: snapshot))
}

/// The avatar picture of the character `mascot` names (an unknown or nil id: the first, orb):
/// The avatar picture of the character `mascot` names (an unknown or nil id: the default, orb):
/// the whole character on a transparent square, for a Chief with no avatar of its own.
public static func avatarURL(of mascot: String?) -> URL? {
(ChiefPetMascot(rawValue: mascot ?? "") ?? .initial).avatarURL
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
public import AppKit

/// The Chief pet's character: the Chief Dock's nine pixel mascots (#21194, its `ChiefMascot`
/// ids), bundled at 256 px wide, and Mochi, the first animated avatar set. Orb is the dock's default too.
/// ids), bundled at 256 px wide, Mochi, the first animated avatar set, and Series 2, new colourways
/// of Fox, Machine and Moon (`scripts/cmux-next/art/chief_mascot_series2.py`). The cases are in the
/// picker's order: Series 1 without the orbs, Mochi, Series 2, then the orbs (Leo 2026-11-01: "we can
/// reorder them a little bit so they orb doesn't come first"). A choice is kept by its id, never its
/// place, so the order changes nobody's pick, and Orb stays the default (`initial`).
public nonisolated enum ChiefPetMascot: String, CaseIterable, Sendable {
case orb
case orbSpark = "orb-spark"
case orbHood = "orb-hood"
case fox
case machine
case machineCyan = "machine-cyan"
case machineBlue = "machine-blue"
case moon
case moonBlue = "moon-blue"
case fox
/// The first animated set (`cmux-avatar/1`, webviews avatars/sets/mochi); still here, its idle frame.
case mochi
// Series 2.
case foxArctic = "fox-arctic"
case machineViolet = "machine-violet"
case machineGold = "machine-gold"
case moonRose = "moon-rose"
case orb
case orbSpark = "orb-spark"
case orbHood = "orb-hood"

public static let initial = ChiefPetMascot.orb

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ public import CmuxNextActions
/// default; the person or an agent turns it on (Settings > Home > Chief Pet, View > Show Chief Pet, the
/// palette's Show Chief Pet, the empty Chief conversation, `cmux settings set`), and the person can
/// hide it again the same way. Message the Chief shows it for one message without turning it on.
/// `home.chiefPetMascot` picks its character among the Chief Dock's nine pixel mascots and the animated sets.
/// `home.chiefPetMascot` picks its character among the Chief Dock's pixel mascots, Series 2 and the animated sets.
public nonisolated struct HomeChiefPetSetting {
public nonisolated init() {}
public static let configPath = ["home", "chiefPetEnabled"]
public static let mascotPath = ["home", "chiefPetMascot"]
/// The Chief Dock's mascot ids (`ChiefPetMascot`, webviews `ChiefMascot`), its default first.
public static let mascots = ["orb", "orb-spark", "orb-hood", "machine", "machine-cyan", "machine-blue", "moon", "moon-blue", "fox", "mochi"]
/// The characters' ids in the picker's order (`ChiefPetMascot`, webviews `ChiefMascot`): Series 1
/// without the orbs, Mochi, Series 2, then the orbs. A pick is kept by id, so the order moves no
/// one's character, and the default stays orb wherever it sits in the list.
public static let mascots = ["fox", "machine", "machine-cyan", "machine-blue", "moon", "moon-blue", "mochi",
"fox-arctic", "machine-violet", "machine-gold", "moon-rose", "orb", "orb-spark", "orb-hood"]
public static let defaultMascot = "orb"

/// The mascot id; one that is not a mascot is the default plus a diagnostic.
Expand Down
Loading
Loading